xref: /freebsd/lib/libc/gen/sem.c (revision 06064893b3c62c648518be78604fac29fc0d9d61)
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/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 
72 static sem_t sem_alloc(unsigned int value, semid_t semid, int system_sem);
73 static void  sem_free(sem_t sem);
74 
75 static LIST_HEAD(, sem) named_sems = LIST_HEAD_INITIALIZER(&named_sems);
76 static 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 
90 static inline int
91 sem_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 
102 static void
103 sem_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 
112 static sem_t
113 sem_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 
138 int
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 
160 int
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 		_pthread_mutex_destroy(&(*sem)->lock);
187 		_pthread_cond_destroy(&(*sem)->gtzero);
188 		sem_free(*sem);
189 	}
190 	return (retval);
191 }
192 
193 sem_t *
194 __sem_open(const char *name, int oflag, ...)
195 {
196 	sem_t *sem;
197 	sem_t s;
198 	semid_t semid;
199 	mode_t mode;
200 	unsigned int value;
201 
202 	mode = 0;
203 	value = 0;
204 
205 	if ((oflag & O_CREAT) != 0) {
206 		va_list ap;
207 
208 		va_start(ap, oflag);
209 		mode = va_arg(ap, int);
210 		value = va_arg(ap, unsigned int);
211 		va_end(ap);
212 	}
213 	/*
214 	 * we can be lazy and let the kernel handle the "oflag",
215 	 * we'll just merge duplicate IDs into our list.
216 	 */
217 	if (ksem_open(&semid, name, oflag, mode, value) == -1)
218 		return (SEM_FAILED);
219 	/*
220 	 * search for a duplicate ID, we must return the same sem_t *
221 	 * if we locate one.
222 	 */
223 	_pthread_mutex_lock(&named_sems_mtx);
224 	LIST_FOREACH(s, &named_sems, entry) {
225 		if (s->semid == semid) {
226 			sem = s->backpointer;
227 			_pthread_mutex_unlock(&named_sems_mtx);
228 			return (sem);
229 		}
230 	}
231 	sem = (sem_t *)malloc(sizeof(*sem));
232 	if (sem == NULL)
233 		goto err;
234 	*sem = sem_alloc(value, semid, 1);
235 	if ((*sem) == NULL)
236 		goto err;
237 	LIST_INSERT_HEAD(&named_sems, *sem, entry);
238 	(*sem)->backpointer = sem;
239 	_pthread_mutex_unlock(&named_sems_mtx);
240 	return (sem);
241 err:
242 	_pthread_mutex_unlock(&named_sems_mtx);
243 	ksem_close(semid);
244 	if (sem != NULL) {
245 		if (*sem != NULL)
246 			sem_free(*sem);
247 		else
248 			errno = ENOSPC;
249 		free(sem);
250 	} else {
251 		errno = ENOSPC;
252 	}
253 	return (SEM_FAILED);
254 }
255 
256 int
257 __sem_close(sem_t *sem)
258 {
259 
260 	if (sem_check_validity(sem) != 0)
261 		return (-1);
262 
263 	if ((*sem)->syssem == 0) {
264 		errno = EINVAL;
265 		return (-1);
266 	}
267 
268 	_pthread_mutex_lock(&named_sems_mtx);
269 	if (ksem_close((*sem)->semid) != 0) {
270 		_pthread_mutex_unlock(&named_sems_mtx);
271 		return (-1);
272 	}
273 	LIST_REMOVE((*sem), entry);
274 	_pthread_mutex_unlock(&named_sems_mtx);
275 	sem_free(*sem);
276 	*sem = NULL;
277 	free(sem);
278 	return (0);
279 }
280 
281 int
282 __sem_unlink(const char *name)
283 {
284 
285 	return (ksem_unlink(name));
286 }
287 
288 int
289 __sem_wait(sem_t *sem)
290 {
291 
292 	if (sem_check_validity(sem) != 0)
293 		return (-1);
294 
295 	return (ksem_wait((*sem)->semid));
296 }
297 
298 int
299 __sem_trywait(sem_t *sem)
300 {
301 	int retval;
302 
303 	if (sem_check_validity(sem) != 0)
304 		return (-1);
305 
306 	if ((*sem)->syssem != 0)
307  		retval = ksem_trywait((*sem)->semid);
308 	else {
309 		_pthread_mutex_lock(&(*sem)->lock);
310 		if ((*sem)->count > 0) {
311 			(*sem)->count--;
312 			retval = 0;
313 		} else {
314 			errno = EAGAIN;
315 			retval = -1;
316 		}
317 		_pthread_mutex_unlock(&(*sem)->lock);
318 	}
319 	return (retval);
320 }
321 
322 int
323 __sem_timedwait(sem_t * __restrict sem,
324     struct timespec * __restrict abs_timeout)
325 {
326 	if (sem_check_validity(sem) != 0)
327 		return (-1);
328 
329 	return (ksem_timedwait((*sem)->semid, abs_timeout));
330 }
331 
332 int
333 __sem_post(sem_t *sem)
334 {
335 
336 	if (sem_check_validity(sem) != 0)
337 		return (-1);
338 
339 	return (ksem_post((*sem)->semid));
340 }
341 
342 int
343 __sem_getvalue(sem_t * __restrict sem, int * __restrict sval)
344 {
345 	int retval;
346 
347 	if (sem_check_validity(sem) != 0)
348 		return (-1);
349 
350 	if ((*sem)->syssem != 0)
351 		retval = ksem_getvalue((*sem)->semid, sval);
352 	else {
353 		_pthread_mutex_lock(&(*sem)->lock);
354 		*sval = (int)(*sem)->count;
355 		_pthread_mutex_unlock(&(*sem)->lock);
356 
357 		retval = 0;
358 	}
359 	return (retval);
360 }
361