xref: /freebsd/lib/libc/gen/sem.c (revision 5c70dac87936d2236aa28cc0705c66c85a339f89)
1bf4dc877SAlfred Perlstein /*
2bf4dc877SAlfred Perlstein  * Copyright (C) 2000 Jason Evans <jasone@freebsd.org>.
3bf4dc877SAlfred Perlstein  * All rights reserved.
4bf4dc877SAlfred Perlstein  *
5bf4dc877SAlfred Perlstein  * Redistribution and use in source and binary forms, with or without
6bf4dc877SAlfred Perlstein  * modification, are permitted provided that the following conditions
7bf4dc877SAlfred Perlstein  * are met:
8bf4dc877SAlfred Perlstein  * 1. Redistributions of source code must retain the above copyright
9bf4dc877SAlfred Perlstein  *    notice(s), this list of conditions and the following disclaimer as
10bf4dc877SAlfred Perlstein  *    the first lines of this file unmodified other than the possible
11bf4dc877SAlfred Perlstein  *    addition of one or more copyright notices.
12bf4dc877SAlfred Perlstein  * 2. Redistributions in binary form must reproduce the above copyright
13bf4dc877SAlfred Perlstein  *    notice(s), this list of conditions and the following disclaimer in
14bf4dc877SAlfred Perlstein  *    the documentation and/or other materials provided with the
15bf4dc877SAlfred Perlstein  *    distribution.
16bf4dc877SAlfred Perlstein  *
17bf4dc877SAlfred Perlstein  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY
18bf4dc877SAlfred Perlstein  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19bf4dc877SAlfred Perlstein  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20bf4dc877SAlfred Perlstein  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE
21bf4dc877SAlfred Perlstein  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22bf4dc877SAlfred Perlstein  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23bf4dc877SAlfred Perlstein  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
24bf4dc877SAlfred Perlstein  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25bf4dc877SAlfred Perlstein  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
26bf4dc877SAlfred Perlstein  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27bf4dc877SAlfred Perlstein  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28bf4dc877SAlfred Perlstein  *
29bf4dc877SAlfred Perlstein  * $FreeBSD$
30bf4dc877SAlfred Perlstein  */
31bf4dc877SAlfred Perlstein 
325c70dac8SDaniel Eischen /*
335c70dac8SDaniel Eischen  * Some notes about this implementation.
345c70dac8SDaniel Eischen  *
355c70dac8SDaniel Eischen  * This is mostly a simple implementation of POSIX semaphores that
365c70dac8SDaniel Eischen  * does not need threading.  Any semaphore created is a kernel-based
375c70dac8SDaniel Eischen  * semaphore regardless of the pshared attribute.  This is necessary
385c70dac8SDaniel Eischen  * because libc's stub for pthread_cond_wait() doesn't really wait,
395c70dac8SDaniel Eischen  * and it is not worth the effort impose this behavior on libc.
405c70dac8SDaniel Eischen  *
415c70dac8SDaniel Eischen  * All functions here are designed to be thread-safe so that a
425c70dac8SDaniel Eischen  * threads library need not provide wrappers except to make
435c70dac8SDaniel Eischen  * sem_wait() and sem_timedwait() cancellation points or to
445c70dac8SDaniel Eischen  * provide a faster userland implementation for non-pshared
455c70dac8SDaniel Eischen  * semaphores.
465c70dac8SDaniel Eischen  *
475c70dac8SDaniel Eischen  * Also, this implementation of semaphores cannot really support
485c70dac8SDaniel Eischen  * real pshared semaphores.  The sem_t is an allocated object
495c70dac8SDaniel Eischen  * and can't be seen by other processes when placed in shared
505c70dac8SDaniel Eischen  * memory.  It should work across forks as long as the semaphore
515c70dac8SDaniel Eischen  * is created before any forks.
525c70dac8SDaniel Eischen  *
535c70dac8SDaniel Eischen  * The function sem_init() should be overridden by a threads
545c70dac8SDaniel Eischen  * library if it wants to provide a different userland version
555c70dac8SDaniel Eischen  * of semaphores.  The functions sem_wait() and sem_timedwait()
565c70dac8SDaniel Eischen  * need to be wrapped to provide cancellation points.  The function
575c70dac8SDaniel Eischen  * sem_post() may need to be wrapped to be signal-safe.
585c70dac8SDaniel Eischen  */
59e0554a53SJacques Vidrine #include "namespace.h"
605c70dac8SDaniel Eischen #include <sys/queue.h>
61bf4dc877SAlfred Perlstein #include <errno.h>
62bf4dc877SAlfred Perlstein #include <fcntl.h>
635c70dac8SDaniel Eischen #include <pthread.h>
64bf4dc877SAlfred Perlstein #include <semaphore.h>
65bf4dc877SAlfred Perlstein #include <stdarg.h>
665c70dac8SDaniel Eischen #include <stdlib.h>
675c70dac8SDaniel Eischen #include <time.h>
68bf4dc877SAlfred Perlstein #include <_semaphore.h>
69e0554a53SJacques Vidrine #include "un-namespace.h"
705c70dac8SDaniel Eischen #include "libc_private.h"
71bf4dc877SAlfred Perlstein 
72bf4dc877SAlfred Perlstein static sem_t sem_alloc(unsigned int value, semid_t semid, int system_sem);
73bf4dc877SAlfred Perlstein static void  sem_free(sem_t sem);
74bf4dc877SAlfred Perlstein 
75bf4dc877SAlfred Perlstein static LIST_HEAD(, sem) named_sems = LIST_HEAD_INITIALIZER(&named_sems);
76bf4dc877SAlfred Perlstein static pthread_mutex_t named_sems_mtx = PTHREAD_MUTEX_INITIALIZER;
77bf4dc877SAlfred Perlstein 
785c70dac8SDaniel Eischen __weak_reference(__sem_init, sem_init);
795c70dac8SDaniel Eischen __weak_reference(__sem_destroy, sem_destroy);
805c70dac8SDaniel Eischen __weak_reference(__sem_open, sem_open);
815c70dac8SDaniel Eischen __weak_reference(__sem_close, sem_close);
825c70dac8SDaniel Eischen __weak_reference(__sem_unlink, sem_post);
835c70dac8SDaniel Eischen __weak_reference(__sem_wait, sem_wait);
845c70dac8SDaniel Eischen __weak_reference(__sem_trywait, sem_trywait);
855c70dac8SDaniel Eischen __weak_reference(__sem_timedwait, sem_timedwait);
865c70dac8SDaniel Eischen __weak_reference(__sem_post, sem_post);
875c70dac8SDaniel Eischen __weak_reference(__sem_getvalue, sem_getvalue);
885c70dac8SDaniel Eischen 
895c70dac8SDaniel Eischen 
905c70dac8SDaniel Eischen static inline int
915c70dac8SDaniel Eischen sem_check_validity(sem_t *sem)
925c70dac8SDaniel Eischen {
935c70dac8SDaniel Eischen 
945c70dac8SDaniel Eischen 	if ((sem != NULL) && ((*sem)->magic == SEM_MAGIC))
955c70dac8SDaniel Eischen 		return (0);
965c70dac8SDaniel Eischen 	else {
975c70dac8SDaniel Eischen 		errno = EINVAL;
985c70dac8SDaniel Eischen 		return (-1);
995c70dac8SDaniel Eischen 	}
1005c70dac8SDaniel Eischen }
1015c70dac8SDaniel Eischen 
102bf4dc877SAlfred Perlstein static void
103bf4dc877SAlfred Perlstein sem_free(sem_t sem)
104bf4dc877SAlfred Perlstein {
105bf4dc877SAlfred Perlstein 
106bf4dc877SAlfred Perlstein 	_pthread_mutex_destroy(&sem->lock);
107bf4dc877SAlfred Perlstein 	_pthread_cond_destroy(&sem->gtzero);
108bf4dc877SAlfred Perlstein 	sem->magic = 0;
109bf4dc877SAlfred Perlstein 	free(sem);
110bf4dc877SAlfred Perlstein }
111bf4dc877SAlfred Perlstein 
112bf4dc877SAlfred Perlstein static sem_t
113bf4dc877SAlfred Perlstein sem_alloc(unsigned int value, semid_t semid, int system_sem)
114bf4dc877SAlfred Perlstein {
115bf4dc877SAlfred Perlstein 	sem_t sem;
116bf4dc877SAlfred Perlstein 
117bf4dc877SAlfred Perlstein 	if (value > SEM_VALUE_MAX) {
118bf4dc877SAlfred Perlstein 		errno = EINVAL;
119bf4dc877SAlfred Perlstein 		return (NULL);
120bf4dc877SAlfred Perlstein 	}
121bf4dc877SAlfred Perlstein 
122bf4dc877SAlfred Perlstein 	sem = (sem_t)malloc(sizeof(struct sem));
123bf4dc877SAlfred Perlstein 	if (sem == NULL) {
124bf4dc877SAlfred Perlstein 		errno = ENOSPC;
125bf4dc877SAlfred Perlstein 		return (NULL);
126bf4dc877SAlfred Perlstein 	}
127bf4dc877SAlfred Perlstein 
128bf4dc877SAlfred Perlstein 	sem->count = (u_int32_t)value;
129bf4dc877SAlfred Perlstein 	sem->nwaiters = 0;
130bf4dc877SAlfred Perlstein 	sem->magic = SEM_MAGIC;
131bf4dc877SAlfred Perlstein 	sem->semid = semid;
132bf4dc877SAlfred Perlstein 	sem->syssem = system_sem;
1335c70dac8SDaniel Eischen 	sem->lock = PTHREAD_MUTEX_INITIALIZER;
1345c70dac8SDaniel Eischen 	sem->gtzero = PTHREAD_COND_INITIALIZER;
135bf4dc877SAlfred Perlstein 	return (sem);
136bf4dc877SAlfred Perlstein }
137bf4dc877SAlfred Perlstein 
138bf4dc877SAlfred Perlstein int
1395c70dac8SDaniel Eischen __sem_init(sem_t *sem, int pshared, unsigned int value)
140bf4dc877SAlfred Perlstein {
141bf4dc877SAlfred Perlstein 	semid_t semid;
142bf4dc877SAlfred Perlstein 
143bf4dc877SAlfred Perlstein 	/*
1445c70dac8SDaniel Eischen 	 * We always have to create the kernel semaphore if the
1455c70dac8SDaniel Eischen 	 * threads library isn't present since libc's version of
1465c70dac8SDaniel Eischen 	 * pthread_cond_wait() is just a stub that doesn't really
1475c70dac8SDaniel Eischen 	 * wait.
148bf4dc877SAlfred Perlstein 	 */
1495c70dac8SDaniel Eischen 	if (ksem_init(&semid, value) != 0)
1505c70dac8SDaniel Eischen 		return (-1);
151bf4dc877SAlfred Perlstein 
1525c70dac8SDaniel Eischen 	(*sem) = sem_alloc(value, semid, 1);
1535c70dac8SDaniel Eischen 	if ((*sem) == NULL) {
154bf4dc877SAlfred Perlstein 		ksem_destroy(semid);
1555c70dac8SDaniel Eischen 		return (-1);
1565c70dac8SDaniel Eischen 	}
1575c70dac8SDaniel Eischen 	return (0);
158bf4dc877SAlfred Perlstein }
159bf4dc877SAlfred Perlstein 
160bf4dc877SAlfred Perlstein int
1615c70dac8SDaniel Eischen __sem_destroy(sem_t *sem)
162bf4dc877SAlfred Perlstein {
163bf4dc877SAlfred Perlstein 	int retval;
164bf4dc877SAlfred Perlstein 
1655c70dac8SDaniel Eischen 	if (sem_check_validity(sem) == 0)
1665c70dac8SDaniel Eischen 		return (-1);
167bf4dc877SAlfred Perlstein 
168bf4dc877SAlfred Perlstein 	_pthread_mutex_lock(&(*sem)->lock);
169bf4dc877SAlfred Perlstein 	/*
170bf4dc877SAlfred Perlstein 	 * If this is a system semaphore let the kernel track it otherwise
171bf4dc877SAlfred Perlstein 	 * make sure there are no waiters.
172bf4dc877SAlfred Perlstein 	 */
1735c70dac8SDaniel Eischen 	if ((*sem)->syssem != 0)
174bf4dc877SAlfred Perlstein 		retval = ksem_destroy((*sem)->semid);
1755c70dac8SDaniel Eischen 	else if ((*sem)->nwaiters > 0) {
176bf4dc877SAlfred Perlstein 		errno = EBUSY;
177bf4dc877SAlfred Perlstein 		retval = -1;
1785c70dac8SDaniel Eischen 	}
1795c70dac8SDaniel Eischen 	else {
1805c70dac8SDaniel Eischen 		retval = 0;
1815c70dac8SDaniel Eischen 		(*sem)->magic = 0;
182bf4dc877SAlfred Perlstein 	}
183bf4dc877SAlfred Perlstein 	_pthread_mutex_unlock(&(*sem)->lock);
184bf4dc877SAlfred Perlstein 
1855c70dac8SDaniel Eischen 	if (retval == 0)
186bf4dc877SAlfred Perlstein 		sem_free(*sem);
1875c70dac8SDaniel Eischen 	return (retval);
188bf4dc877SAlfred Perlstein }
189bf4dc877SAlfred Perlstein 
190bf4dc877SAlfred Perlstein sem_t *
1915c70dac8SDaniel Eischen __sem_open(const char *name, int oflag, ...)
192bf4dc877SAlfred Perlstein {
193bf4dc877SAlfred Perlstein 	sem_t *sem;
194bf4dc877SAlfred Perlstein 	sem_t s;
195bf4dc877SAlfred Perlstein 	semid_t semid;
196bf4dc877SAlfred Perlstein 	mode_t mode;
197bf4dc877SAlfred Perlstein 	unsigned int value;
198bf4dc877SAlfred Perlstein 
199bf4dc877SAlfred Perlstein 	mode = 0;
200bf4dc877SAlfred Perlstein 	value = 0;
201bf4dc877SAlfred Perlstein 
202bf4dc877SAlfred Perlstein 	if ((oflag & O_CREAT) != 0) {
203bf4dc877SAlfred Perlstein 		va_list ap;
204bf4dc877SAlfred Perlstein 
205bf4dc877SAlfred Perlstein 		va_start(ap, oflag);
206bf4dc877SAlfred Perlstein 		mode = va_arg(ap, int);
207bf4dc877SAlfred Perlstein 		value = va_arg(ap, unsigned int);
208bf4dc877SAlfred Perlstein 		va_end(ap);
209bf4dc877SAlfred Perlstein 	}
210bf4dc877SAlfred Perlstein 	/*
211bf4dc877SAlfred Perlstein 	 * we can be lazy and let the kernel handle the "oflag",
212bf4dc877SAlfred Perlstein 	 * we'll just merge duplicate IDs into our list.
213bf4dc877SAlfred Perlstein 	 */
214bf4dc877SAlfred Perlstein 	if (ksem_open(&semid, name, oflag, mode, value) == -1)
215bf4dc877SAlfred Perlstein 		return (SEM_FAILED);
216bf4dc877SAlfred Perlstein 	/*
217bf4dc877SAlfred Perlstein 	 * search for a duplicate ID, we must return the same sem_t *
218bf4dc877SAlfred Perlstein 	 * if we locate one.
219bf4dc877SAlfred Perlstein 	 */
220bf4dc877SAlfred Perlstein 	_pthread_mutex_lock(&named_sems_mtx);
221bf4dc877SAlfred Perlstein 	LIST_FOREACH(s, &named_sems, entry) {
222bf4dc877SAlfred Perlstein 		if (s->semid == semid) {
2235c70dac8SDaniel Eischen 			sem = s->backpointer;
224bf4dc877SAlfred Perlstein 			_pthread_mutex_unlock(&named_sems_mtx);
2255c70dac8SDaniel Eischen 			return (sem);
226bf4dc877SAlfred Perlstein 		}
227bf4dc877SAlfred Perlstein 	}
228bf4dc877SAlfred Perlstein 	sem = (sem_t *)malloc(sizeof(*sem));
229bf4dc877SAlfred Perlstein 	if (sem == NULL)
230bf4dc877SAlfred Perlstein 		goto err;
231bf4dc877SAlfred Perlstein 	*sem = sem_alloc(value, semid, 1);
232bf4dc877SAlfred Perlstein 	if ((*sem) == NULL)
233bf4dc877SAlfred Perlstein 		goto err;
234a91b25dcSTim J. Robbins 	LIST_INSERT_HEAD(&named_sems, *sem, entry);
235bf4dc877SAlfred Perlstein 	(*sem)->backpointer = sem;
236a91b25dcSTim J. Robbins 	_pthread_mutex_unlock(&named_sems_mtx);
237bf4dc877SAlfred Perlstein 	return (sem);
238bf4dc877SAlfred Perlstein err:
239bf4dc877SAlfred Perlstein 	_pthread_mutex_unlock(&named_sems_mtx);
240bf4dc877SAlfred Perlstein 	ksem_close(semid);
241bf4dc877SAlfred Perlstein 	if (sem != NULL) {
242bf4dc877SAlfred Perlstein 		if (*sem != NULL)
243bf4dc877SAlfred Perlstein 			sem_free(*sem);
244bf4dc877SAlfred Perlstein 		else
245bf4dc877SAlfred Perlstein 			errno = ENOSPC;
246bf4dc877SAlfred Perlstein 		free(sem);
247bf4dc877SAlfred Perlstein 	} else {
248bf4dc877SAlfred Perlstein 		errno = ENOSPC;
249bf4dc877SAlfred Perlstein 	}
250bf4dc877SAlfred Perlstein 	return (SEM_FAILED);
251bf4dc877SAlfred Perlstein }
252bf4dc877SAlfred Perlstein 
253bf4dc877SAlfred Perlstein int
2545c70dac8SDaniel Eischen __sem_close(sem_t *sem)
255bf4dc877SAlfred Perlstein {
256bf4dc877SAlfred Perlstein 
2575c70dac8SDaniel Eischen 	if (sem_check_validity(sem) != 0)
2585c70dac8SDaniel Eischen 		return (-1);
2595c70dac8SDaniel Eischen 
260bf4dc877SAlfred Perlstein 	if ((*sem)->syssem == 0) {
261bf4dc877SAlfred Perlstein 		errno = EINVAL;
262bf4dc877SAlfred Perlstein 		return (-1);
263bf4dc877SAlfred Perlstein 	}
2645c70dac8SDaniel Eischen 
265bf4dc877SAlfred Perlstein 	_pthread_mutex_lock(&named_sems_mtx);
2665c70dac8SDaniel Eischen 	if (ksem_close((*sem)->semid) != 0) {
267bf4dc877SAlfred Perlstein 		_pthread_mutex_unlock(&named_sems_mtx);
268bf4dc877SAlfred Perlstein 		return (-1);
269bf4dc877SAlfred Perlstein 	}
270bf4dc877SAlfred Perlstein 	LIST_REMOVE((*sem), entry);
271bf4dc877SAlfred Perlstein 	_pthread_mutex_unlock(&named_sems_mtx);
272bf4dc877SAlfred Perlstein 	sem_free(*sem);
2735c70dac8SDaniel Eischen 	*sem = NULL;
274bf4dc877SAlfred Perlstein 	free(sem);
275bf4dc877SAlfred Perlstein 	return (0);
276bf4dc877SAlfred Perlstein }
277bf4dc877SAlfred Perlstein 
278bf4dc877SAlfred Perlstein int
2795c70dac8SDaniel Eischen __sem_unlink(const char *name)
280bf4dc877SAlfred Perlstein {
281bf4dc877SAlfred Perlstein 
282bf4dc877SAlfred Perlstein 	return (ksem_unlink(name));
283bf4dc877SAlfred Perlstein }
284bf4dc877SAlfred Perlstein 
285b6897522SDavid Xu int
2865c70dac8SDaniel Eischen __sem_wait(sem_t *sem)
287bf4dc877SAlfred Perlstein {
288bf4dc877SAlfred Perlstein 
2895c70dac8SDaniel Eischen 	if (sem_check_validity(sem) != 0)
2905c70dac8SDaniel Eischen 		return (-1);
291bf4dc877SAlfred Perlstein 
2925c70dac8SDaniel Eischen  	return (ksem_wait((*sem)->semid));
293bf4dc877SAlfred Perlstein }
294bf4dc877SAlfred Perlstein 
295bf4dc877SAlfred Perlstein int
2965c70dac8SDaniel Eischen __sem_trywait(sem_t *sem)
297bf4dc877SAlfred Perlstein {
298bf4dc877SAlfred Perlstein 
2995c70dac8SDaniel Eischen 	if (sem_check_validity(sem) != 0)
3005c70dac8SDaniel Eischen 		return (-1);
301bf4dc877SAlfred Perlstein 
3025c70dac8SDaniel Eischen 	return (ksem_trywait((*sem)->semid));
303bf4dc877SAlfred Perlstein }
304bf4dc877SAlfred Perlstein 
305bf4dc877SAlfred Perlstein int
3065c70dac8SDaniel Eischen __sem_timedwait(sem_t * __restrict sem,
3075c70dac8SDaniel Eischen     struct timespec * __restrict abs_timeout)
308bf4dc877SAlfred Perlstein {
3095c70dac8SDaniel Eischen 	if (sem_check_validity(sem) != 0)
3105c70dac8SDaniel Eischen 		return (-1);
311bf4dc877SAlfred Perlstein 
3125c70dac8SDaniel Eischen 	return (ksem_timedwait((*sem)->semid, abs_timeout));
313bf4dc877SAlfred Perlstein }
314bf4dc877SAlfred Perlstein 
315bf4dc877SAlfred Perlstein int
3165c70dac8SDaniel Eischen __sem_post(sem_t *sem)
3175c70dac8SDaniel Eischen {
3185c70dac8SDaniel Eischen 
3195c70dac8SDaniel Eischen 	if (sem_check_validity(sem) != 0)
3205c70dac8SDaniel Eischen 		return (-1);
3215c70dac8SDaniel Eischen 
3225c70dac8SDaniel Eischen 	return (ksem_post((*sem)->semid));
3235c70dac8SDaniel Eischen }
3245c70dac8SDaniel Eischen 
3255c70dac8SDaniel Eischen int
3265c70dac8SDaniel Eischen __sem_getvalue(sem_t * __restrict sem, int * __restrict sval)
327bf4dc877SAlfred Perlstein {
328bf4dc877SAlfred Perlstein 	int retval;
329bf4dc877SAlfred Perlstein 
3305c70dac8SDaniel Eischen 	if (sem_check_validity(sem) != 0)
3315c70dac8SDaniel Eischen 		return (-1);
332bf4dc877SAlfred Perlstein 
3335c70dac8SDaniel Eischen 	if ((*sem)->syssem != 0)
334bf4dc877SAlfred Perlstein 		retval = ksem_getvalue((*sem)->semid, sval);
3355c70dac8SDaniel Eischen 	else {
336bf4dc877SAlfred Perlstein 		_pthread_mutex_lock(&(*sem)->lock);
337bf4dc877SAlfred Perlstein 		*sval = (int)(*sem)->count;
338bf4dc877SAlfred Perlstein 		_pthread_mutex_unlock(&(*sem)->lock);
339bf4dc877SAlfred Perlstein 
340bf4dc877SAlfred Perlstein 		retval = 0;
3415c70dac8SDaniel Eischen 	}
3425c70dac8SDaniel Eischen 	return (retval);
343bf4dc877SAlfred Perlstein }
344