xref: /freebsd/lib/libc/gen/sem.c (revision b3e7694832e81d7a904a10f525f8797b753bf0d3)
1d915a14eSPedro F. Giffuni /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3d915a14eSPedro F. Giffuni  *
49b0f1823SDavid Xu  * Copyright (C) 2010 David Xu <davidxu@freebsd.org>.
5bf4dc877SAlfred Perlstein  * Copyright (C) 2000 Jason Evans <jasone@freebsd.org>.
6bf4dc877SAlfred Perlstein  * All rights reserved.
7bf4dc877SAlfred Perlstein  *
8bf4dc877SAlfred Perlstein  * Redistribution and use in source and binary forms, with or without
9bf4dc877SAlfred Perlstein  * modification, are permitted provided that the following conditions
10bf4dc877SAlfred Perlstein  * are met:
11bf4dc877SAlfred Perlstein  * 1. Redistributions of source code must retain the above copyright
12bf4dc877SAlfred Perlstein  *    notice(s), this list of conditions and the following disclaimer as
13bf4dc877SAlfred Perlstein  *    the first lines of this file unmodified other than the possible
14bf4dc877SAlfred Perlstein  *    addition of one or more copyright notices.
15bf4dc877SAlfred Perlstein  * 2. Redistributions in binary form must reproduce the above copyright
16bf4dc877SAlfred Perlstein  *    notice(s), this list of conditions and the following disclaimer in
17bf4dc877SAlfred Perlstein  *    the documentation and/or other materials provided with the
18bf4dc877SAlfred Perlstein  *    distribution.
19bf4dc877SAlfred Perlstein  *
20bf4dc877SAlfred Perlstein  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY
21bf4dc877SAlfred Perlstein  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22bf4dc877SAlfred Perlstein  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23bf4dc877SAlfred Perlstein  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE
24bf4dc877SAlfred Perlstein  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25bf4dc877SAlfred Perlstein  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26bf4dc877SAlfred Perlstein  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
27bf4dc877SAlfred Perlstein  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28bf4dc877SAlfred Perlstein  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
29bf4dc877SAlfred Perlstein  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
30bf4dc877SAlfred Perlstein  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
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
sem_check_validity(sem_t * sem)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
sem_free(sem_t sem)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
sem_alloc(unsigned int value,semid_t semid,int system_sem)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
_libc_sem_init_compat(sem_t * sem,int pshared,unsigned int value)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
_libc_sem_destroy_compat(sem_t * sem)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 *
_libc_sem_open_compat(const char * name,int oflag,...)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
_libc_sem_close_compat(sem_t * sem)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
_libc_sem_unlink_compat(const char * name)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
_umtx_wait_uint(volatile unsigned * mtx,unsigned id,const struct timespec * abstime)315df1f1baeSDavid Xu _umtx_wait_uint(volatile unsigned *mtx, unsigned id, const struct timespec *abstime)
3169b0f1823SDavid Xu {
317df1f1baeSDavid Xu 	struct _umtx_time *tm_p, timeout;
318df1f1baeSDavid Xu 	size_t tm_size;
319df1f1baeSDavid Xu 
320df1f1baeSDavid Xu 	if (abstime == NULL) {
321df1f1baeSDavid Xu 		tm_p = NULL;
322df1f1baeSDavid Xu 		tm_size = 0;
323df1f1baeSDavid Xu 	} else {
324df1f1baeSDavid Xu 		timeout._clockid = CLOCK_REALTIME;
325df1f1baeSDavid Xu 		timeout._flags = UMTX_ABSTIME;
326df1f1baeSDavid Xu 		timeout._timeout = *abstime;
327df1f1baeSDavid Xu 		tm_p = &timeout;
328df1f1baeSDavid Xu 		tm_size = sizeof(timeout);
3299b0f1823SDavid Xu 	}
3309b0f1823SDavid Xu 	return _umtx_op(__DEVOLATILE(void *, mtx),
331df1f1baeSDavid Xu 		UMTX_OP_WAIT_UINT_PRIVATE, id,
332df1f1baeSDavid Xu 		(void *)tm_size, __DECONST(void*, tm_p));
3339b0f1823SDavid Xu }
334bf4dc877SAlfred Perlstein 
3359b0f1823SDavid Xu static int
_umtx_wake(volatile void * mtx)3369b0f1823SDavid Xu _umtx_wake(volatile void *mtx)
3379b0f1823SDavid Xu {
3389b0f1823SDavid Xu 	return _umtx_op(__DEVOLATILE(void *, mtx), UMTX_OP_WAKE_PRIVATE,
3399b0f1823SDavid Xu 			1, NULL, NULL);
3409b0f1823SDavid Xu }
3419b0f1823SDavid Xu 
3429b0f1823SDavid Xu #define TIMESPEC_SUB(dst, src, val)                             \
3439b0f1823SDavid Xu         do {                                                    \
3449b0f1823SDavid Xu                 (dst)->tv_sec = (src)->tv_sec - (val)->tv_sec;  \
3459b0f1823SDavid Xu                 (dst)->tv_nsec = (src)->tv_nsec - (val)->tv_nsec; \
3469b0f1823SDavid Xu                 if ((dst)->tv_nsec < 0) {                       \
3479b0f1823SDavid Xu                         (dst)->tv_sec--;                        \
3489b0f1823SDavid Xu                         (dst)->tv_nsec += 1000000000;           \
3499b0f1823SDavid Xu                 }                                               \
3509b0f1823SDavid Xu         } while (0)
3519b0f1823SDavid Xu 
3529b0f1823SDavid Xu 
3539b0f1823SDavid Xu static void
sem_cancel_handler(void * arg)3549b0f1823SDavid Xu sem_cancel_handler(void *arg)
3559b0f1823SDavid Xu {
3569b0f1823SDavid Xu 	sem_t *sem = arg;
3579b0f1823SDavid Xu 
3589b0f1823SDavid Xu 	atomic_add_int(&(*sem)->nwaiters, -1);
3599b0f1823SDavid Xu 	if ((*sem)->nwaiters && (*sem)->count)
3609b0f1823SDavid Xu 		_umtx_wake(&(*sem)->count);
361bf4dc877SAlfred Perlstein }
362bf4dc877SAlfred Perlstein 
363bf4dc877SAlfred Perlstein int
_libc_sem_timedwait_compat(sem_t * __restrict sem,const struct timespec * __restrict abstime)3649b0f1823SDavid Xu _libc_sem_timedwait_compat(sem_t * __restrict sem,
3659b0f1823SDavid Xu 	const struct timespec * __restrict abstime)
366bf4dc877SAlfred Perlstein {
367f4213b90SDavid Xu 	int val, retval;
368bf4dc877SAlfred Perlstein 
3695c70dac8SDaniel Eischen 	if (sem_check_validity(sem) != 0)
3705c70dac8SDaniel Eischen 		return (-1);
371bf4dc877SAlfred Perlstein 
3729b0f1823SDavid Xu 	if ((*sem)->syssem != 0) {
373f4213b90SDavid Xu 		_pthread_cancel_enter(1);
374f4213b90SDavid Xu 		retval = ksem_wait((*sem)->semid); /* XXX no timeout */
375f4213b90SDavid Xu 		_pthread_cancel_leave(retval == -1);
3769b0f1823SDavid Xu 		return (retval);
3779b0f1823SDavid Xu 	}
3789b0f1823SDavid Xu 
379903f2e50SDaniel Eischen 	retval = 0;
3809b0f1823SDavid Xu 	_pthread_testcancel();
3819b0f1823SDavid Xu 	for (;;) {
3829b0f1823SDavid Xu 		while ((val = (*sem)->count) > 0) {
3839b0f1823SDavid Xu 			if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1))
3849b0f1823SDavid Xu 				return (0);
385903f2e50SDaniel Eischen 		}
386f4213b90SDavid Xu 		if (retval) {
387f4213b90SDavid Xu 			_pthread_testcancel();
3889b0f1823SDavid Xu 			break;
389f4213b90SDavid Xu 		}
3909b0f1823SDavid Xu 		if (abstime) {
3919b0f1823SDavid Xu 			if (abstime->tv_nsec >= 1000000000 || abstime->tv_nsec < 0) {
3929b0f1823SDavid Xu 				errno = EINVAL;
3939b0f1823SDavid Xu 				return (-1);
3949b0f1823SDavid Xu 			}
3959b0f1823SDavid Xu 		}
3969b0f1823SDavid Xu 		atomic_add_int(&(*sem)->nwaiters, 1);
3979b0f1823SDavid Xu 		pthread_cleanup_push(sem_cancel_handler, sem);
398f4213b90SDavid Xu 		_pthread_cancel_enter(1);
399df1f1baeSDavid Xu 		retval = _umtx_wait_uint(&(*sem)->count, 0, abstime);
400f4213b90SDavid Xu 		_pthread_cancel_leave(0);
4019b0f1823SDavid Xu 		pthread_cleanup_pop(0);
4029b0f1823SDavid Xu 		atomic_add_int(&(*sem)->nwaiters, -1);
403903f2e50SDaniel Eischen 	}
404903f2e50SDaniel Eischen 	return (retval);
405bf4dc877SAlfred Perlstein }
406bf4dc877SAlfred Perlstein 
407bf4dc877SAlfred Perlstein int
_libc_sem_wait_compat(sem_t * sem)4089b0f1823SDavid Xu _libc_sem_wait_compat(sem_t *sem)
409bf4dc877SAlfred Perlstein {
4109b0f1823SDavid Xu 	return _libc_sem_timedwait_compat(sem, NULL);
411bf4dc877SAlfred Perlstein }
412bf4dc877SAlfred Perlstein 
413bf4dc877SAlfred Perlstein int
_libc_sem_trywait_compat(sem_t * sem)4149b0f1823SDavid Xu _libc_sem_trywait_compat(sem_t *sem)
4159b0f1823SDavid Xu {
4169b0f1823SDavid Xu 	int val;
4179b0f1823SDavid Xu 
4189b0f1823SDavid Xu 	if (sem_check_validity(sem) != 0)
4199b0f1823SDavid Xu 		return (-1);
4209b0f1823SDavid Xu 
4219b0f1823SDavid Xu 	if ((*sem)->syssem != 0)
4229b0f1823SDavid Xu  		return ksem_trywait((*sem)->semid);
4239b0f1823SDavid Xu 
4249b0f1823SDavid Xu 	while ((val = (*sem)->count) > 0) {
4259b0f1823SDavid Xu 		if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1))
4269b0f1823SDavid Xu 			return (0);
4279b0f1823SDavid Xu 	}
4289b0f1823SDavid Xu 	errno = EAGAIN;
4299b0f1823SDavid Xu 	return (-1);
4309b0f1823SDavid Xu }
4319b0f1823SDavid Xu 
4329b0f1823SDavid Xu int
_libc_sem_post_compat(sem_t * sem)4339b0f1823SDavid Xu _libc_sem_post_compat(sem_t *sem)
4345c70dac8SDaniel Eischen {
4355c70dac8SDaniel Eischen 
4365c70dac8SDaniel Eischen 	if (sem_check_validity(sem) != 0)
4375c70dac8SDaniel Eischen 		return (-1);
4385c70dac8SDaniel Eischen 
4399b0f1823SDavid Xu 	if ((*sem)->syssem != 0)
4409b0f1823SDavid Xu 		return ksem_post((*sem)->semid);
4419b0f1823SDavid Xu 
4429b0f1823SDavid Xu 	atomic_add_rel_int(&(*sem)->count, 1);
443d22d46ceSDavid Xu 	rmb();
4449b0f1823SDavid Xu 	if ((*sem)->nwaiters)
4459b0f1823SDavid Xu 		return _umtx_wake(&(*sem)->count);
4469b0f1823SDavid Xu 	return (0);
4475c70dac8SDaniel Eischen }
4485c70dac8SDaniel Eischen 
4495c70dac8SDaniel Eischen int
_libc_sem_getvalue_compat(sem_t * __restrict sem,int * __restrict sval)4509b0f1823SDavid Xu _libc_sem_getvalue_compat(sem_t * __restrict sem, int * __restrict sval)
451bf4dc877SAlfred Perlstein {
452bf4dc877SAlfred Perlstein 	int retval;
453bf4dc877SAlfred Perlstein 
4545c70dac8SDaniel Eischen 	if (sem_check_validity(sem) != 0)
4555c70dac8SDaniel Eischen 		return (-1);
456bf4dc877SAlfred Perlstein 
4575c70dac8SDaniel Eischen 	if ((*sem)->syssem != 0)
458bf4dc877SAlfred Perlstein 		retval = ksem_getvalue((*sem)->semid, sval);
4595c70dac8SDaniel Eischen 	else {
460bf4dc877SAlfred Perlstein 		*sval = (int)(*sem)->count;
461bf4dc877SAlfred Perlstein 		retval = 0;
4625c70dac8SDaniel Eischen 	}
4635c70dac8SDaniel Eischen 	return (retval);
464bf4dc877SAlfred Perlstein }
465