11897Swollman/*
21897Swollman * Copyright (C) 2010 David Xu <davidxu@freebsd.org>.
31897Swollman * All rights reserved.
41897Swollman *
51897Swollman * Redistribution and use in source and binary forms, with or without
61897Swollman * modification, are permitted provided that the following conditions
71897Swollman * are met:
812798Swpaul * 1. Redistributions of source code must retain the above copyright
91897Swollman *    notice(s), this list of conditions and the following disclaimer as
101897Swollman *    the first lines of this file unmodified other than the possible
111897Swollman *    addition of one or more copyright notices.
1212798Swpaul * 2. Redistributions in binary form must reproduce the above copyright
131897Swollman *    notice(s), this list of conditions and the following disclaimer in
141897Swollman *    the documentation and/or other materials provided with the
151897Swollman *    distribution.
1612798Swpaul *
171897Swollman * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY
181897Swollman * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191897Swollman * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2012798Swpaul * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE
211897Swollman * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
221897Swollman * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
231897Swollman * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
2412798Swpaul * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
251897Swollman * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
261897Swollman * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
271897Swollman * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
281897Swollman *
2912798Swpaul * $FreeBSD$
3012798Swpaul */
3112798Swpaul
321897Swollman#include "namespace.h"
3312798Swpaul#include <sys/types.h>
341897Swollman#include <sys/queue.h>
351897Swollman#include <sys/mman.h>
361897Swollman#include <sys/stat.h>
378874Srgrimes#include <errno.h>
388874Srgrimes#include <machine/atomic.h>
391897Swollman#include <sys/umtx.h>
401897Swollman#include <limits.h>
4112798Swpaul#include <fcntl.h>
4217142Sjkh#include <pthread.h>
4312798Swpaul#include <stdarg.h>
441897Swollman#include <stdlib.h>
451897Swollman#include <string.h>
4617142Sjkh#include <time.h>
4717142Sjkh#include <semaphore.h>
4817142Sjkh#include <unistd.h>
4917142Sjkh#include "un-namespace.h"
5017142Sjkh#include "libc_private.h"
5117142Sjkh
5217142Sjkh__weak_reference(_sem_close, sem_close);
5317142Sjkh__weak_reference(_sem_destroy, sem_destroy);
5417142Sjkh__weak_reference(_sem_getvalue, sem_getvalue);
5517142Sjkh__weak_reference(_sem_init, sem_init);
561897Swollman__weak_reference(_sem_open, sem_open);
571897Swollman__weak_reference(_sem_post, sem_post);
588874Srgrimes__weak_reference(_sem_timedwait, sem_timedwait);
591897Swollman__weak_reference(_sem_trywait, sem_trywait);
601897Swollman__weak_reference(_sem_unlink, sem_unlink);
611897Swollman__weak_reference(_sem_wait, sem_wait);
621897Swollman
631897Swollman#define SEM_PREFIX	"/tmp/SEMD"
6412798Swpaul#define SEM_MAGIC	((u_int32_t)0x73656d31)
651897Swollman
661897Swollmanstruct sem_nameinfo {
6712798Swpaul	int open_count;
6812798Swpaul	char *name;
6912798Swpaul	dev_t dev;
7012798Swpaul	ino_t ino;
7112798Swpaul	sem_t *sem;
7212798Swpaul	LIST_ENTRY(sem_nameinfo) next;
7312798Swpaul};
7412798Swpaul
7512798Swpaulstatic pthread_once_t once = PTHREAD_ONCE_INIT;
7612798Swpaulstatic pthread_mutex_t sem_llock;
7712798Swpaulstatic LIST_HEAD(,sem_nameinfo) sem_list = LIST_HEAD_INITIALIZER(sem_list);
7812798Swpaul
7912798Swpaulstatic void
8012798Swpaulsem_prefork()
811897Swollman{
821897Swollman
831897Swollman	_pthread_mutex_lock(&sem_llock);
841897Swollman}
851897Swollman
861897Swollmanstatic void
871897Swollmansem_postfork()
881897Swollman{
891897Swollman	_pthread_mutex_unlock(&sem_llock);
901897Swollman}
911897Swollman
921897Swollmanstatic void
931897Swollmansem_child_postfork()
941897Swollman{
9517142Sjkh	_pthread_mutex_unlock(&sem_llock);
9617142Sjkh}
971897Swollman
981897Swollmanstatic void
991897Swollmansem_module_init(void)
1001897Swollman{
10117142Sjkh	pthread_mutexattr_t ma;
1021897Swollman
1031897Swollman	_pthread_mutexattr_init(&ma);
1041897Swollman	_pthread_mutexattr_settype(&ma,  PTHREAD_MUTEX_RECURSIVE);
1051897Swollman	_pthread_mutex_init(&sem_llock, &ma);
10612798Swpaul	_pthread_mutexattr_destroy(&ma);
1071897Swollman	_pthread_atfork(sem_prefork, sem_postfork, sem_child_postfork);
1081897Swollman}
1091897Swollman
1101897Swollmanstatic inline int
1111897Swollmansem_check_validity(sem_t *sem)
1121897Swollman{
1131897Swollman
11417142Sjkh	if (sem->_magic == SEM_MAGIC)
1151897Swollman		return (0);
1161897Swollman	else {
1171897Swollman		errno = EINVAL;
1181897Swollman		return (-1);
1191897Swollman	}
1201897Swollman}
1211897Swollman
1221897Swollmanint
1231897Swollman_sem_init(sem_t *sem, int pshared, unsigned int value)
1241897Swollman{
12517142Sjkh
12612798Swpaul	if (value > SEM_VALUE_MAX) {
12712798Swpaul		errno = EINVAL;
12812798Swpaul		return (-1);
1291897Swollman	}
13012798Swpaul
1311897Swollman	bzero(sem, sizeof(sem_t));
13212798Swpaul	sem->_magic = SEM_MAGIC;
13312798Swpaul	sem->_kern._count = (u_int32_t)value;
13412798Swpaul	sem->_kern._has_waiters = 0;
13512798Swpaul	sem->_kern._flags = pshared ? USYNC_PROCESS_SHARED : 0;
13612798Swpaul	return (0);
13712798Swpaul}
13812798Swpaul
13912798Swpaulsem_t *
14012798Swpaul_sem_open(const char *name, int flags, ...)
14112798Swpaul{
14212798Swpaul	char path[PATH_MAX];
14312798Swpaul
14412798Swpaul	struct stat sb;
14512798Swpaul	va_list ap;
1461897Swollman	struct sem_nameinfo *ni = NULL;
1471897Swollman	sem_t *sem = NULL;
1481897Swollman	int fd = -1, mode, len, errsave;
14917142Sjkh	int value = 0;
15012798Swpaul
15112798Swpaul	if (name[0] != '/') {
15212798Swpaul		errno = EINVAL;
15312798Swpaul		return (SEM_FAILED);
15412798Swpaul	}
15512798Swpaul	name++;
15612798Swpaul	strcpy(path, SEM_PREFIX);
15712798Swpaul	if (strlcat(path, name, sizeof(path)) >= sizeof(path)) {
15812798Swpaul		errno = ENAMETOOLONG;
15912798Swpaul		return (SEM_FAILED);
16012798Swpaul	}
16112798Swpaul	if (flags & ~(O_CREAT|O_EXCL)) {
16212798Swpaul		errno = EINVAL;
16312798Swpaul		return (SEM_FAILED);
16412798Swpaul	}
16517142Sjkh	if ((flags & O_CREAT) != 0) {
16612798Swpaul		va_start(ap, flags);
16712798Swpaul		mode = va_arg(ap, int);
16812798Swpaul		value = va_arg(ap, int);
16912798Swpaul		va_end(ap);
17012798Swpaul	}
17112798Swpaul	fd = -1;
17217142Sjkh	_pthread_once(&once, sem_module_init);
1731897Swollman
1741897Swollman	_pthread_mutex_lock(&sem_llock);
1751897Swollman	LIST_FOREACH(ni, &sem_list, next) {
1761897Swollman		if (ni->name != NULL && strcmp(name, ni->name) == 0) {
1771897Swollman			fd = _open(path, flags | O_RDWR | O_CLOEXEC |
1781897Swollman			    O_EXLOCK, mode);
1791897Swollman			if (fd == -1 || _fstat(fd, &sb) == -1)
18017142Sjkh				goto error;
1811897Swollman			if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT |
1821897Swollman			    O_EXCL) || ni->dev != sb.st_dev ||
1831897Swollman			    ni->ino != sb.st_ino) {
1841897Swollman				ni->name = NULL;
1851897Swollman				ni = NULL;
1861897Swollman				break;
1871897Swollman			}
1881897Swollman			ni->open_count++;
18917142Sjkh			sem = ni->sem;
1901897Swollman			_pthread_mutex_unlock(&sem_llock);
1911897Swollman			_close(fd);
1921897Swollman			return (sem);
1931897Swollman		}
1941897Swollman	}
1951897Swollman
19617142Sjkh	len = sizeof(*ni) + strlen(name) + 1;
19712798Swpaul	ni = (struct sem_nameinfo *)malloc(len);
19812798Swpaul	if (ni == NULL) {
1991897Swollman		errno = ENOSPC;
2001897Swollman		goto error;
2011897Swollman	}
20212798Swpaul
20312798Swpaul	ni->name = (char *)(ni+1);
20412798Swpaul	strcpy(ni->name, name);
20512798Swpaul
20612798Swpaul	if (fd == -1) {
20712798Swpaul		fd = _open(path, flags | O_RDWR | O_CLOEXEC | O_EXLOCK, mode);
2081897Swollman		if (fd == -1 || _fstat(fd, &sb) == -1)
20912798Swpaul			goto error;
2101897Swollman	}
21112798Swpaul	if (sb.st_size < sizeof(sem_t)) {
2121897Swollman		sem_t tmp;
2131897Swollman
2141897Swollman		tmp._magic = SEM_MAGIC;
21512798Swpaul		tmp._kern._has_waiters = 0;
2161897Swollman		tmp._kern._count = value;
2171897Swollman		tmp._kern._flags = USYNC_PROCESS_SHARED | SEM_NAMED;
2181897Swollman		if (_write(fd, &tmp, sizeof(tmp)) != sizeof(tmp))
21917142Sjkh			goto error;
2201897Swollman	}
2211897Swollman	flock(fd, LOCK_UN);
2221897Swollman	sem = (sem_t *)mmap(NULL, sizeof(sem_t), PROT_READ|PROT_WRITE,
22312798Swpaul		MAP_SHARED|MAP_NOSYNC, fd, 0);
2241897Swollman	if (sem == MAP_FAILED) {
2251897Swollman		sem = NULL;
2261897Swollman		if (errno == ENOMEM)
2271897Swollman			errno = ENOSPC;
22817142Sjkh		goto error;
2291897Swollman	}
2301897Swollman	if (sem->_magic != SEM_MAGIC) {
2311897Swollman		errno = EINVAL;
2321897Swollman		goto error;
2331897Swollman	}
2341897Swollman	ni->open_count = 1;
2351897Swollman	ni->sem = sem;
2361897Swollman	ni->dev = sb.st_dev;
2371897Swollman	ni->ino = sb.st_ino;
2381897Swollman	LIST_INSERT_HEAD(&sem_list, ni, next);
2391897Swollman	_close(fd);
2401897Swollman	_pthread_mutex_unlock(&sem_llock);
2411897Swollman	return (sem);
2421897Swollman
2431897Swollmanerror:
2441897Swollman	errsave = errno;
24512798Swpaul	if (fd != -1)
2461897Swollman		_close(fd);
2471897Swollman	if (sem != NULL)
2481897Swollman		munmap(sem, sizeof(sem_t));
2491897Swollman	free(ni);
2501897Swollman	_pthread_mutex_unlock(&sem_llock);
2511897Swollman	errno = errsave;
2521897Swollman	return (SEM_FAILED);
2531897Swollman}
2541897Swollman
2551897Swollmanint
2561897Swollman_sem_close(sem_t *sem)
2571897Swollman{
2581897Swollman	struct sem_nameinfo *ni;
2591897Swollman
2601897Swollman	if (sem_check_validity(sem) != 0)
2611897Swollman		return (-1);
2621897Swollman
26312798Swpaul	if (!(sem->_kern._flags & SEM_NAMED)) {
2641897Swollman		errno = EINVAL;
2651897Swollman		return (-1);
2661897Swollman	}
2671897Swollman
2681897Swollman	_pthread_once(&once, sem_module_init);
2691897Swollman
2701897Swollman	_pthread_mutex_lock(&sem_llock);
2711897Swollman	LIST_FOREACH(ni, &sem_list, next) {
2721897Swollman		if (sem == ni->sem) {
2731897Swollman			if (--ni->open_count > 0) {
2741897Swollman				_pthread_mutex_unlock(&sem_llock);
2751897Swollman				return (0);
2761897Swollman			}
2771897Swollman			else
2781897Swollman				break;
2791897Swollman		}
2801897Swollman	}
2811897Swollman
2821897Swollman	if (ni) {
28312798Swpaul		LIST_REMOVE(ni, next);
2841897Swollman		_pthread_mutex_unlock(&sem_llock);
2851897Swollman		munmap(sem, sizeof(*sem));
28612798Swpaul		free(ni);
28712798Swpaul		return (0);
2881897Swollman	}
2891897Swollman	_pthread_mutex_unlock(&sem_llock);
2901897Swollman	errno = EINVAL;
2911897Swollman	return (-1);
2921897Swollman}
29312798Swpaul
2941897Swollmanint
2951897Swollman_sem_unlink(const char *name)
2961897Swollman{
2971897Swollman	char path[PATH_MAX];
2981897Swollman
2991897Swollman	if (name[0] != '/') {
3001897Swollman		errno = ENOENT;
3011897Swollman		return -1;
3021897Swollman	}
3031897Swollman	name++;
3041897Swollman	strcpy(path, SEM_PREFIX);
30517142Sjkh	if (strlcat(path, name, sizeof(path)) >= sizeof(path)) {
3061897Swollman		errno = ENAMETOOLONG;
3071897Swollman		return (-1);
3081897Swollman	}
3091897Swollman
3101897Swollman	return (unlink(path));
3111897Swollman}
3121897Swollman
3131897Swollmanint
31417142Sjkh_sem_destroy(sem_t *sem)
31512798Swpaul{
31612798Swpaul
31712798Swpaul	if (sem_check_validity(sem) != 0)
31812798Swpaul		return (-1);
31912798Swpaul
32012798Swpaul	if (sem->_kern._flags & SEM_NAMED) {
3211897Swollman		errno = EINVAL;
32212798Swpaul		return (-1);
32312798Swpaul	}
32412798Swpaul	sem->_magic = 0;
32512798Swpaul	return (0);
32612798Swpaul}
32712798Swpaul
32812798Swpaulint
32912798Swpaul_sem_getvalue(sem_t * __restrict sem, int * __restrict sval)
33012798Swpaul{
33112798Swpaul
33212798Swpaul	if (sem_check_validity(sem) != 0)
33312798Swpaul		return (-1);
33412798Swpaul
33517142Sjkh	*sval = (int)sem->_kern._count;
3361897Swollman	return (0);
3371897Swollman}
3381897Swollman
3391897Swollmanstatic __inline int
3401897Swollmanusem_wake(struct _usem *sem)
3411897Swollman{
3421897Swollman	if (!sem->_has_waiters)
34312798Swpaul		return (0);
3441897Swollman	return _umtx_op(sem, UMTX_OP_SEM_WAKE, 0, NULL, NULL);
3451897Swollman}
34612798Swpaul
3471897Swollmanstatic __inline int
3481897Swollmanusem_wait(struct _usem *sem, const struct timespec *timeout)
34912798Swpaul{
35012798Swpaul	if (timeout && (timeout->tv_sec < 0 || (timeout->tv_sec == 0 &&
35112798Swpaul	    timeout->tv_nsec <= 0))) {
35212798Swpaul		errno = ETIMEDOUT;
3531897Swollman		return (-1);
3541897Swollman	}
3551897Swollman	return _umtx_op(sem, UMTX_OP_SEM_WAIT, 0, NULL,
3561897Swollman			__DECONST(void*, timeout));
35712798Swpaul}
35812798Swpaul
35912798Swpaulint
36012798Swpaul_sem_trywait(sem_t *sem)
36112798Swpaul{
36212798Swpaul	int val;
36312798Swpaul
36412798Swpaul	if (sem_check_validity(sem) != 0)
36512798Swpaul		return (-1);
3661897Swollman
3671897Swollman	while ((val = sem->_kern._count) > 0) {
3681897Swollman		if (atomic_cmpset_acq_int(&sem->_kern._count, val, val - 1))
3691897Swollman			return (0);
3701897Swollman	}
3711897Swollman	errno = EAGAIN;
3721897Swollman	return (-1);
3731897Swollman}
3741897Swollman
37512798Swpaul#define TIMESPEC_SUB(dst, src, val)                             \
37612798Swpaul        do {                                                    \
37712798Swpaul                (dst)->tv_sec = (src)->tv_sec - (val)->tv_sec;  \
37812798Swpaul                (dst)->tv_nsec = (src)->tv_nsec - (val)->tv_nsec; \
37912798Swpaul                if ((dst)->tv_nsec < 0) {                       \
38012798Swpaul                        (dst)->tv_sec--;                        \
38112798Swpaul                        (dst)->tv_nsec += 1000000000;           \
38212798Swpaul                }                                               \
38312798Swpaul        } while (0)
3841897Swollman
38512798Swpaul
3861897Swollmanint
3871897Swollman_sem_timedwait(sem_t * __restrict sem,
38817142Sjkh	const struct timespec * __restrict abstime)
38917142Sjkh{
39017142Sjkh	struct timespec ts, ts2;
3911897Swollman	int val, retval;
3921897Swollman
3931897Swollman	if (sem_check_validity(sem) != 0)
3941897Swollman		return (-1);
3951897Swollman
39612798Swpaul	retval = 0;
3971897Swollman	_pthread_testcancel();
3981897Swollman	for (;;) {
3991897Swollman		while ((val = sem->_kern._count) > 0) {
40012798Swpaul			if (atomic_cmpset_acq_int(&sem->_kern._count, val, val - 1))
40112798Swpaul				return (0);
40212798Swpaul		}
40312798Swpaul
40412798Swpaul		if (retval) {
40512798Swpaul			_pthread_testcancel();
40612798Swpaul			break;
40712798Swpaul		}
40812798Swpaul
40912798Swpaul		/*
41012798Swpaul		 * The timeout argument is only supposed to
41112798Swpaul		 * be checked if the thread would have blocked.
4121897Swollman		 */
41312798Swpaul		if (abstime != NULL) {
41412798Swpaul			if (abstime->tv_nsec >= 1000000000 || abstime->tv_nsec < 0) {
41512798Swpaul				errno = EINVAL;
41612798Swpaul				return (-1);
4171897Swollman			}
41812798Swpaul			clock_gettime(CLOCK_REALTIME, &ts);
41912798Swpaul			TIMESPEC_SUB(&ts2, abstime, &ts);
42012798Swpaul		}
42112798Swpaul		_pthread_cancel_enter(1);
42212798Swpaul		retval = usem_wait(&sem->_kern, abstime ? &ts2 : NULL);
42312798Swpaul		_pthread_cancel_leave(0);
42412798Swpaul	}
42512798Swpaul	return (retval);
42612798Swpaul}
42712798Swpaul
42812798Swpaulint
42912798Swpaul_sem_wait(sem_t *sem)
43012798Swpaul{
43112798Swpaul	return _sem_timedwait(sem, NULL);
43212798Swpaul}
43312798Swpaul
43412798Swpaul/*
43512798Swpaul * POSIX:
43612798Swpaul * The sem_post() interface is reentrant with respect to signals and may be
43712798Swpaul * invoked from a signal-catching function.
43812798Swpaul * The implementation does not use lock, so it should be safe.
43912798Swpaul */
44012798Swpaulint
44112798Swpaul_sem_post(sem_t *sem)
44212798Swpaul{
44312798Swpaul
44412798Swpaul	if (sem_check_validity(sem) != 0)
44512798Swpaul		return (-1);
44612798Swpaul
44712798Swpaul	atomic_add_rel_int(&sem->_kern._count, 1);
44812798Swpaul	return usem_wake(&sem->_kern);
44912798Swpaul}
45012798Swpaul