sem_new.c revision 201561
1201546Sdavidxu/*
2201546Sdavidxu * Copyright (C) 2010 David Xu <davidxu@freebsd.org>.
3201546Sdavidxu * All rights reserved.
4201546Sdavidxu *
5201546Sdavidxu * Redistribution and use in source and binary forms, with or without
6201546Sdavidxu * modification, are permitted provided that the following conditions
7201546Sdavidxu * are met:
8201546Sdavidxu * 1. Redistributions of source code must retain the above copyright
9201546Sdavidxu *    notice(s), this list of conditions and the following disclaimer as
10201546Sdavidxu *    the first lines of this file unmodified other than the possible
11201546Sdavidxu *    addition of one or more copyright notices.
12201546Sdavidxu * 2. Redistributions in binary form must reproduce the above copyright
13201546Sdavidxu *    notice(s), this list of conditions and the following disclaimer in
14201546Sdavidxu *    the documentation and/or other materials provided with the
15201546Sdavidxu *    distribution.
16201546Sdavidxu *
17201546Sdavidxu * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY
18201546Sdavidxu * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19201546Sdavidxu * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20201546Sdavidxu * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE
21201546Sdavidxu * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22201546Sdavidxu * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23201546Sdavidxu * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
24201546Sdavidxu * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25201546Sdavidxu * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
26201546Sdavidxu * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27201546Sdavidxu * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28201546Sdavidxu *
29201546Sdavidxu * $FreeBSD: head/lib/libc/gen/sem_new.c 201561 2010-01-05 06:40:27Z davidxu $
30201546Sdavidxu */
31201546Sdavidxu
32201546Sdavidxu#include "namespace.h"
33201546Sdavidxu#include <sys/types.h>
34201546Sdavidxu#include <sys/queue.h>
35201546Sdavidxu#include <sys/mman.h>
36201546Sdavidxu#include <sys/stat.h>
37201546Sdavidxu#include <errno.h>
38201546Sdavidxu#include <machine/atomic.h>
39201546Sdavidxu#include <sys/umtx.h>
40201546Sdavidxu#include <limits.h>
41201546Sdavidxu#include <fcntl.h>
42201546Sdavidxu#include <pthread.h>
43201546Sdavidxu#include <stdarg.h>
44201546Sdavidxu#include <stdlib.h>
45201546Sdavidxu#include <string.h>
46201546Sdavidxu#include <time.h>
47201546Sdavidxu#include <semaphore.h>
48201546Sdavidxu#include <unistd.h>
49201546Sdavidxu#include "un-namespace.h"
50201546Sdavidxu
51201561Sdavidxu__weak_reference(_sem_close, sem_close);
52201561Sdavidxu__weak_reference(_sem_destroy, sem_destroy);
53201561Sdavidxu__weak_reference(_sem_getvalue, sem_getvalue);
54201561Sdavidxu__weak_reference(_sem_init, sem_init);
55201561Sdavidxu__weak_reference(_sem_open, sem_open);
56201561Sdavidxu__weak_reference(_sem_post, sem_post);
57201561Sdavidxu__weak_reference(_sem_timedwait, sem_timedwait);
58201561Sdavidxu__weak_reference(_sem_trywait, sem_trywait);
59201561Sdavidxu__weak_reference(_sem_unlink, sem_unlink);
60201561Sdavidxu__weak_reference(_sem_wait, sem_wait);
61201546Sdavidxu
62201546Sdavidxu#define SEM_PREFIX	"/tmp/SEMD"
63201546Sdavidxu#define SEM_MAGIC	((u_int32_t)0x73656d31)
64201546Sdavidxu
65201546Sdavidxustruct sem_nameinfo {
66201546Sdavidxu	int open_count;
67201546Sdavidxu	char *name;
68201546Sdavidxu	sem_t *sem;
69201546Sdavidxu	LIST_ENTRY(sem_nameinfo) next;
70201546Sdavidxu};
71201546Sdavidxu
72201546Sdavidxustatic pthread_once_t once = PTHREAD_ONCE_INIT;
73201546Sdavidxustatic pthread_mutex_t sem_llock;
74201546Sdavidxustatic LIST_HEAD(,sem_nameinfo) sem_list = LIST_HEAD_INITIALIZER(sem_list);
75201546Sdavidxu
76201546Sdavidxustatic void
77201546Sdavidxusem_prefork()
78201546Sdavidxu{
79201546Sdavidxu
80201546Sdavidxu	_pthread_mutex_lock(&sem_llock);
81201546Sdavidxu}
82201546Sdavidxu
83201546Sdavidxustatic void
84201546Sdavidxusem_postfork()
85201546Sdavidxu{
86201546Sdavidxu	_pthread_mutex_unlock(&sem_llock);
87201546Sdavidxu}
88201546Sdavidxu
89201546Sdavidxustatic void
90201546Sdavidxusem_child_postfork()
91201546Sdavidxu{
92201546Sdavidxu	_pthread_mutex_unlock(&sem_llock);
93201546Sdavidxu}
94201546Sdavidxu
95201546Sdavidxustatic void
96201546Sdavidxusem_module_init(void)
97201546Sdavidxu{
98201546Sdavidxu	pthread_mutexattr_t ma;
99201546Sdavidxu
100201546Sdavidxu	_pthread_mutexattr_init(&ma);
101201546Sdavidxu	_pthread_mutexattr_settype(&ma,  PTHREAD_MUTEX_RECURSIVE);
102201546Sdavidxu	_pthread_mutex_init(&sem_llock, &ma);
103201546Sdavidxu	_pthread_mutexattr_destroy(&ma);
104201546Sdavidxu	_pthread_atfork(sem_prefork, sem_postfork, sem_child_postfork);
105201546Sdavidxu}
106201546Sdavidxu
107201546Sdavidxustatic inline int
108201546Sdavidxusem_check_validity(sem_t *sem)
109201546Sdavidxu{
110201546Sdavidxu
111201546Sdavidxu	if (sem->_magic == SEM_MAGIC)
112201546Sdavidxu		return (0);
113201546Sdavidxu	else {
114201546Sdavidxu		errno = EINVAL;
115201546Sdavidxu		return (-1);
116201546Sdavidxu	}
117201546Sdavidxu}
118201546Sdavidxu
119201546Sdavidxuint
120201561Sdavidxu_sem_init(sem_t *sem, int pshared, unsigned int value)
121201546Sdavidxu{
122201546Sdavidxu
123201546Sdavidxu	if (value > SEM_VALUE_MAX) {
124201546Sdavidxu		errno = EINVAL;
125201546Sdavidxu		return (-1);
126201546Sdavidxu	}
127201546Sdavidxu
128201546Sdavidxu	bzero(sem, sizeof(sem_t));
129201546Sdavidxu	sem->_magic = SEM_MAGIC;
130201546Sdavidxu	sem->_kern._count = (u_int32_t)value;
131201546Sdavidxu	sem->_kern._has_waiters = 0;
132201546Sdavidxu	sem->_kern._flags = pshared ? USYNC_PROCESS_SHARED : 0;
133201546Sdavidxu	return (0);
134201546Sdavidxu}
135201546Sdavidxu
136201546Sdavidxusem_t *
137201561Sdavidxu_sem_open(const char *name, int flags, ...)
138201546Sdavidxu{
139201546Sdavidxu	char path[PATH_MAX];
140201546Sdavidxu
141201546Sdavidxu	struct stat sb;
142201546Sdavidxu	va_list ap;
143201546Sdavidxu	struct sem_nameinfo *ni = NULL;
144201546Sdavidxu	sem_t *sem = NULL;
145201546Sdavidxu	int fd = -1, mode, len;
146201546Sdavidxu
147201546Sdavidxu	if (name[0] != '/') {
148201546Sdavidxu		errno = EINVAL;
149201546Sdavidxu		return (NULL);
150201546Sdavidxu	}
151201546Sdavidxu	name++;
152201546Sdavidxu
153201546Sdavidxu	if (flags & ~(O_CREAT|O_EXCL)) {
154201546Sdavidxu		errno = EINVAL;
155201546Sdavidxu		return (NULL);
156201546Sdavidxu	}
157201546Sdavidxu
158201546Sdavidxu	_pthread_once(&once, sem_module_init);
159201546Sdavidxu
160201546Sdavidxu	_pthread_mutex_lock(&sem_llock);
161201546Sdavidxu	LIST_FOREACH(ni, &sem_list, next) {
162201546Sdavidxu		if (strcmp(name, ni->name) == 0) {
163201546Sdavidxu			ni->open_count++;
164201546Sdavidxu			sem = ni->sem;
165201546Sdavidxu			_pthread_mutex_unlock(&sem_llock);
166201546Sdavidxu			return (sem);
167201546Sdavidxu		}
168201546Sdavidxu	}
169201546Sdavidxu
170201546Sdavidxu	if (flags & O_CREAT) {
171201546Sdavidxu		va_start(ap, flags);
172201546Sdavidxu		mode = va_arg(ap, int);
173201546Sdavidxu		va_end(ap);
174201546Sdavidxu	}
175201546Sdavidxu
176201546Sdavidxu	len = sizeof(*ni) + strlen(name) + 1;
177201546Sdavidxu	ni = (struct sem_nameinfo *)malloc(len);
178201546Sdavidxu	if (ni == NULL) {
179201546Sdavidxu		errno = ENOSPC;
180201546Sdavidxu		goto error;
181201546Sdavidxu	}
182201546Sdavidxu
183201546Sdavidxu	ni->name = (char *)(ni+1);
184201546Sdavidxu	strcpy(ni->name, name);
185201546Sdavidxu
186201546Sdavidxu	strcpy(path, SEM_PREFIX);
187201546Sdavidxu	if (strlcat(path, name, sizeof(path)) >= sizeof(path)) {
188201546Sdavidxu		errno = ENAMETOOLONG;
189201546Sdavidxu		goto error;
190201546Sdavidxu	}
191201546Sdavidxu
192201546Sdavidxu	fd = _open(path, flags|O_RDWR, mode);
193201546Sdavidxu	if (fd == -1)
194201546Sdavidxu		goto error;
195201546Sdavidxu	if (flock(fd, LOCK_EX) == -1)
196201546Sdavidxu		goto error;
197201546Sdavidxu	if (_fstat(fd, &sb)) {
198201546Sdavidxu		flock(fd, LOCK_UN);
199201546Sdavidxu		goto error;
200201546Sdavidxu	}
201201546Sdavidxu	if (sb.st_size < sizeof(sem_t)) {
202201546Sdavidxu		sem_t tmp;
203201546Sdavidxu
204201546Sdavidxu		tmp._magic = SEM_MAGIC;
205201546Sdavidxu		tmp._kern._has_waiters = 0;
206201546Sdavidxu		tmp._kern._count = 0;
207201546Sdavidxu		tmp._kern._flags = USYNC_PROCESS_SHARED | SEM_NAMED;
208201546Sdavidxu		if (_write(fd, &tmp, sizeof(tmp)) != sizeof(tmp)) {
209201546Sdavidxu			flock(fd, LOCK_UN);
210201546Sdavidxu			goto error;
211201546Sdavidxu		}
212201546Sdavidxu	}
213201546Sdavidxu	flock(fd, LOCK_UN);
214201546Sdavidxu	sem = (sem_t *)mmap(NULL, sizeof(sem_t), PROT_READ|PROT_WRITE,
215201546Sdavidxu		MAP_SHARED|MAP_NOSYNC, fd, 0);
216201546Sdavidxu	if (sem == MAP_FAILED) {
217201546Sdavidxu		sem = NULL;
218201546Sdavidxu		if (errno == ENOMEM)
219201546Sdavidxu			errno = ENOSPC;
220201546Sdavidxu		goto error;
221201546Sdavidxu	}
222201546Sdavidxu	if (sem->_magic != SEM_MAGIC) {
223201546Sdavidxu		errno = EINVAL;
224201546Sdavidxu		goto error;
225201546Sdavidxu	}
226201546Sdavidxu	ni->open_count = 1;
227201546Sdavidxu	ni->sem = sem;
228201546Sdavidxu	LIST_INSERT_HEAD(&sem_list, ni, next);
229201546Sdavidxu	_pthread_mutex_unlock(&sem_llock);
230201546Sdavidxu	_close(fd);
231201546Sdavidxu	return (sem);
232201546Sdavidxu
233201546Sdavidxuerror:
234201546Sdavidxu	_pthread_mutex_unlock(&sem_llock);
235201546Sdavidxu	if (fd != -1)
236201546Sdavidxu		_close(fd);
237201546Sdavidxu	if (sem != NULL)
238201546Sdavidxu		munmap(sem, sizeof(sem_t));
239201546Sdavidxu	free(ni);
240201546Sdavidxu	return (SEM_FAILED);
241201546Sdavidxu}
242201546Sdavidxu
243201546Sdavidxuint
244201561Sdavidxu_sem_close(sem_t *sem)
245201546Sdavidxu{
246201546Sdavidxu	struct sem_nameinfo *ni;
247201546Sdavidxu
248201546Sdavidxu	if (sem_check_validity(sem) != 0)
249201546Sdavidxu		return (-1);
250201546Sdavidxu
251201546Sdavidxu	if (!(sem->_kern._flags & SEM_NAMED)) {
252201546Sdavidxu		errno = EINVAL;
253201546Sdavidxu		return (-1);
254201546Sdavidxu	}
255201546Sdavidxu
256201546Sdavidxu	_pthread_mutex_lock(&sem_llock);
257201546Sdavidxu	LIST_FOREACH(ni, &sem_list, next) {
258201546Sdavidxu		if (sem == ni->sem) {
259201546Sdavidxu			if (--ni->open_count > 0) {
260201546Sdavidxu				_pthread_mutex_unlock(&sem_llock);
261201546Sdavidxu				return (0);
262201546Sdavidxu			}
263201546Sdavidxu			else
264201546Sdavidxu				break;
265201546Sdavidxu		}
266201546Sdavidxu	}
267201546Sdavidxu
268201546Sdavidxu	if (ni) {
269201546Sdavidxu		LIST_REMOVE(ni, next);
270201546Sdavidxu		_pthread_mutex_unlock(&sem_llock);
271201546Sdavidxu		munmap(sem, sizeof(*sem));
272201546Sdavidxu		free(ni);
273201546Sdavidxu		return (0);
274201546Sdavidxu	}
275201546Sdavidxu	_pthread_mutex_unlock(&sem_llock);
276201546Sdavidxu	return (-1);
277201546Sdavidxu}
278201546Sdavidxu
279201546Sdavidxuint
280201561Sdavidxu_sem_unlink(const char *name)
281201546Sdavidxu{
282201546Sdavidxu	char path[PATH_MAX];
283201546Sdavidxu
284201546Sdavidxu	if (name[0] != '/') {
285201546Sdavidxu		errno = ENOENT;
286201546Sdavidxu		return -1;
287201546Sdavidxu	}
288201546Sdavidxu	name++;
289201546Sdavidxu
290201546Sdavidxu	strcpy(path, SEM_PREFIX);
291201546Sdavidxu	if (strlcat(path, name, sizeof(path)) >= sizeof(path)) {
292201546Sdavidxu		errno = ENAMETOOLONG;
293201546Sdavidxu		return (-1);
294201546Sdavidxu	}
295201546Sdavidxu	return unlink(path);
296201546Sdavidxu}
297201546Sdavidxu
298201546Sdavidxuint
299201561Sdavidxu_sem_destroy(sem_t *sem)
300201546Sdavidxu{
301201546Sdavidxu
302201546Sdavidxu	if (sem_check_validity(sem) != 0)
303201546Sdavidxu		return (-1);
304201546Sdavidxu
305201546Sdavidxu	if (sem->_kern._flags & SEM_NAMED) {
306201546Sdavidxu		errno = EINVAL;
307201546Sdavidxu		return (-1);
308201546Sdavidxu	}
309201546Sdavidxu	sem->_magic = 0;
310201546Sdavidxu	return (0);
311201546Sdavidxu}
312201546Sdavidxu
313201546Sdavidxuint
314201561Sdavidxu_sem_getvalue(sem_t * __restrict sem, int * __restrict sval)
315201546Sdavidxu{
316201546Sdavidxu
317201546Sdavidxu	if (sem_check_validity(sem) != 0)
318201546Sdavidxu		return (-1);
319201546Sdavidxu
320201546Sdavidxu	*sval = (int)sem->_kern._count;
321201546Sdavidxu	return (0);
322201546Sdavidxu}
323201546Sdavidxu
324201547Sdavidxustatic __inline int
325201546Sdavidxuusem_wake(struct _usem *sem)
326201546Sdavidxu{
327201546Sdavidxu	if (!sem->_has_waiters)
328201546Sdavidxu		return (0);
329201546Sdavidxu	return _umtx_op(sem, UMTX_OP_SEM_WAKE, 0, NULL, NULL);
330201546Sdavidxu}
331201546Sdavidxu
332201547Sdavidxustatic __inline int
333201546Sdavidxuusem_wait(struct _usem *sem, const struct timespec *timeout)
334201546Sdavidxu{
335201546Sdavidxu	if (timeout && (timeout->tv_sec < 0 || (timeout->tv_sec == 0 &&
336201546Sdavidxu	    timeout->tv_nsec <= 0))) {
337201546Sdavidxu		errno = ETIMEDOUT;
338201546Sdavidxu		return (-1);
339201546Sdavidxu	}
340201546Sdavidxu	return _umtx_op(sem, UMTX_OP_SEM_WAIT, 0, NULL,
341201546Sdavidxu			__DECONST(void*, timeout));
342201546Sdavidxu}
343201546Sdavidxu
344201546Sdavidxuint
345201561Sdavidxu_sem_trywait(sem_t *sem)
346201546Sdavidxu{
347201546Sdavidxu	int val;
348201546Sdavidxu
349201546Sdavidxu	if (sem_check_validity(sem) != 0)
350201546Sdavidxu		return (-1);
351201546Sdavidxu
352201546Sdavidxu	while ((val = sem->_kern._count) > 0) {
353201546Sdavidxu		if (atomic_cmpset_acq_int(&sem->_kern._count, val, val - 1))
354201546Sdavidxu			return (0);
355201546Sdavidxu	}
356201546Sdavidxu	errno = EAGAIN;
357201546Sdavidxu	return (-1);
358201546Sdavidxu}
359201546Sdavidxu
360201546Sdavidxustatic void
361201546Sdavidxusem_cancel_handler(void *arg)
362201546Sdavidxu{
363201546Sdavidxu	sem_t *sem = arg;
364201546Sdavidxu
365201546Sdavidxu	if (sem->_kern._has_waiters && sem->_kern._count)
366201546Sdavidxu		usem_wake(&sem->_kern);
367201546Sdavidxu}
368201546Sdavidxu
369201546Sdavidxu#define TIMESPEC_SUB(dst, src, val)                             \
370201546Sdavidxu        do {                                                    \
371201546Sdavidxu                (dst)->tv_sec = (src)->tv_sec - (val)->tv_sec;  \
372201546Sdavidxu                (dst)->tv_nsec = (src)->tv_nsec - (val)->tv_nsec; \
373201546Sdavidxu                if ((dst)->tv_nsec < 0) {                       \
374201546Sdavidxu                        (dst)->tv_sec--;                        \
375201546Sdavidxu                        (dst)->tv_nsec += 1000000000;           \
376201546Sdavidxu                }                                               \
377201546Sdavidxu        } while (0)
378201546Sdavidxu
379201546Sdavidxu
380201547Sdavidxustatic __inline int
381201546Sdavidxuenable_async_cancel(void)
382201546Sdavidxu{
383201546Sdavidxu	int old;
384201546Sdavidxu
385201546Sdavidxu	_pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &old);
386201546Sdavidxu	return (old);
387201546Sdavidxu}
388201546Sdavidxu
389201547Sdavidxustatic __inline void
390201546Sdavidxurestore_async_cancel(int val)
391201546Sdavidxu{
392201546Sdavidxu	_pthread_setcanceltype(val, NULL);
393201546Sdavidxu}
394201546Sdavidxu
395201546Sdavidxuint
396201561Sdavidxu_sem_timedwait(sem_t * __restrict sem,
397201546Sdavidxu	const struct timespec * __restrict abstime)
398201546Sdavidxu{
399201546Sdavidxu	struct timespec ts, ts2;
400201546Sdavidxu	int val, retval, saved_cancel;
401201546Sdavidxu
402201546Sdavidxu	if (sem_check_validity(sem) != 0)
403201546Sdavidxu		return (-1);
404201546Sdavidxu
405201546Sdavidxu	retval = 0;
406201546Sdavidxu	for (;;) {
407201546Sdavidxu		while ((val = sem->_kern._count) > 0) {
408201546Sdavidxu			if (atomic_cmpset_acq_int(&sem->_kern._count, val, val - 1))
409201546Sdavidxu				return (0);
410201546Sdavidxu		}
411201546Sdavidxu
412201546Sdavidxu		if (retval)
413201546Sdavidxu			break;
414201546Sdavidxu
415201546Sdavidxu		/*
416201546Sdavidxu		 * The timeout argument is only supposed to
417201546Sdavidxu		 * be checked if the thread would have blocked.
418201546Sdavidxu		 */
419201546Sdavidxu		if (abstime != NULL) {
420201546Sdavidxu			if (abstime->tv_nsec >= 1000000000 || abstime->tv_nsec < 0) {
421201546Sdavidxu				errno = EINVAL;
422201546Sdavidxu				return (-1);
423201546Sdavidxu			}
424201546Sdavidxu			clock_gettime(CLOCK_REALTIME, &ts);
425201546Sdavidxu			TIMESPEC_SUB(&ts2, abstime, &ts);
426201546Sdavidxu		}
427201546Sdavidxu		pthread_cleanup_push(sem_cancel_handler, sem);
428201546Sdavidxu		saved_cancel = enable_async_cancel();
429201546Sdavidxu		retval = usem_wait(&sem->_kern, abstime ? &ts2 : NULL);
430201546Sdavidxu		restore_async_cancel(saved_cancel);
431201546Sdavidxu		pthread_cleanup_pop(0);
432201546Sdavidxu	}
433201546Sdavidxu	return (retval);
434201546Sdavidxu}
435201546Sdavidxu
436201546Sdavidxuint
437201561Sdavidxu_sem_wait(sem_t *sem)
438201546Sdavidxu{
439201561Sdavidxu	return _sem_timedwait(sem, NULL);
440201546Sdavidxu}
441201546Sdavidxu
442201546Sdavidxu/*
443201546Sdavidxu * POSIX:
444201546Sdavidxu * The sem_post() interface is reentrant with respect to signals and may be
445201546Sdavidxu * invoked from a signal-catching function.
446201546Sdavidxu * The implementation does not use lock, so it should be safe.
447201546Sdavidxu */
448201546Sdavidxuint
449201561Sdavidxu_sem_post(sem_t *sem)
450201546Sdavidxu{
451201546Sdavidxu
452201546Sdavidxu	if (sem_check_validity(sem) != 0)
453201546Sdavidxu		return (-1);
454201546Sdavidxu
455201546Sdavidxu	atomic_add_rel_int(&sem->_kern._count, 1);
456201547Sdavidxu	return usem_wake(&sem->_kern);
457201546Sdavidxu}
458