sem.c revision 125509
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: head/lib/libc/gen/sem.c 125509 2004-02-05 23:32:45Z deischen $
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/queue.h>
61#include <errno.h>
62#include <fcntl.h>
63#include <pthread.h>
64#include <semaphore.h>
65#include <stdarg.h>
66#include <stdlib.h>
67#include <time.h>
68#include <_semaphore.h>
69#include "un-namespace.h"
70#include "libc_private.h"
71
72static sem_t sem_alloc(unsigned int value, semid_t semid, int system_sem);
73static void  sem_free(sem_t sem);
74
75static LIST_HEAD(, sem) named_sems = LIST_HEAD_INITIALIZER(&named_sems);
76static pthread_mutex_t named_sems_mtx = PTHREAD_MUTEX_INITIALIZER;
77
78__weak_reference(__sem_init, sem_init);
79__weak_reference(__sem_destroy, sem_destroy);
80__weak_reference(__sem_open, sem_open);
81__weak_reference(__sem_close, sem_close);
82__weak_reference(__sem_unlink, sem_unlink);
83__weak_reference(__sem_wait, sem_wait);
84__weak_reference(__sem_trywait, sem_trywait);
85__weak_reference(__sem_timedwait, sem_timedwait);
86__weak_reference(__sem_post, sem_post);
87__weak_reference(__sem_getvalue, sem_getvalue);
88
89
90static inline int
91sem_check_validity(sem_t *sem)
92{
93
94	if ((sem != NULL) && ((*sem)->magic == SEM_MAGIC))
95		return (0);
96	else {
97		errno = EINVAL;
98		return (-1);
99	}
100}
101
102static void
103sem_free(sem_t sem)
104{
105
106	_pthread_mutex_destroy(&sem->lock);
107	_pthread_cond_destroy(&sem->gtzero);
108	sem->magic = 0;
109	free(sem);
110}
111
112static sem_t
113sem_alloc(unsigned int value, semid_t semid, int system_sem)
114{
115	sem_t sem;
116
117	if (value > SEM_VALUE_MAX) {
118		errno = EINVAL;
119		return (NULL);
120	}
121
122	sem = (sem_t)malloc(sizeof(struct sem));
123	if (sem == NULL) {
124		errno = ENOSPC;
125		return (NULL);
126	}
127
128	sem->count = (u_int32_t)value;
129	sem->nwaiters = 0;
130	sem->magic = SEM_MAGIC;
131	sem->semid = semid;
132	sem->syssem = system_sem;
133	sem->lock = PTHREAD_MUTEX_INITIALIZER;
134	sem->gtzero = PTHREAD_COND_INITIALIZER;
135	return (sem);
136}
137
138int
139__sem_init(sem_t *sem, int pshared, unsigned int value)
140{
141	semid_t semid;
142
143	/*
144	 * We always have to create the kernel semaphore if the
145	 * threads library isn't present since libc's version of
146	 * pthread_cond_wait() is just a stub that doesn't really
147	 * wait.
148	 */
149	if (ksem_init(&semid, value) != 0)
150		return (-1);
151
152	(*sem) = sem_alloc(value, semid, 1);
153	if ((*sem) == NULL) {
154		ksem_destroy(semid);
155		return (-1);
156	}
157	return (0);
158}
159
160int
161__sem_destroy(sem_t *sem)
162{
163	int retval;
164
165	if (sem_check_validity(sem) != 0)
166		return (-1);
167
168	_pthread_mutex_lock(&(*sem)->lock);
169	/*
170	 * If this is a system semaphore let the kernel track it otherwise
171	 * make sure there are no waiters.
172	 */
173	if ((*sem)->syssem != 0)
174		retval = ksem_destroy((*sem)->semid);
175	else if ((*sem)->nwaiters > 0) {
176		errno = EBUSY;
177		retval = -1;
178	}
179	else {
180		retval = 0;
181		(*sem)->magic = 0;
182	}
183	_pthread_mutex_unlock(&(*sem)->lock);
184
185	if (retval == 0)
186		sem_free(*sem);
187	return (retval);
188}
189
190sem_t *
191__sem_open(const char *name, int oflag, ...)
192{
193	sem_t *sem;
194	sem_t s;
195	semid_t semid;
196	mode_t mode;
197	unsigned int value;
198
199	mode = 0;
200	value = 0;
201
202	if ((oflag & O_CREAT) != 0) {
203		va_list ap;
204
205		va_start(ap, oflag);
206		mode = va_arg(ap, int);
207		value = va_arg(ap, unsigned int);
208		va_end(ap);
209	}
210	/*
211	 * we can be lazy and let the kernel handle the "oflag",
212	 * we'll just merge duplicate IDs into our list.
213	 */
214	if (ksem_open(&semid, name, oflag, mode, value) == -1)
215		return (SEM_FAILED);
216	/*
217	 * search for a duplicate ID, we must return the same sem_t *
218	 * if we locate one.
219	 */
220	_pthread_mutex_lock(&named_sems_mtx);
221	LIST_FOREACH(s, &named_sems, entry) {
222		if (s->semid == semid) {
223			sem = s->backpointer;
224			_pthread_mutex_unlock(&named_sems_mtx);
225			return (sem);
226		}
227	}
228	sem = (sem_t *)malloc(sizeof(*sem));
229	if (sem == NULL)
230		goto err;
231	*sem = sem_alloc(value, semid, 1);
232	if ((*sem) == NULL)
233		goto err;
234	LIST_INSERT_HEAD(&named_sems, *sem, entry);
235	(*sem)->backpointer = sem;
236	_pthread_mutex_unlock(&named_sems_mtx);
237	return (sem);
238err:
239	_pthread_mutex_unlock(&named_sems_mtx);
240	ksem_close(semid);
241	if (sem != NULL) {
242		if (*sem != NULL)
243			sem_free(*sem);
244		else
245			errno = ENOSPC;
246		free(sem);
247	} else {
248		errno = ENOSPC;
249	}
250	return (SEM_FAILED);
251}
252
253int
254__sem_close(sem_t *sem)
255{
256
257	if (sem_check_validity(sem) != 0)
258		return (-1);
259
260	if ((*sem)->syssem == 0) {
261		errno = EINVAL;
262		return (-1);
263	}
264
265	_pthread_mutex_lock(&named_sems_mtx);
266	if (ksem_close((*sem)->semid) != 0) {
267		_pthread_mutex_unlock(&named_sems_mtx);
268		return (-1);
269	}
270	LIST_REMOVE((*sem), entry);
271	_pthread_mutex_unlock(&named_sems_mtx);
272	sem_free(*sem);
273	*sem = NULL;
274	free(sem);
275	return (0);
276}
277
278int
279__sem_unlink(const char *name)
280{
281
282	return (ksem_unlink(name));
283}
284
285int
286__sem_wait(sem_t *sem)
287{
288
289	if (sem_check_validity(sem) != 0)
290		return (-1);
291
292	return (ksem_wait((*sem)->semid));
293}
294
295int
296__sem_trywait(sem_t *sem)
297{
298	int retval;
299
300	if (sem_check_validity(sem) != 0)
301		return (-1);
302
303	if ((*sem)->syssem != 0)
304 		retval = ksem_trywait((*sem)->semid);
305	else {
306		_pthread_mutex_lock(&(*sem)->lock);
307		if ((*sem)->count != 0) {
308			(*sem)->count--;
309			retval = 0;
310		} else {
311			errno = EAGAIN;
312			retval = -1;
313		}
314		(*sem)->count--;
315		_pthread_mutex_unlock(&(*sem)->lock);
316	}
317	return (retval);
318}
319
320int
321__sem_timedwait(sem_t * __restrict sem,
322    struct timespec * __restrict abs_timeout)
323{
324	if (sem_check_validity(sem) != 0)
325		return (-1);
326
327	return (ksem_timedwait((*sem)->semid, abs_timeout));
328}
329
330int
331__sem_post(sem_t *sem)
332{
333
334	if (sem_check_validity(sem) != 0)
335		return (-1);
336
337	return (ksem_post((*sem)->semid));
338}
339
340int
341__sem_getvalue(sem_t * __restrict sem, int * __restrict sval)
342{
343	int retval;
344
345	if (sem_check_validity(sem) != 0)
346		return (-1);
347
348	if ((*sem)->syssem != 0)
349		retval = ksem_getvalue((*sem)->semid, sval);
350	else {
351		_pthread_mutex_lock(&(*sem)->lock);
352		*sval = (int)(*sem)->count;
353		_pthread_mutex_unlock(&(*sem)->lock);
354
355		retval = 0;
356	}
357	return (retval);
358}
359