xref: /freebsd/lib/libc/gen/sem.c (revision 4b2eaea43fec8e8792be611dea204071a10b655a)
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 	LIST_INSERT_HEAD(&named_sems, *sem, entry);
210 	(*sem)->backpointer = sem;
211 	_pthread_mutex_unlock(&named_sems_mtx);
212 	return (sem);
213 err:
214 	_pthread_mutex_unlock(&named_sems_mtx);
215 	ksem_close(semid);
216 	if (sem != NULL) {
217 		if (*sem != NULL)
218 			sem_free(*sem);
219 		else
220 			errno = ENOSPC;
221 		free(sem);
222 	} else {
223 		errno = ENOSPC;
224 	}
225 	return (SEM_FAILED);
226 }
227 
228 int
229 sem_close(sem_t *sem)
230 {
231 
232 	if ((*sem)->syssem == 0) {
233 		errno = EINVAL;
234 		return (-1);
235 	}
236 	_pthread_mutex_lock(&named_sems_mtx);
237 	if (ksem_close((*sem)->semid) == -1) {
238 		_pthread_mutex_unlock(&named_sems_mtx);
239 		return (-1);
240 	}
241 	LIST_REMOVE((*sem), entry);
242 	_pthread_mutex_unlock(&named_sems_mtx);
243 	sem_free(*sem);
244 	free(sem);
245 	return (0);
246 }
247 
248 int
249 sem_unlink(const char *name)
250 {
251 
252 	return (ksem_unlink(name));
253 }
254 
255 int
256 sem_wait(sem_t *sem)
257 {
258 	int	retval;
259 
260 	_SEM_CHECK_VALIDITY(sem);
261 
262 	if ((*sem)->syssem != 0) {
263 		retval = ksem_wait((*sem)->semid);
264 		goto RETURN;
265 	}
266 
267 	_pthread_mutex_lock(&(*sem)->lock);
268 
269 	while ((*sem)->count == 0) {
270 		(*sem)->nwaiters++;
271 		_pthread_cond_wait(&(*sem)->gtzero, &(*sem)->lock);
272 		(*sem)->nwaiters--;
273 	}
274 	(*sem)->count--;
275 
276 	_pthread_mutex_unlock(&(*sem)->lock);
277 
278 	retval = 0;
279   RETURN:
280 	return retval;
281 }
282 
283 int
284 sem_trywait(sem_t *sem)
285 {
286 	int	retval;
287 
288 	_SEM_CHECK_VALIDITY(sem);
289 
290 	if ((*sem)->syssem != 0) {
291 		retval = ksem_trywait((*sem)->semid);
292 		goto RETURN;
293 	}
294 
295 	_pthread_mutex_lock(&(*sem)->lock);
296 
297 	if ((*sem)->count > 0) {
298 		(*sem)->count--;
299 		retval = 0;
300 	} else {
301 		errno = EAGAIN;
302 		retval = -1;
303 	}
304 
305 	_pthread_mutex_unlock(&(*sem)->lock);
306 
307   RETURN:
308 	return retval;
309 }
310 
311 int
312 sem_post(sem_t *sem)
313 {
314 	int	retval;
315 
316 	_SEM_CHECK_VALIDITY(sem);
317 
318 	if ((*sem)->syssem != 0) {
319 		retval = ksem_post((*sem)->semid);
320 		goto RETURN;
321 	}
322 
323 	_pthread_mutex_lock(&(*sem)->lock);
324 
325 	(*sem)->count++;
326 	if ((*sem)->nwaiters > 0)
327 		_pthread_cond_signal(&(*sem)->gtzero);
328 
329 	_pthread_mutex_unlock(&(*sem)->lock);
330 
331 	retval = 0;
332   RETURN:
333 	return retval;
334 }
335 
336 int
337 sem_getvalue(sem_t * __restrict sem, int * __restrict sval)
338 {
339 	int	retval;
340 
341 	_SEM_CHECK_VALIDITY(sem);
342 
343 	if ((*sem)->syssem != 0) {
344 		retval = ksem_getvalue((*sem)->semid, sval);
345 		goto RETURN;
346 	}
347 
348 	_pthread_mutex_lock(&(*sem)->lock);
349 	*sval = (int)(*sem)->count;
350 	_pthread_mutex_unlock(&(*sem)->lock);
351 
352 	retval = 0;
353   RETURN:
354 	return retval;
355 }
356