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