xref: /freebsd/lib/libc/gen/sem.c (revision 1e413cf93298b5b97441a21d9a50fdcd0ee9945e)
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 }
362