1 /* $NetBSD: sem.c,v 1.9 2019/02/21 21:33:34 christos 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@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.9 2019/02/21 21:33:34 christos 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 <stdint.h> 97 #include <semaphore.h> 98 #include <stdarg.h> 99 100 #ifdef __LIBPTHREAD_SOURCE__ 101 #include "pthread.h" 102 #endif /* __LIBPTHREAD_SOURCE__ */ 103 104 #define SEM_NAMED 0x4e414d44U /* 'NAMD' */ 105 #define SEM_MAGIC 0x90af0421U 106 #define SEM_MAGIC_NAMED (SEM_MAGIC ^ SEM_NAMED) 107 108 #define SEMID_IS_KSEMID(id) (((uint32_t)(id) & KSEM_MARKER_MASK) \ 109 == KSEM_PSHARED_MARKER) 110 #define SEM_IS_KSEMID(k) SEMID_IS_KSEMID((intptr_t)(k)) 111 112 113 #define SEM_IS_UNNAMED(k) (SEM_IS_KSEMID(k) || \ 114 (k)->ksem_magic == SEM_MAGIC) 115 116 #define SEM_IS_NAMED(k) (!SEM_IS_UNNAMED(k)) 117 118 #define SEM_MAGIC_OK(k) (SEM_IS_KSEMID(k) || \ 119 (k)->ksem_magic == SEM_MAGIC || \ 120 (k)->ksem_magic == SEM_MAGIC_NAMED) 121 122 struct _sem_st { 123 unsigned int ksem_magic; 124 intptr_t ksem_semid; 125 126 /* Used only to de-dup named semaphores. */ 127 LIST_ENTRY(_sem_st) ksem_list; 128 sem_t *ksem_identity; 129 }; 130 131 static LIST_HEAD(, _sem_st) named_sems = LIST_HEAD_INITIALIZER(&named_sems); 132 #ifdef __LIBPTHREAD_SOURCE__ 133 static pthread_mutex_t named_sems_mtx = PTHREAD_MUTEX_INITIALIZER; 134 135 #define LOCK_NAMED_SEMS() pthread_mutex_lock(&named_sems_mtx) 136 #define UNLOCK_NAMED_SEMS() pthread_mutex_unlock(&named_sems_mtx) 137 #else /* ! __LIBPTHREAD_SOURCE__ */ 138 #define LOCK_NAMED_SEMS() __nothing 139 #define UNLOCK_NAMED_SEMS() __nothing 140 #endif /* __LIBPTHREAD_SOURCE__ */ 141 142 #ifndef __LIBPTHREAD_SOURCE__ 143 #ifdef __weak_alias 144 __weak_alias(sem_init,_librt_sem_init) 145 __weak_alias(sem_destroy,_librt_sem_destroy) 146 __weak_alias(sem_open,_librt_sem_open) 147 __weak_alias(sem_close,_librt_sem_close) 148 __weak_alias(sem_unlink,_librt_sem_unlink) 149 __weak_alias(sem_wait,_librt_sem_wait) 150 __weak_alias(sem_timedwait,_librt_sem_timedwait) 151 __weak_alias(sem_trywait,_librt_sem_trywait) 152 __weak_alias(sem_post,_librt_sem_post) 153 __weak_alias(sem_getvalue,_librt_sem_getvalue) 154 #else 155 #error Weak aliases required to build POSIX semaphore support. 156 #endif /* __weak_alias */ 157 #endif /* __LIBPTHREAD_SOURCE__ */ 158 159 static inline intptr_t 160 sem_to_semid(sem_t *sem) 161 { 162 163 if (SEM_IS_KSEMID(*sem)) 164 return (intptr_t)*sem; 165 166 return (*sem)->ksem_semid; 167 } 168 169 static void 170 sem_free(sem_t sem) 171 { 172 173 sem->ksem_magic = 0; 174 free(sem); 175 } 176 177 static int 178 sem_alloc(unsigned int value, intptr_t semid, unsigned int magic, sem_t *semp) 179 { 180 sem_t sem; 181 182 if (value > SEM_VALUE_MAX) 183 return EINVAL; 184 185 if ((sem = malloc(sizeof(struct _sem_st))) == NULL) 186 return ENOSPC; 187 188 sem->ksem_magic = magic; 189 sem->ksem_semid = semid; 190 191 *semp = sem; 192 return 0; 193 } 194 195 /* ARGSUSED */ 196 int 197 sem_init(sem_t *sem, int pshared, unsigned int value) 198 { 199 intptr_t semid = pshared ? KSEM_PSHARED : 0; 200 int error; 201 202 if (_ksem_init(value, &semid) == -1) 203 return -1; 204 205 /* 206 * pshared anonymous semaphores are treated a little differently. 207 * We don't allocate a sem structure and return a pointer to it. 208 * That pointer might live in the shared memory segment that's 209 * shared between processes, but the _sem_st that contains the 210 * important bits certainly would not be. 211 * 212 * So, instead, we return the ksem ID given to us by the kernel. 213 * The kernel has arranged for the least-significant bit of the 214 * ksem ID to always be 1 so as to ensure we can always tell 215 * these IDs apart from the pointers that we vend out for other 216 * non-pshared semaphores. 217 */ 218 if (pshared) { 219 if (!SEMID_IS_KSEMID(semid)) { 220 _ksem_destroy(semid); 221 errno = ENOTSUP; 222 return -1; 223 } 224 *sem = (sem_t)semid; 225 return 0; 226 } 227 228 if ((error = sem_alloc(value, semid, SEM_MAGIC, sem)) != 0) { 229 _ksem_destroy(semid); 230 errno = error; 231 return -1; 232 } 233 234 return 0; 235 } 236 237 int 238 sem_destroy(sem_t *sem) 239 { 240 int error, save_errno; 241 242 #ifdef ERRORCHECK 243 if (sem == NULL || *sem == NULL || !SEM_MAGIC_OK(*sem)) { 244 errno = EINVAL; 245 return -1; 246 } 247 #endif 248 249 if (SEM_IS_KSEMID(*sem)) { 250 error = _ksem_destroy((intptr_t)*sem); 251 } else { 252 if (SEM_IS_NAMED(*sem)) { 253 errno = EINVAL; 254 return -1; 255 } 256 257 error = _ksem_destroy((*sem)->ksem_semid); 258 save_errno = errno; 259 sem_free(*sem); 260 if (error == -1) 261 errno = save_errno; 262 } 263 264 return error; 265 } 266 267 sem_t * 268 sem_open(const char *name, int oflag, ...) 269 { 270 sem_t *sem, s; 271 intptr_t semid; 272 mode_t mode; 273 unsigned int value; 274 int error; 275 va_list ap; 276 277 mode = 0; 278 value = 0; 279 280 if (oflag & O_CREAT) { 281 va_start(ap, oflag); 282 mode = va_arg(ap, int); 283 value = va_arg(ap, unsigned int); 284 va_end(ap); 285 } 286 287 /* 288 * We can be lazy and let the kernel handle the oflag, 289 * we'll just merge duplicate IDs into our list. 290 */ 291 if (_ksem_open(name, oflag, mode, value, &semid) == -1) 292 return SEM_FAILED; 293 294 /* 295 * Search for a duplicate ID, we must return the same sem_t * 296 * if we locate one. 297 */ 298 LOCK_NAMED_SEMS(); 299 LIST_FOREACH(s, &named_sems, ksem_list) { 300 if (s->ksem_semid == semid) { 301 UNLOCK_NAMED_SEMS(); 302 return s->ksem_identity; 303 } 304 } 305 306 if ((sem = malloc(sizeof(*sem))) == NULL) { 307 error = ENOSPC; 308 goto bad; 309 } 310 if ((error = sem_alloc(value, semid, SEM_MAGIC_NAMED, sem)) != 0) 311 goto bad; 312 313 LIST_INSERT_HEAD(&named_sems, *sem, ksem_list); 314 UNLOCK_NAMED_SEMS(); 315 (*sem)->ksem_identity = sem; 316 317 return sem; 318 319 bad: 320 UNLOCK_NAMED_SEMS(); 321 _ksem_close(semid); 322 if (sem != NULL) { 323 if (*sem != NULL) 324 sem_free(*sem); 325 free(sem); 326 } 327 errno = error; 328 return SEM_FAILED; 329 } 330 331 int 332 sem_close(sem_t *sem) 333 { 334 int error, save_errno; 335 336 #ifdef ERRORCHECK 337 if (sem == NULL || *sem == NULL || !SEM_MAGIC_OK(*sem)) { 338 errno = EINVAL; 339 return -1; 340 } 341 #endif 342 343 if (!SEM_IS_NAMED(*sem)) { 344 errno = EINVAL; 345 return -1; 346 } 347 348 LOCK_NAMED_SEMS(); 349 error = _ksem_close((*sem)->ksem_semid); 350 LIST_REMOVE((*sem), ksem_list); 351 save_errno = errno; 352 UNLOCK_NAMED_SEMS(); 353 sem_free(*sem); 354 free(sem); 355 if (error == -1) 356 errno = save_errno; 357 return error; 358 } 359 360 int 361 sem_unlink(const char *name) 362 { 363 364 return _ksem_unlink(name); 365 } 366 367 int 368 sem_wait(sem_t *sem) 369 { 370 371 #ifdef ERRORCHECK 372 if (sem == NULL || *sem == NULL || !SEM_MAGIC_OK(*sem)) { 373 errno = EINVAL; 374 return -1; 375 } 376 #endif 377 378 return _ksem_wait(sem_to_semid(sem)); 379 } 380 381 int 382 sem_timedwait(sem_t *sem, const struct timespec * __restrict abstime) 383 { 384 385 #ifdef ERRORCHECK 386 if (sem == NULL || *sem == NULL || !SEM_MAGIC_OK(*sem)) { 387 errno = EINVAL; 388 return -1; 389 } 390 #endif 391 392 return _ksem_timedwait(sem_to_semid(sem), abstime); 393 } 394 395 int 396 sem_trywait(sem_t *sem) 397 { 398 399 #ifdef ERRORCHECK 400 if (sem == NULL || *sem == NULL || !SEM_MAGIC_OK(*sem)) { 401 errno = EINVAL; 402 return -1; 403 } 404 #endif 405 406 return _ksem_trywait(sem_to_semid(sem)); 407 } 408 409 int 410 sem_post(sem_t *sem) 411 { 412 413 #ifdef ERRORCHECK 414 if (sem == NULL || *sem == NULL || !SEM_MAGIC_OK(*sem)) { 415 errno = EINVAL; 416 return -1; 417 } 418 #endif 419 420 return _ksem_post(sem_to_semid(sem)); 421 } 422 423 int 424 sem_getvalue(sem_t * __restrict sem, int * __restrict sval) 425 { 426 427 #ifdef ERRORCHECK 428 if (sem == NULL || *sem == NULL || !SEM_MAGIC_OK(*sem)) { 429 errno = EINVAL; 430 return -1; 431 } 432 #endif 433 434 return _ksem_getvalue(sem_to_semid(sem), sval); 435 } 436