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