]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - lib/libc/gen/sem.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / lib / libc / gen / sem.c
1 /*
2  * Copyright (C) 2000 Jason Evans <jasone@freebsd.org>.
3  * All rights reserved.
4  * 
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
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
15  *    distribution.
16  * 
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.
28  *
29  * $FreeBSD$
30  */
31
32 /*
33  * Some notes about this implementation.
34  *
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.
40  *
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
45  * semaphores.
46  *
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.
52  *
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.
58  */
59 #include "namespace.h"
60 #include <sys/types.h>
61 #include <sys/queue.h>
62 #include <errno.h>
63 #include <fcntl.h>
64 #include <pthread.h>
65 #include <semaphore.h>
66 #include <stdarg.h>
67 #include <stdlib.h>
68 #include <time.h>
69 #include <_semaphore.h>
70 #include "un-namespace.h"
71 #include "libc_private.h"
72
73 static sem_t sem_alloc(unsigned int value, semid_t semid, int system_sem);
74 static void  sem_free(sem_t sem);
75
76 static LIST_HEAD(, sem) named_sems = LIST_HEAD_INITIALIZER(named_sems);
77 static pthread_mutex_t named_sems_mtx = PTHREAD_MUTEX_INITIALIZER;
78
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);
89
90
91 static inline int
92 sem_check_validity(sem_t *sem)
93 {
94
95         if ((sem != NULL) && ((*sem)->magic == SEM_MAGIC))
96                 return (0);
97         else {
98                 errno = EINVAL;
99                 return (-1);
100         }
101 }
102
103 static void
104 sem_free(sem_t sem)
105 {
106
107         _pthread_mutex_destroy(&sem->lock);
108         _pthread_cond_destroy(&sem->gtzero);
109         sem->magic = 0;
110         free(sem);
111 }
112
113 static sem_t
114 sem_alloc(unsigned int value, semid_t semid, int system_sem)
115 {
116         sem_t sem;
117
118         if (value > SEM_VALUE_MAX) {
119                 errno = EINVAL;
120                 return (NULL);
121         }
122
123         sem = (sem_t)malloc(sizeof(struct sem));
124         if (sem == NULL) {
125                 errno = ENOSPC;
126                 return (NULL);
127         }
128
129         sem->count = (u_int32_t)value;
130         sem->nwaiters = 0;
131         sem->magic = SEM_MAGIC;
132         sem->semid = semid;
133         sem->syssem = system_sem;
134         sem->lock = PTHREAD_MUTEX_INITIALIZER;
135         sem->gtzero = PTHREAD_COND_INITIALIZER;
136         return (sem);
137 }
138
139 int
140 __sem_init(sem_t *sem, int pshared, unsigned int value)
141 {
142         semid_t semid;
143
144         /*
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
148          * wait.
149          */
150         if (ksem_init(&semid, value) != 0)
151                 return (-1);
152
153         (*sem) = sem_alloc(value, semid, 1);
154         if ((*sem) == NULL) {
155                 ksem_destroy(semid);
156                 return (-1);
157         }
158         return (0);
159 }
160
161 int
162 __sem_destroy(sem_t *sem)
163 {
164         int retval;
165
166         if (sem_check_validity(sem) != 0)
167                 return (-1);
168
169         _pthread_mutex_lock(&(*sem)->lock);
170         /*
171          * If this is a system semaphore let the kernel track it otherwise
172          * make sure there are no waiters.
173          */
174         if ((*sem)->syssem != 0)
175                 retval = ksem_destroy((*sem)->semid);
176         else if ((*sem)->nwaiters > 0) {
177                 errno = EBUSY;
178                 retval = -1;
179         }
180         else {
181                 retval = 0;
182                 (*sem)->magic = 0;
183         }
184         _pthread_mutex_unlock(&(*sem)->lock);
185
186         if (retval == 0) {
187                 _pthread_mutex_destroy(&(*sem)->lock);
188                 _pthread_cond_destroy(&(*sem)->gtzero);
189                 sem_free(*sem);
190         }
191         return (retval);
192 }
193
194 sem_t *
195 __sem_open(const char *name, int oflag, ...)
196 {
197         sem_t *sem;
198         sem_t s;
199         semid_t semid;
200         mode_t mode;
201         unsigned int value;
202
203         mode = 0;
204         value = 0;
205
206         if ((oflag & O_CREAT) != 0) {
207                 va_list ap;
208
209                 va_start(ap, oflag);
210                 mode = va_arg(ap, int);
211                 value = va_arg(ap, unsigned int);
212                 va_end(ap);
213         }
214         /*
215          * we can be lazy and let the kernel handle the "oflag",
216          * we'll just merge duplicate IDs into our list.
217          */
218         if (ksem_open(&semid, name, oflag, mode, value) == -1)
219                 return (SEM_FAILED);
220         /*
221          * search for a duplicate ID, we must return the same sem_t *
222          * if we locate one.
223          */
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);
229                         return (sem);
230                 }
231         }
232         sem = (sem_t *)malloc(sizeof(*sem));
233         if (sem == NULL)
234                 goto err;
235         *sem = sem_alloc(value, semid, 1);
236         if ((*sem) == NULL)
237                 goto err;
238         LIST_INSERT_HEAD(&named_sems, *sem, entry);
239         (*sem)->backpointer = sem;
240         _pthread_mutex_unlock(&named_sems_mtx);
241         return (sem);
242 err:
243         _pthread_mutex_unlock(&named_sems_mtx);
244         ksem_close(semid);
245         if (sem != NULL) {
246                 if (*sem != NULL)
247                         sem_free(*sem);
248                 else
249                         errno = ENOSPC;
250                 free(sem);
251         } else {
252                 errno = ENOSPC;
253         }
254         return (SEM_FAILED);
255 }
256
257 int
258 __sem_close(sem_t *sem)
259 {
260
261         if (sem_check_validity(sem) != 0)
262                 return (-1);
263
264         if ((*sem)->syssem == 0) {
265                 errno = EINVAL;
266                 return (-1);
267         }
268
269         _pthread_mutex_lock(&named_sems_mtx);
270         if (ksem_close((*sem)->semid) != 0) {
271                 _pthread_mutex_unlock(&named_sems_mtx);
272                 return (-1);
273         }
274         LIST_REMOVE((*sem), entry);
275         _pthread_mutex_unlock(&named_sems_mtx);
276         sem_free(*sem);
277         *sem = NULL;
278         free(sem);
279         return (0);
280 }
281
282 int
283 __sem_unlink(const char *name)
284 {
285
286         return (ksem_unlink(name));
287 }
288
289 int
290 __sem_wait(sem_t *sem)
291 {
292
293         if (sem_check_validity(sem) != 0)
294                 return (-1);
295
296         return (ksem_wait((*sem)->semid));
297 }
298
299 int
300 __sem_trywait(sem_t *sem)
301 {
302         int retval;
303
304         if (sem_check_validity(sem) != 0)
305                 return (-1);
306
307         if ((*sem)->syssem != 0)
308                 retval = ksem_trywait((*sem)->semid);
309         else {
310                 _pthread_mutex_lock(&(*sem)->lock);
311                 if ((*sem)->count > 0) {
312                         (*sem)->count--;
313                         retval = 0;
314                 } else {
315                         errno = EAGAIN;
316                         retval = -1;
317                 }
318                 _pthread_mutex_unlock(&(*sem)->lock);
319         }
320         return (retval);
321 }
322
323 int
324 __sem_timedwait(sem_t * __restrict sem,
325     const struct timespec * __restrict abs_timeout)
326 {
327         if (sem_check_validity(sem) != 0)
328                 return (-1);
329
330         return (ksem_timedwait((*sem)->semid, abs_timeout));
331 }
332
333 int
334 __sem_post(sem_t *sem)
335 {
336
337         if (sem_check_validity(sem) != 0)
338                 return (-1);
339
340         return (ksem_post((*sem)->semid));
341 }
342
343 int
344 __sem_getvalue(sem_t * __restrict sem, int * __restrict sval)
345 {
346         int retval;
347
348         if (sem_check_validity(sem) != 0)
349                 return (-1);
350
351         if ((*sem)->syssem != 0)
352                 retval = ksem_getvalue((*sem)->semid, sval);
353         else {
354                 _pthread_mutex_lock(&(*sem)->lock);
355                 *sval = (int)(*sem)->count;
356                 _pthread_mutex_unlock(&(*sem)->lock);
357
358                 retval = 0;
359         }
360         return (retval);
361 }