xref: /freebsd/lib/libc/gen/sem.c (revision dd554467bb5560004a538e60c8aa7ebb1f795be2)
1bf4dc877SAlfred Perlstein /*
29b0f1823SDavid Xu  * Copyright (C) 2010 David Xu <davidxu@freebsd.org>.
3bf4dc877SAlfred Perlstein  * Copyright (C) 2000 Jason Evans <jasone@freebsd.org>.
4bf4dc877SAlfred Perlstein  * All rights reserved.
5bf4dc877SAlfred Perlstein  *
6bf4dc877SAlfred Perlstein  * Redistribution and use in source and binary forms, with or without
7bf4dc877SAlfred Perlstein  * modification, are permitted provided that the following conditions
8bf4dc877SAlfred Perlstein  * are met:
9bf4dc877SAlfred Perlstein  * 1. Redistributions of source code must retain the above copyright
10bf4dc877SAlfred Perlstein  *    notice(s), this list of conditions and the following disclaimer as
11bf4dc877SAlfred Perlstein  *    the first lines of this file unmodified other than the possible
12bf4dc877SAlfred Perlstein  *    addition of one or more copyright notices.
13bf4dc877SAlfred Perlstein  * 2. Redistributions in binary form must reproduce the above copyright
14bf4dc877SAlfred Perlstein  *    notice(s), this list of conditions and the following disclaimer in
15bf4dc877SAlfred Perlstein  *    the documentation and/or other materials provided with the
16bf4dc877SAlfred Perlstein  *    distribution.
17bf4dc877SAlfred Perlstein  *
18bf4dc877SAlfred Perlstein  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY
19bf4dc877SAlfred Perlstein  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20bf4dc877SAlfred Perlstein  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21bf4dc877SAlfred Perlstein  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE
22bf4dc877SAlfred Perlstein  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23bf4dc877SAlfred Perlstein  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24bf4dc877SAlfred Perlstein  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
25bf4dc877SAlfred Perlstein  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26bf4dc877SAlfred Perlstein  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
27bf4dc877SAlfred Perlstein  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28bf4dc877SAlfred Perlstein  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29bf4dc877SAlfred Perlstein  *
30bf4dc877SAlfred Perlstein  * $FreeBSD$
31bf4dc877SAlfred Perlstein  */
32bf4dc877SAlfred Perlstein 
335c70dac8SDaniel Eischen /*
345c70dac8SDaniel Eischen  * Some notes about this implementation.
355c70dac8SDaniel Eischen  *
365c70dac8SDaniel Eischen  * This is mostly a simple implementation of POSIX semaphores that
375c70dac8SDaniel Eischen  * does not need threading.  Any semaphore created is a kernel-based
385c70dac8SDaniel Eischen  * semaphore regardless of the pshared attribute.  This is necessary
395c70dac8SDaniel Eischen  * because libc's stub for pthread_cond_wait() doesn't really wait,
405c70dac8SDaniel Eischen  * and it is not worth the effort impose this behavior on libc.
415c70dac8SDaniel Eischen  *
425c70dac8SDaniel Eischen  * All functions here are designed to be thread-safe so that a
435c70dac8SDaniel Eischen  * threads library need not provide wrappers except to make
445c70dac8SDaniel Eischen  * sem_wait() and sem_timedwait() cancellation points or to
455c70dac8SDaniel Eischen  * provide a faster userland implementation for non-pshared
465c70dac8SDaniel Eischen  * semaphores.
475c70dac8SDaniel Eischen  *
485c70dac8SDaniel Eischen  * Also, this implementation of semaphores cannot really support
495c70dac8SDaniel Eischen  * real pshared semaphores.  The sem_t is an allocated object
505c70dac8SDaniel Eischen  * and can't be seen by other processes when placed in shared
515c70dac8SDaniel Eischen  * memory.  It should work across forks as long as the semaphore
525c70dac8SDaniel Eischen  * is created before any forks.
535c70dac8SDaniel Eischen  *
545c70dac8SDaniel Eischen  * The function sem_init() should be overridden by a threads
555c70dac8SDaniel Eischen  * library if it wants to provide a different userland version
565c70dac8SDaniel Eischen  * of semaphores.  The functions sem_wait() and sem_timedwait()
575c70dac8SDaniel Eischen  * need to be wrapped to provide cancellation points.  The function
585c70dac8SDaniel Eischen  * sem_post() may need to be wrapped to be signal-safe.
595c70dac8SDaniel Eischen  */
60e0554a53SJacques Vidrine #include "namespace.h"
61d8f77b45SStefan Farfeleder #include <sys/types.h>
625c70dac8SDaniel Eischen #include <sys/queue.h>
639b0f1823SDavid Xu #include <machine/atomic.h>
64bf4dc877SAlfred Perlstein #include <errno.h>
659b0f1823SDavid Xu #include <sys/umtx.h>
669b0f1823SDavid Xu #include <sys/_semaphore.h>
679b0f1823SDavid Xu #include <limits.h>
68bf4dc877SAlfred Perlstein #include <fcntl.h>
695c70dac8SDaniel Eischen #include <pthread.h>
70bf4dc877SAlfred Perlstein #include <stdarg.h>
715c70dac8SDaniel Eischen #include <stdlib.h>
725c70dac8SDaniel Eischen #include <time.h>
73e0554a53SJacques Vidrine #include "un-namespace.h"
745c70dac8SDaniel Eischen #include "libc_private.h"
75bf4dc877SAlfred Perlstein 
769b0f1823SDavid Xu /*
779b0f1823SDavid Xu  * Old semaphore definitions.
789b0f1823SDavid Xu  */
799b0f1823SDavid Xu struct sem {
809b0f1823SDavid Xu #define SEM_MAGIC       ((u_int32_t) 0x09fa4012)
819b0f1823SDavid Xu         u_int32_t       magic;
829b0f1823SDavid Xu         pthread_mutex_t lock;
839b0f1823SDavid Xu         pthread_cond_t  gtzero;
849b0f1823SDavid Xu         u_int32_t       count;
859b0f1823SDavid Xu         u_int32_t       nwaiters;
869b0f1823SDavid Xu #define SEM_USER        (NULL)
879b0f1823SDavid Xu         semid_t         semid;  /* semaphore id if kernel (shared) semaphore */
889b0f1823SDavid Xu         int             syssem; /* 1 if kernel (shared) semaphore */
899b0f1823SDavid Xu         LIST_ENTRY(sem) entry;
909b0f1823SDavid Xu         struct sem      **backpointer;
919b0f1823SDavid Xu };
929b0f1823SDavid Xu 
939b0f1823SDavid Xu typedef struct sem* sem_t;
949b0f1823SDavid Xu 
959b0f1823SDavid Xu #define SEM_FAILED     ((sem_t *)0)
969b0f1823SDavid Xu #define SEM_VALUE_MAX  __INT_MAX
979b0f1823SDavid Xu 
989b0f1823SDavid Xu #define SYM_FB10(sym)                   __CONCAT(sym, _fb10)
999b0f1823SDavid Xu #define WEAK_REF(sym, alias)            __weak_reference(sym, alias)
1009b0f1823SDavid Xu #define SYM_COMPAT(sym, impl, ver)      __sym_compat(sym, impl, ver)
1019b0f1823SDavid Xu 
1029b0f1823SDavid Xu #define FB10_COMPAT(func, sym)                          \
1039b0f1823SDavid Xu         WEAK_REF(func, SYM_FB10(sym));                  \
1049b0f1823SDavid Xu         SYM_COMPAT(sym, SYM_FB10(sym), FBSD_1.0)
1059b0f1823SDavid Xu 
106bf4dc877SAlfred Perlstein static sem_t sem_alloc(unsigned int value, semid_t semid, int system_sem);
107bf4dc877SAlfred Perlstein static void  sem_free(sem_t sem);
108bf4dc877SAlfred Perlstein 
109dd554467SAntoine Brodin static LIST_HEAD(, sem) named_sems = LIST_HEAD_INITIALIZER(named_sems);
110bf4dc877SAlfred Perlstein static pthread_mutex_t named_sems_mtx = PTHREAD_MUTEX_INITIALIZER;
111bf4dc877SAlfred Perlstein 
1129b0f1823SDavid Xu FB10_COMPAT(_libc_sem_init_compat, sem_init);
1139b0f1823SDavid Xu FB10_COMPAT(_libc_sem_destroy_compat, sem_destroy);
1149b0f1823SDavid Xu FB10_COMPAT(_libc_sem_open_compat, sem_open);
1159b0f1823SDavid Xu FB10_COMPAT(_libc_sem_close_compat, sem_close);
1169b0f1823SDavid Xu FB10_COMPAT(_libc_sem_unlink_compat, sem_unlink);
1179b0f1823SDavid Xu FB10_COMPAT(_libc_sem_wait_compat, sem_wait);
1189b0f1823SDavid Xu FB10_COMPAT(_libc_sem_trywait_compat, sem_trywait);
1199b0f1823SDavid Xu FB10_COMPAT(_libc_sem_timedwait_compat, sem_timedwait);
1209b0f1823SDavid Xu FB10_COMPAT(_libc_sem_post_compat, sem_post);
1219b0f1823SDavid Xu FB10_COMPAT(_libc_sem_getvalue_compat, sem_getvalue);
1225c70dac8SDaniel Eischen 
1235c70dac8SDaniel Eischen static inline int
1245c70dac8SDaniel Eischen sem_check_validity(sem_t *sem)
1255c70dac8SDaniel Eischen {
1265c70dac8SDaniel Eischen 
1275c70dac8SDaniel Eischen 	if ((sem != NULL) && ((*sem)->magic == SEM_MAGIC))
1285c70dac8SDaniel Eischen 		return (0);
1295c70dac8SDaniel Eischen 	else {
1305c70dac8SDaniel Eischen 		errno = EINVAL;
1315c70dac8SDaniel Eischen 		return (-1);
1325c70dac8SDaniel Eischen 	}
1335c70dac8SDaniel Eischen }
1345c70dac8SDaniel Eischen 
135bf4dc877SAlfred Perlstein static void
136bf4dc877SAlfred Perlstein sem_free(sem_t sem)
137bf4dc877SAlfred Perlstein {
138bf4dc877SAlfred Perlstein 
139bf4dc877SAlfred Perlstein 	sem->magic = 0;
140bf4dc877SAlfred Perlstein 	free(sem);
141bf4dc877SAlfred Perlstein }
142bf4dc877SAlfred Perlstein 
143bf4dc877SAlfred Perlstein static sem_t
144bf4dc877SAlfred Perlstein sem_alloc(unsigned int value, semid_t semid, int system_sem)
145bf4dc877SAlfred Perlstein {
146bf4dc877SAlfred Perlstein 	sem_t sem;
147bf4dc877SAlfred Perlstein 
148bf4dc877SAlfred Perlstein 	if (value > SEM_VALUE_MAX) {
149bf4dc877SAlfred Perlstein 		errno = EINVAL;
150bf4dc877SAlfred Perlstein 		return (NULL);
151bf4dc877SAlfred Perlstein 	}
152bf4dc877SAlfred Perlstein 
153bf4dc877SAlfred Perlstein 	sem = (sem_t)malloc(sizeof(struct sem));
154bf4dc877SAlfred Perlstein 	if (sem == NULL) {
155bf4dc877SAlfred Perlstein 		errno = ENOSPC;
156bf4dc877SAlfred Perlstein 		return (NULL);
157bf4dc877SAlfred Perlstein 	}
158bf4dc877SAlfred Perlstein 
159bf4dc877SAlfred Perlstein 	sem->count = (u_int32_t)value;
160bf4dc877SAlfred Perlstein 	sem->nwaiters = 0;
161bf4dc877SAlfred Perlstein 	sem->magic = SEM_MAGIC;
162bf4dc877SAlfred Perlstein 	sem->semid = semid;
163bf4dc877SAlfred Perlstein 	sem->syssem = system_sem;
164bf4dc877SAlfred Perlstein 	return (sem);
165bf4dc877SAlfred Perlstein }
166bf4dc877SAlfred Perlstein 
167bf4dc877SAlfred Perlstein int
1689b0f1823SDavid Xu _libc_sem_init_compat(sem_t *sem, int pshared, unsigned int value)
169bf4dc877SAlfred Perlstein {
170bf4dc877SAlfred Perlstein 	semid_t semid;
171bf4dc877SAlfred Perlstein 
172bf4dc877SAlfred Perlstein 	/*
1735c70dac8SDaniel Eischen 	 * We always have to create the kernel semaphore if the
1745c70dac8SDaniel Eischen 	 * threads library isn't present since libc's version of
1755c70dac8SDaniel Eischen 	 * pthread_cond_wait() is just a stub that doesn't really
1765c70dac8SDaniel Eischen 	 * wait.
177bf4dc877SAlfred Perlstein 	 */
1789b0f1823SDavid Xu 	semid = (semid_t)SEM_USER;
1799b0f1823SDavid Xu 	if ((pshared != 0) && ksem_init(&semid, value) != 0)
1805c70dac8SDaniel Eischen 		return (-1);
181bf4dc877SAlfred Perlstein 
1829b0f1823SDavid Xu 	*sem = sem_alloc(value, semid, pshared);
1835c70dac8SDaniel Eischen 	if ((*sem) == NULL) {
1849b0f1823SDavid Xu 		if (pshared != 0)
185bf4dc877SAlfred Perlstein 			ksem_destroy(semid);
1865c70dac8SDaniel Eischen 		return (-1);
1875c70dac8SDaniel Eischen 	}
1885c70dac8SDaniel Eischen 	return (0);
189bf4dc877SAlfred Perlstein }
190bf4dc877SAlfred Perlstein 
191bf4dc877SAlfred Perlstein int
1929b0f1823SDavid Xu _libc_sem_destroy_compat(sem_t *sem)
193bf4dc877SAlfred Perlstein {
194bf4dc877SAlfred Perlstein 	int retval;
195bf4dc877SAlfred Perlstein 
19608a6a888SDaniel Eischen 	if (sem_check_validity(sem) != 0)
1975c70dac8SDaniel Eischen 		return (-1);
198bf4dc877SAlfred Perlstein 
199bf4dc877SAlfred Perlstein 	/*
200bf4dc877SAlfred Perlstein 	 * If this is a system semaphore let the kernel track it otherwise
201bf4dc877SAlfred Perlstein 	 * make sure there are no waiters.
202bf4dc877SAlfred Perlstein 	 */
2035c70dac8SDaniel Eischen 	if ((*sem)->syssem != 0)
204bf4dc877SAlfred Perlstein 		retval = ksem_destroy((*sem)->semid);
2055c70dac8SDaniel Eischen 	else if ((*sem)->nwaiters > 0) {
206bf4dc877SAlfred Perlstein 		errno = EBUSY;
207bf4dc877SAlfred Perlstein 		retval = -1;
2085c70dac8SDaniel Eischen 	}
2095c70dac8SDaniel Eischen 	else {
2105c70dac8SDaniel Eischen 		retval = 0;
2115c70dac8SDaniel Eischen 		(*sem)->magic = 0;
212bf4dc877SAlfred Perlstein 	}
213bf4dc877SAlfred Perlstein 
2149b0f1823SDavid Xu 	if (retval == 0)
215bf4dc877SAlfred Perlstein 		sem_free(*sem);
2165c70dac8SDaniel Eischen 	return (retval);
217bf4dc877SAlfred Perlstein }
218bf4dc877SAlfred Perlstein 
219bf4dc877SAlfred Perlstein sem_t *
2209b0f1823SDavid Xu _libc_sem_open_compat(const char *name, int oflag, ...)
221bf4dc877SAlfred Perlstein {
222bf4dc877SAlfred Perlstein 	sem_t *sem;
223bf4dc877SAlfred Perlstein 	sem_t s;
224bf4dc877SAlfred Perlstein 	semid_t semid;
225bf4dc877SAlfred Perlstein 	mode_t mode;
226bf4dc877SAlfred Perlstein 	unsigned int value;
227bf4dc877SAlfred Perlstein 
228bf4dc877SAlfred Perlstein 	mode = 0;
229bf4dc877SAlfred Perlstein 	value = 0;
230bf4dc877SAlfred Perlstein 
231bf4dc877SAlfred Perlstein 	if ((oflag & O_CREAT) != 0) {
232bf4dc877SAlfred Perlstein 		va_list ap;
233bf4dc877SAlfred Perlstein 
234bf4dc877SAlfred Perlstein 		va_start(ap, oflag);
235bf4dc877SAlfred Perlstein 		mode = va_arg(ap, int);
236bf4dc877SAlfred Perlstein 		value = va_arg(ap, unsigned int);
237bf4dc877SAlfred Perlstein 		va_end(ap);
238bf4dc877SAlfred Perlstein 	}
239bf4dc877SAlfred Perlstein 	/*
240bf4dc877SAlfred Perlstein 	 * we can be lazy and let the kernel handle the "oflag",
241bf4dc877SAlfred Perlstein 	 * we'll just merge duplicate IDs into our list.
242bf4dc877SAlfred Perlstein 	 */
243bf4dc877SAlfred Perlstein 	if (ksem_open(&semid, name, oflag, mode, value) == -1)
244bf4dc877SAlfred Perlstein 		return (SEM_FAILED);
245bf4dc877SAlfred Perlstein 	/*
246bf4dc877SAlfred Perlstein 	 * search for a duplicate ID, we must return the same sem_t *
247bf4dc877SAlfred Perlstein 	 * if we locate one.
248bf4dc877SAlfred Perlstein 	 */
249bf4dc877SAlfred Perlstein 	_pthread_mutex_lock(&named_sems_mtx);
250bf4dc877SAlfred Perlstein 	LIST_FOREACH(s, &named_sems, entry) {
251bf4dc877SAlfred Perlstein 		if (s->semid == semid) {
2525c70dac8SDaniel Eischen 			sem = s->backpointer;
253bf4dc877SAlfred Perlstein 			_pthread_mutex_unlock(&named_sems_mtx);
2545c70dac8SDaniel Eischen 			return (sem);
255bf4dc877SAlfred Perlstein 		}
256bf4dc877SAlfred Perlstein 	}
257bf4dc877SAlfred Perlstein 	sem = (sem_t *)malloc(sizeof(*sem));
258bf4dc877SAlfred Perlstein 	if (sem == NULL)
259bf4dc877SAlfred Perlstein 		goto err;
260bf4dc877SAlfred Perlstein 	*sem = sem_alloc(value, semid, 1);
261bf4dc877SAlfred Perlstein 	if ((*sem) == NULL)
262bf4dc877SAlfred Perlstein 		goto err;
263a91b25dcSTim J. Robbins 	LIST_INSERT_HEAD(&named_sems, *sem, entry);
264bf4dc877SAlfred Perlstein 	(*sem)->backpointer = sem;
265a91b25dcSTim J. Robbins 	_pthread_mutex_unlock(&named_sems_mtx);
266bf4dc877SAlfred Perlstein 	return (sem);
267bf4dc877SAlfred Perlstein err:
268bf4dc877SAlfred Perlstein 	_pthread_mutex_unlock(&named_sems_mtx);
269bf4dc877SAlfred Perlstein 	ksem_close(semid);
270bf4dc877SAlfred Perlstein 	if (sem != NULL) {
271bf4dc877SAlfred Perlstein 		if (*sem != NULL)
272bf4dc877SAlfred Perlstein 			sem_free(*sem);
273bf4dc877SAlfred Perlstein 		else
274bf4dc877SAlfred Perlstein 			errno = ENOSPC;
275bf4dc877SAlfred Perlstein 		free(sem);
276bf4dc877SAlfred Perlstein 	} else {
277bf4dc877SAlfred Perlstein 		errno = ENOSPC;
278bf4dc877SAlfred Perlstein 	}
279bf4dc877SAlfred Perlstein 	return (SEM_FAILED);
280bf4dc877SAlfred Perlstein }
281bf4dc877SAlfred Perlstein 
282bf4dc877SAlfred Perlstein int
2839b0f1823SDavid Xu _libc_sem_close_compat(sem_t *sem)
284bf4dc877SAlfred Perlstein {
285bf4dc877SAlfred Perlstein 
2865c70dac8SDaniel Eischen 	if (sem_check_validity(sem) != 0)
2875c70dac8SDaniel Eischen 		return (-1);
2885c70dac8SDaniel Eischen 
289bf4dc877SAlfred Perlstein 	if ((*sem)->syssem == 0) {
290bf4dc877SAlfred Perlstein 		errno = EINVAL;
291bf4dc877SAlfred Perlstein 		return (-1);
292bf4dc877SAlfred Perlstein 	}
2935c70dac8SDaniel Eischen 
294bf4dc877SAlfred Perlstein 	_pthread_mutex_lock(&named_sems_mtx);
2955c70dac8SDaniel Eischen 	if (ksem_close((*sem)->semid) != 0) {
296bf4dc877SAlfred Perlstein 		_pthread_mutex_unlock(&named_sems_mtx);
297bf4dc877SAlfred Perlstein 		return (-1);
298bf4dc877SAlfred Perlstein 	}
299bf4dc877SAlfred Perlstein 	LIST_REMOVE((*sem), entry);
300bf4dc877SAlfred Perlstein 	_pthread_mutex_unlock(&named_sems_mtx);
301bf4dc877SAlfred Perlstein 	sem_free(*sem);
3025c70dac8SDaniel Eischen 	*sem = NULL;
303bf4dc877SAlfred Perlstein 	free(sem);
304bf4dc877SAlfred Perlstein 	return (0);
305bf4dc877SAlfred Perlstein }
306bf4dc877SAlfred Perlstein 
307bf4dc877SAlfred Perlstein int
3089b0f1823SDavid Xu _libc_sem_unlink_compat(const char *name)
309bf4dc877SAlfred Perlstein {
310bf4dc877SAlfred Perlstein 
311bf4dc877SAlfred Perlstein 	return (ksem_unlink(name));
312bf4dc877SAlfred Perlstein }
313bf4dc877SAlfred Perlstein 
3149b0f1823SDavid Xu static int
3159b0f1823SDavid Xu enable_async_cancel(void)
316bf4dc877SAlfred Perlstein {
3179b0f1823SDavid Xu 	int old;
318bf4dc877SAlfred Perlstein 
3199b0f1823SDavid Xu 	_pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &old);
3209b0f1823SDavid Xu 	return (old);
3219b0f1823SDavid Xu }
3229b0f1823SDavid Xu 
3239b0f1823SDavid Xu static void
3249b0f1823SDavid Xu restore_async_cancel(int val)
3259b0f1823SDavid Xu {
3269b0f1823SDavid Xu 	_pthread_setcanceltype(val, NULL);
3279b0f1823SDavid Xu }
3289b0f1823SDavid Xu 
3299b0f1823SDavid Xu static int
3309b0f1823SDavid Xu _umtx_wait_uint(volatile unsigned *mtx, unsigned id, const struct timespec *timeout)
3319b0f1823SDavid Xu {
3329b0f1823SDavid Xu 	if (timeout && (timeout->tv_sec < 0 || (timeout->tv_sec == 0 &&
3339b0f1823SDavid Xu 	    timeout->tv_nsec <= 0))) {
3349b0f1823SDavid Xu 		errno = ETIMEDOUT;
3355c70dac8SDaniel Eischen 		return (-1);
3369b0f1823SDavid Xu 	}
3379b0f1823SDavid Xu 	return _umtx_op(__DEVOLATILE(void *, mtx),
3389b0f1823SDavid Xu 		UMTX_OP_WAIT_UINT_PRIVATE, id, NULL, __DECONST(void*, timeout));
3399b0f1823SDavid Xu }
340bf4dc877SAlfred Perlstein 
3419b0f1823SDavid Xu static int
3429b0f1823SDavid Xu _umtx_wake(volatile void *mtx)
3439b0f1823SDavid Xu {
3449b0f1823SDavid Xu 	return _umtx_op(__DEVOLATILE(void *, mtx), UMTX_OP_WAKE_PRIVATE,
3459b0f1823SDavid Xu 			1, NULL, NULL);
3469b0f1823SDavid Xu }
3479b0f1823SDavid Xu 
3489b0f1823SDavid Xu #define TIMESPEC_SUB(dst, src, val)                             \
3499b0f1823SDavid Xu         do {                                                    \
3509b0f1823SDavid Xu                 (dst)->tv_sec = (src)->tv_sec - (val)->tv_sec;  \
3519b0f1823SDavid Xu                 (dst)->tv_nsec = (src)->tv_nsec - (val)->tv_nsec; \
3529b0f1823SDavid Xu                 if ((dst)->tv_nsec < 0) {                       \
3539b0f1823SDavid Xu                         (dst)->tv_sec--;                        \
3549b0f1823SDavid Xu                         (dst)->tv_nsec += 1000000000;           \
3559b0f1823SDavid Xu                 }                                               \
3569b0f1823SDavid Xu         } while (0)
3579b0f1823SDavid Xu 
3589b0f1823SDavid Xu 
3599b0f1823SDavid Xu static void
3609b0f1823SDavid Xu sem_cancel_handler(void *arg)
3619b0f1823SDavid Xu {
3629b0f1823SDavid Xu 	sem_t *sem = arg;
3639b0f1823SDavid Xu 
3649b0f1823SDavid Xu 	atomic_add_int(&(*sem)->nwaiters, -1);
3659b0f1823SDavid Xu 	if ((*sem)->nwaiters && (*sem)->count)
3669b0f1823SDavid Xu 		_umtx_wake(&(*sem)->count);
367bf4dc877SAlfred Perlstein }
368bf4dc877SAlfred Perlstein 
369bf4dc877SAlfred Perlstein int
3709b0f1823SDavid Xu _libc_sem_timedwait_compat(sem_t * __restrict sem,
3719b0f1823SDavid Xu 	const struct timespec * __restrict abstime)
372bf4dc877SAlfred Perlstein {
3739b0f1823SDavid Xu 	struct timespec ts, ts2;
3749b0f1823SDavid Xu 	int val, retval, saved_cancel;
375bf4dc877SAlfred Perlstein 
3765c70dac8SDaniel Eischen 	if (sem_check_validity(sem) != 0)
3775c70dac8SDaniel Eischen 		return (-1);
378bf4dc877SAlfred Perlstein 
3799b0f1823SDavid Xu 	if ((*sem)->syssem != 0) {
3809b0f1823SDavid Xu 		saved_cancel = enable_async_cancel();
3819b0f1823SDavid Xu 		retval = ksem_wait((*sem)->semid);
3829b0f1823SDavid Xu 		restore_async_cancel(saved_cancel);
3839b0f1823SDavid Xu 		return (retval);
3849b0f1823SDavid Xu 	}
3859b0f1823SDavid Xu 
386903f2e50SDaniel Eischen 	retval = 0;
3879b0f1823SDavid Xu 	_pthread_testcancel();
3889b0f1823SDavid Xu 	for (;;) {
3899b0f1823SDavid Xu 		while ((val = (*sem)->count) > 0) {
3909b0f1823SDavid Xu 			if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1))
3919b0f1823SDavid Xu 				return (0);
392903f2e50SDaniel Eischen 		}
3939b0f1823SDavid Xu 		if (retval)
3949b0f1823SDavid Xu 			break;
3959b0f1823SDavid Xu 		if (abstime) {
3969b0f1823SDavid Xu 			if (abstime->tv_nsec >= 1000000000 || abstime->tv_nsec < 0) {
3979b0f1823SDavid Xu 				errno = EINVAL;
3989b0f1823SDavid Xu 				return (-1);
3999b0f1823SDavid Xu 			}
4009b0f1823SDavid Xu 			clock_gettime(CLOCK_REALTIME, &ts);
4019b0f1823SDavid Xu 	                TIMESPEC_SUB(&ts2, abstime, &ts);
4029b0f1823SDavid Xu 		}
4039b0f1823SDavid Xu 		atomic_add_int(&(*sem)->nwaiters, 1);
4049b0f1823SDavid Xu 		pthread_cleanup_push(sem_cancel_handler, sem);
4059b0f1823SDavid Xu 		saved_cancel = enable_async_cancel();
4069b0f1823SDavid Xu 		retval = _umtx_wait_uint(&(*sem)->count, 0, abstime ? &ts2 : NULL);
4079b0f1823SDavid Xu 		restore_async_cancel(saved_cancel);
4089b0f1823SDavid Xu 		pthread_cleanup_pop(0);
4099b0f1823SDavid Xu 		atomic_add_int(&(*sem)->nwaiters, -1);
410903f2e50SDaniel Eischen 	}
411903f2e50SDaniel Eischen 	return (retval);
412bf4dc877SAlfred Perlstein }
413bf4dc877SAlfred Perlstein 
414bf4dc877SAlfred Perlstein int
4159b0f1823SDavid Xu _libc_sem_wait_compat(sem_t *sem)
416bf4dc877SAlfred Perlstein {
4179b0f1823SDavid Xu 	return _libc_sem_timedwait_compat(sem, NULL);
418bf4dc877SAlfred Perlstein }
419bf4dc877SAlfred Perlstein 
420bf4dc877SAlfred Perlstein int
4219b0f1823SDavid Xu _libc_sem_trywait_compat(sem_t *sem)
4229b0f1823SDavid Xu {
4239b0f1823SDavid Xu 	int val;
4249b0f1823SDavid Xu 
4259b0f1823SDavid Xu 	if (sem_check_validity(sem) != 0)
4269b0f1823SDavid Xu 		return (-1);
4279b0f1823SDavid Xu 
4289b0f1823SDavid Xu 	if ((*sem)->syssem != 0)
4299b0f1823SDavid Xu  		return ksem_trywait((*sem)->semid);
4309b0f1823SDavid Xu 
4319b0f1823SDavid Xu 	while ((val = (*sem)->count) > 0) {
4329b0f1823SDavid Xu 		if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1))
4339b0f1823SDavid Xu 			return (0);
4349b0f1823SDavid Xu 	}
4359b0f1823SDavid Xu 	errno = EAGAIN;
4369b0f1823SDavid Xu 	return (-1);
4379b0f1823SDavid Xu }
4389b0f1823SDavid Xu 
4399b0f1823SDavid Xu int
4409b0f1823SDavid Xu _libc_sem_post_compat(sem_t *sem)
4415c70dac8SDaniel Eischen {
4425c70dac8SDaniel Eischen 
4435c70dac8SDaniel Eischen 	if (sem_check_validity(sem) != 0)
4445c70dac8SDaniel Eischen 		return (-1);
4455c70dac8SDaniel Eischen 
4469b0f1823SDavid Xu 	if ((*sem)->syssem != 0)
4479b0f1823SDavid Xu 		return ksem_post((*sem)->semid);
4489b0f1823SDavid Xu 
4499b0f1823SDavid Xu 	atomic_add_rel_int(&(*sem)->count, 1);
4509b0f1823SDavid Xu 
4519b0f1823SDavid Xu 	if ((*sem)->nwaiters)
4529b0f1823SDavid Xu 		return _umtx_wake(&(*sem)->count);
4539b0f1823SDavid Xu 	return (0);
4545c70dac8SDaniel Eischen }
4555c70dac8SDaniel Eischen 
4565c70dac8SDaniel Eischen int
4579b0f1823SDavid Xu _libc_sem_getvalue_compat(sem_t * __restrict sem, int * __restrict sval)
458bf4dc877SAlfred Perlstein {
459bf4dc877SAlfred Perlstein 	int retval;
460bf4dc877SAlfred Perlstein 
4615c70dac8SDaniel Eischen 	if (sem_check_validity(sem) != 0)
4625c70dac8SDaniel Eischen 		return (-1);
463bf4dc877SAlfred Perlstein 
4645c70dac8SDaniel Eischen 	if ((*sem)->syssem != 0)
465bf4dc877SAlfred Perlstein 		retval = ksem_getvalue((*sem)->semid, sval);
4665c70dac8SDaniel Eischen 	else {
467bf4dc877SAlfred Perlstein 		*sval = (int)(*sem)->count;
468bf4dc877SAlfred Perlstein 		retval = 0;
4695c70dac8SDaniel Eischen 	}
4705c70dac8SDaniel Eischen 	return (retval);
471bf4dc877SAlfred Perlstein }
472