2 * Copyright (C) 2000 Jason Evans <jasone@freebsd.org>.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice(s), this list of conditions and the following disclaimer as
10 * the first lines of this file unmodified other than the possible
11 * addition of one or more copyright notices.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice(s), this list of conditions and the following disclaimer in
14 * the documentation and/or other materials provided with the
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
24 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
26 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 * Some notes about this implementation.
35 * This is mostly a simple implementation of POSIX semaphores that
36 * does not need threading. Any semaphore created is a kernel-based
37 * semaphore regardless of the pshared attribute. This is necessary
38 * because libc's stub for pthread_cond_wait() doesn't really wait,
39 * and it is not worth the effort impose this behavior on libc.
41 * All functions here are designed to be thread-safe so that a
42 * threads library need not provide wrappers except to make
43 * sem_wait() and sem_timedwait() cancellation points or to
44 * provide a faster userland implementation for non-pshared
47 * Also, this implementation of semaphores cannot really support
48 * real pshared semaphores. The sem_t is an allocated object
49 * and can't be seen by other processes when placed in shared
50 * memory. It should work across forks as long as the semaphore
51 * is created before any forks.
53 * The function sem_init() should be overridden by a threads
54 * library if it wants to provide a different userland version
55 * of semaphores. The functions sem_wait() and sem_timedwait()
56 * need to be wrapped to provide cancellation points. The function
57 * sem_post() may need to be wrapped to be signal-safe.
59 #include "namespace.h"
60 #include <sys/types.h>
61 #include <sys/queue.h>
65 #include <semaphore.h>
69 #include <_semaphore.h>
70 #include "un-namespace.h"
71 #include "libc_private.h"
73 static sem_t sem_alloc(unsigned int value, semid_t semid, int system_sem);
74 static void sem_free(sem_t sem);
76 static LIST_HEAD(, sem) named_sems = LIST_HEAD_INITIALIZER(&named_sems);
77 static pthread_mutex_t named_sems_mtx = PTHREAD_MUTEX_INITIALIZER;
79 __weak_reference(__sem_init, sem_init);
80 __weak_reference(__sem_destroy, sem_destroy);
81 __weak_reference(__sem_open, sem_open);
82 __weak_reference(__sem_close, sem_close);
83 __weak_reference(__sem_unlink, sem_unlink);
84 __weak_reference(__sem_wait, sem_wait);
85 __weak_reference(__sem_trywait, sem_trywait);
86 __weak_reference(__sem_timedwait, sem_timedwait);
87 __weak_reference(__sem_post, sem_post);
88 __weak_reference(__sem_getvalue, sem_getvalue);
92 sem_check_validity(sem_t *sem)
95 if ((sem != NULL) && ((*sem)->magic == SEM_MAGIC))
107 _pthread_mutex_destroy(&sem->lock);
108 _pthread_cond_destroy(&sem->gtzero);
114 sem_alloc(unsigned int value, semid_t semid, int system_sem)
118 if (value > SEM_VALUE_MAX) {
123 sem = (sem_t)malloc(sizeof(struct sem));
129 sem->count = (u_int32_t)value;
131 sem->magic = SEM_MAGIC;
133 sem->syssem = system_sem;
134 sem->lock = PTHREAD_MUTEX_INITIALIZER;
135 sem->gtzero = PTHREAD_COND_INITIALIZER;
140 __sem_init(sem_t *sem, int pshared, unsigned int value)
145 * We always have to create the kernel semaphore if the
146 * threads library isn't present since libc's version of
147 * pthread_cond_wait() is just a stub that doesn't really
150 if (ksem_init(&semid, value) != 0)
153 (*sem) = sem_alloc(value, semid, 1);
154 if ((*sem) == NULL) {
162 __sem_destroy(sem_t *sem)
166 if (sem_check_validity(sem) != 0)
169 _pthread_mutex_lock(&(*sem)->lock);
171 * If this is a system semaphore let the kernel track it otherwise
172 * make sure there are no waiters.
174 if ((*sem)->syssem != 0)
175 retval = ksem_destroy((*sem)->semid);
176 else if ((*sem)->nwaiters > 0) {
184 _pthread_mutex_unlock(&(*sem)->lock);
187 _pthread_mutex_destroy(&(*sem)->lock);
188 _pthread_cond_destroy(&(*sem)->gtzero);
195 __sem_open(const char *name, int oflag, ...)
206 if ((oflag & O_CREAT) != 0) {
210 mode = va_arg(ap, int);
211 value = va_arg(ap, unsigned int);
215 * we can be lazy and let the kernel handle the "oflag",
216 * we'll just merge duplicate IDs into our list.
218 if (ksem_open(&semid, name, oflag, mode, value) == -1)
221 * search for a duplicate ID, we must return the same sem_t *
224 _pthread_mutex_lock(&named_sems_mtx);
225 LIST_FOREACH(s, &named_sems, entry) {
226 if (s->semid == semid) {
227 sem = s->backpointer;
228 _pthread_mutex_unlock(&named_sems_mtx);
232 sem = (sem_t *)malloc(sizeof(*sem));
235 *sem = sem_alloc(value, semid, 1);
238 LIST_INSERT_HEAD(&named_sems, *sem, entry);
239 (*sem)->backpointer = sem;
240 _pthread_mutex_unlock(&named_sems_mtx);
243 _pthread_mutex_unlock(&named_sems_mtx);
258 __sem_close(sem_t *sem)
261 if (sem_check_validity(sem) != 0)
264 if ((*sem)->syssem == 0) {
269 _pthread_mutex_lock(&named_sems_mtx);
270 if (ksem_close((*sem)->semid) != 0) {
271 _pthread_mutex_unlock(&named_sems_mtx);
274 LIST_REMOVE((*sem), entry);
275 _pthread_mutex_unlock(&named_sems_mtx);
283 __sem_unlink(const char *name)
286 return (ksem_unlink(name));
290 __sem_wait(sem_t *sem)
293 if (sem_check_validity(sem) != 0)
296 return (ksem_wait((*sem)->semid));
300 __sem_trywait(sem_t *sem)
304 if (sem_check_validity(sem) != 0)
307 if ((*sem)->syssem != 0)
308 retval = ksem_trywait((*sem)->semid);
310 _pthread_mutex_lock(&(*sem)->lock);
311 if ((*sem)->count > 0) {
318 _pthread_mutex_unlock(&(*sem)->lock);
324 __sem_timedwait(sem_t * __restrict sem,
325 const struct timespec * __restrict abs_timeout)
327 if (sem_check_validity(sem) != 0)
330 return (ksem_timedwait((*sem)->semid, abs_timeout));
334 __sem_post(sem_t *sem)
337 if (sem_check_validity(sem) != 0)
340 return (ksem_post((*sem)->semid));
344 __sem_getvalue(sem_t * __restrict sem, int * __restrict sval)
348 if (sem_check_validity(sem) != 0)
351 if ((*sem)->syssem != 0)
352 retval = ksem_getvalue((*sem)->semid, sval);
354 _pthread_mutex_lock(&(*sem)->lock);
355 *sval = (int)(*sem)->count;
356 _pthread_mutex_unlock(&(*sem)->lock);