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