sem.c revision 1.8 1 /* $NetBSD: sem.c,v 1.8 2019/02/03 03:20:24 thorpej Exp $ */
2
3 /*-
4 * Copyright (c) 2003, 2019 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Jason R. Thorpe.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 /*
33 * Copyright (C) 2000 Jason Evans <jasone (at) freebsd.org>.
34 * All rights reserved.
35 *
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
38 * are met:
39 * 1. Redistributions of source code must retain the above copyright
40 * notice(s), this list of conditions and the following disclaimer as
41 * the first lines of this file unmodified other than the possible
42 * addition of one or more copyright notices.
43 * 2. Redistributions in binary form must reproduce the above copyright
44 * notice(s), this list of conditions and the following disclaimer in
45 * the documentation and/or other materials provided with the
46 * distribution.
47 *
48 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY
49 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
50 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
51 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE
52 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
53 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
54 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
55 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
56 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
57 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
58 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
59 */
60
61 #include <sys/cdefs.h>
62 __RCSID("$NetBSD: sem.c,v 1.8 2019/02/03 03:20:24 thorpej Exp $");
63
64 #ifndef __LIBPTHREAD_SOURCE__
65 /*
66 * There is no longer any difference between the libpthread and the librt
67 * versions of sem.c; both are fully kernel-assisted via the _ksem_*()
68 * system calls. The only difference is the need to lock some internal
69 * data structures in the pthread version, which could be achieved by
70 * different means. However, in order to maintain binary compatibility
71 * with applications that use POSIX semaphores and linked against only
72 * libpthread, we continue to maintain a copy of the implementation here
73 * that does not depend on any additional libraries (other than libc).
74 */
75 #define sem_init _librt_sem_init
76 #define sem_destroy _librt_sem_destroy
77 #define sem_open _librt_sem_open
78 #define sem_close _librt_sem_close
79 #define sem_unlink _librt_sem_unlink
80 #define sem_wait _librt_sem_wait
81 #define sem_timedwait _librt_sem_timedwait
82 #define sem_trywait _librt_sem_trywait
83 #define sem_post _librt_sem_post
84 #define sem_getvalue _librt_sem_getvalue
85 #endif /* ! __LIBPTHREAD_SOURCE__ */
86
87 #undef _LIBC
88 #define _LIBC
89
90 #include <sys/types.h>
91 #include <sys/ksem.h>
92 #include <sys/queue.h>
93 #include <stdlib.h>
94 #include <errno.h>
95 #include <fcntl.h>
96 #include <semaphore.h>
97 #include <stdarg.h>
98
99 #ifdef __LIBPTHREAD_SOURCE__
100 #include "pthread.h"
101 #endif /* __LIBPTHREAD_SOURCE__ */
102
103 #define SEM_NAMED 0x4e414d44U /* 'NAMD' */
104 #define SEM_MAGIC 0x90af0421U
105 #define SEM_MAGIC_NAMED (SEM_MAGIC ^ SEM_NAMED)
106
107 #define SEM_IS_KSEMID(k) ((((intptr_t)(k)) & KSEM_MARKER_MASK) \
108 == KSEM_PSHARED_MARKER)
109
110 #define SEM_IS_UNNAMED(k) (SEM_IS_KSEMID(k) || \
111 (k)->ksem_magic == SEM_MAGIC)
112
113 #define SEM_IS_NAMED(k) (!SEM_IS_UNNAMED(k))
114
115 #define SEM_MAGIC_OK(k) (SEM_IS_KSEMID(k) || \
116 (k)->ksem_magic == SEM_MAGIC || \
117 (k)->ksem_magic == SEM_MAGIC_NAMED)
118
119 struct _sem_st {
120 unsigned int ksem_magic;
121 intptr_t ksem_semid;
122
123 /* Used only to de-dup named semaphores. */
124 LIST_ENTRY(_sem_st) ksem_list;
125 sem_t *ksem_identity;
126 };
127
128 static LIST_HEAD(, _sem_st) named_sems = LIST_HEAD_INITIALIZER(&named_sems);
129 #ifdef __LIBPTHREAD_SOURCE__
130 static pthread_mutex_t named_sems_mtx = PTHREAD_MUTEX_INITIALIZER;
131
132 #define LOCK_NAMED_SEMS() pthread_mutex_lock(&named_sems_mtx)
133 #define UNLOCK_NAMED_SEMS() pthread_mutex_unlock(&named_sems_mtx)
134 #else /* ! __LIBPTHREAD_SOURCE__ */
135 #define LOCK_NAMED_SEMS() __nothing
136 #define UNLOCK_NAMED_SEMS() __nothing
137 #endif /* __LIBPTHREAD_SOURCE__ */
138
139 #ifndef __LIBPTHREAD_SOURCE__
140 #ifdef __weak_alias
141 __weak_alias(sem_init,_librt_sem_init)
142 __weak_alias(sem_destroy,_librt_sem_destroy)
143 __weak_alias(sem_open,_librt_sem_open)
144 __weak_alias(sem_close,_librt_sem_close)
145 __weak_alias(sem_unlink,_librt_sem_unlink)
146 __weak_alias(sem_wait,_librt_sem_wait)
147 __weak_alias(sem_timedwait,_librt_sem_timedwait)
148 __weak_alias(sem_trywait,_librt_sem_trywait)
149 __weak_alias(sem_post,_librt_sem_post)
150 __weak_alias(sem_getvalue,_librt_sem_getvalue)
151 #else
152 #error Weak aliases required to build POSIX semaphore support.
153 #endif /* __weak_alias */
154 #endif /* __LIBPTHREAD_SOURCE__ */
155
156 static inline intptr_t
157 sem_to_semid(sem_t *sem)
158 {
159
160 if (SEM_IS_KSEMID(*sem))
161 return (intptr_t)*sem;
162
163 return (*sem)->ksem_semid;
164 }
165
166 static void
167 sem_free(sem_t sem)
168 {
169
170 sem->ksem_magic = 0;
171 free(sem);
172 }
173
174 static int
175 sem_alloc(unsigned int value, intptr_t semid, unsigned int magic, sem_t *semp)
176 {
177 sem_t sem;
178
179 if (value > SEM_VALUE_MAX)
180 return (EINVAL);
181
182 if ((sem = malloc(sizeof(struct _sem_st))) == NULL)
183 return (ENOSPC);
184
185 sem->ksem_magic = magic;
186 sem->ksem_semid = semid;
187
188 *semp = sem;
189 return (0);
190 }
191
192 /* ARGSUSED */
193 int
194 sem_init(sem_t *sem, int pshared, unsigned int value)
195 {
196 intptr_t semid = pshared ? KSEM_PSHARED : 0;
197 int error;
198
199 if (_ksem_init(value, &semid) == -1)
200 return (-1);
201
202 /*
203 * pshared anonymous semaphores are treated a little differently.
204 * We don't allocate a sem structure and return a pointer to it.
205 * That pointer might live in the shared memory segment that's
206 * shared between processes, but the _sem_st that contains the
207 * important bits certainly would not be.
208 *
209 * So, instead, we return the ksem ID given to us by the kernel.
210 * The kernel has arranged for the least-significant bit of the
211 * ksem ID to always be 1 so as to ensure we can always tell
212 * these IDs apart from the pointers that we vend out for other
213 * non-pshared semaphores.
214 */
215 if (pshared) {
216 if ((semid & KSEM_MARKER_MASK) != KSEM_PSHARED_MARKER) {
217 _ksem_destroy(semid);
218 errno = ENOTSUP;
219 return (-1);
220 }
221 *sem = (sem_t)semid;
222 return (0);
223 }
224
225 if ((error = sem_alloc(value, semid, SEM_MAGIC, sem)) != 0) {
226 _ksem_destroy(semid);
227 errno = error;
228 return (-1);
229 }
230
231 return (0);
232 }
233
234 int
235 sem_destroy(sem_t *sem)
236 {
237 int error, save_errno;
238
239 #ifdef ERRORCHECK
240 if (sem == NULL || *sem == NULL || !SEM_MAGIC_OK(*sem)) {
241 errno = EINVAL;
242 return (-1);
243 }
244 #endif
245
246 if (SEM_IS_KSEMID(*sem)) {
247 error = _ksem_destroy((intptr_t)*sem);
248 } else {
249 if (SEM_IS_NAMED(*sem)) {
250 errno = EINVAL;
251 return (-1);
252 }
253
254 error = _ksem_destroy((*sem)->ksem_semid);
255 save_errno = errno;
256 sem_free(*sem);
257 errno = save_errno;
258 }
259
260 return error;
261 }
262
263 sem_t *
264 sem_open(const char *name, int oflag, ...)
265 {
266 sem_t *sem, s;
267 intptr_t semid;
268 mode_t mode;
269 unsigned int value;
270 int error;
271 va_list ap;
272
273 mode = 0;
274 value = 0;
275
276 if (oflag & O_CREAT) {
277 va_start(ap, oflag);
278 mode = va_arg(ap, int);
279 value = va_arg(ap, unsigned int);
280 va_end(ap);
281 }
282
283 /*
284 * We can be lazy and let the kernel handle the oflag,
285 * we'll just merge duplicate IDs into our list.
286 */
287 if (_ksem_open(name, oflag, mode, value, &semid) == -1)
288 return (SEM_FAILED);
289
290 /*
291 * Search for a duplicate ID, we must return the same sem_t *
292 * if we locate one.
293 */
294 LOCK_NAMED_SEMS();
295 LIST_FOREACH(s, &named_sems, ksem_list) {
296 if (s->ksem_semid == semid) {
297 UNLOCK_NAMED_SEMS();
298 return (s->ksem_identity);
299 }
300 }
301
302 if ((sem = malloc(sizeof(*sem))) == NULL) {
303 error = ENOSPC;
304 goto bad;
305 }
306 if ((error = sem_alloc(value, semid, SEM_MAGIC_NAMED, sem)) != 0)
307 goto bad;
308
309 LIST_INSERT_HEAD(&named_sems, *sem, ksem_list);
310 UNLOCK_NAMED_SEMS();
311 (*sem)->ksem_identity = sem;
312
313 return (sem);
314
315 bad:
316 UNLOCK_NAMED_SEMS();
317 _ksem_close(semid);
318 if (sem != NULL) {
319 if (*sem != NULL)
320 sem_free(*sem);
321 free(sem);
322 }
323 errno = error;
324 return (SEM_FAILED);
325 }
326
327 int
328 sem_close(sem_t *sem)
329 {
330 int error, save_errno;
331
332 #ifdef ERRORCHECK
333 if (sem == NULL || *sem == NULL || !SEM_MAGIC_OK(*sem)) {
334 errno = EINVAL;
335 return (-1);
336 }
337 #endif
338
339 if (!SEM_IS_NAMED(*sem)) {
340 errno = EINVAL;
341 return (-1);
342 }
343
344 LOCK_NAMED_SEMS();
345 error = _ksem_close((*sem)->ksem_semid);
346 LIST_REMOVE((*sem), ksem_list);
347 save_errno = errno;
348 UNLOCK_NAMED_SEMS();
349 sem_free(*sem);
350 free(sem);
351 errno = save_errno;
352 return error;
353 }
354
355 int
356 sem_unlink(const char *name)
357 {
358
359 return (_ksem_unlink(name));
360 }
361
362 int
363 sem_wait(sem_t *sem)
364 {
365
366 #ifdef ERRORCHECK
367 if (sem == NULL || *sem == NULL || !SEM_MAGIC_OK(*sem)) {
368 errno = EINVAL;
369 return (-1);
370 }
371 #endif
372
373 return (_ksem_wait(sem_to_semid(sem)));
374 }
375
376 int
377 sem_timedwait(sem_t *sem, const struct timespec * __restrict abstime)
378 {
379
380 #ifdef ERRORCHECK
381 if (sem == NULL || *sem == NULL || !SEM_MAGIC_OK(*sem)) {
382 errno = EINVAL;
383 return (-1);
384 }
385 #endif
386
387 return (_ksem_timedwait(sem_to_semid(sem), abstime));
388 }
389
390 int
391 sem_trywait(sem_t *sem)
392 {
393
394 #ifdef ERRORCHECK
395 if (sem == NULL || *sem == NULL || !SEM_MAGIC_OK(*sem)) {
396 errno = EINVAL;
397 return (-1);
398 }
399 #endif
400
401 return (_ksem_trywait(sem_to_semid(sem)));
402 }
403
404 int
405 sem_post(sem_t *sem)
406 {
407
408 #ifdef ERRORCHECK
409 if (sem == NULL || *sem == NULL || !SEM_MAGIC_OK(*sem)) {
410 errno = EINVAL;
411 return (-1);
412 }
413 #endif
414
415 return (_ksem_post(sem_to_semid(sem)));
416 }
417
418 int
419 sem_getvalue(sem_t * __restrict sem, int * __restrict sval)
420 {
421
422 #ifdef ERRORCHECK
423 if (sem == NULL || *sem == NULL || !SEM_MAGIC_OK(*sem)) {
424 errno = EINVAL;
425 return (-1);
426 }
427 #endif
428
429 return (_ksem_getvalue(sem_to_semid(sem), sval));
430 }
431