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 /* 33 * Some notes about this implementation. 34 * 35 * This is mostly a simple implementation of POSIX semaphores that 36 * does not need threading. Any semaphore created is a kernel-based 37 * semaphore regardless of the pshared attribute. This is necessary 38 * because libc's stub for pthread_cond_wait() doesn't really wait, 39 * and it is not worth the effort impose this behavior on libc. 40 * 41 * All functions here are designed to be thread-safe so that a 42 * threads library need not provide wrappers except to make 43 * sem_wait() and sem_timedwait() cancellation points or to 44 * provide a faster userland implementation for non-pshared 45 * semaphores. 46 * 47 * Also, this implementation of semaphores cannot really support 48 * real pshared semaphores. The sem_t is an allocated object 49 * and can't be seen by other processes when placed in shared 50 * memory. It should work across forks as long as the semaphore 51 * is created before any forks. 52 * 53 * The function sem_init() should be overridden by a threads 54 * library if it wants to provide a different userland version 55 * of semaphores. The functions sem_wait() and sem_timedwait() 56 * need to be wrapped to provide cancellation points. The function 57 * sem_post() may need to be wrapped to be signal-safe. 58 */ 59 #include "namespace.h" 60 #include <sys/queue.h> 61 #include <errno.h> 62 #include <fcntl.h> 63 #include <pthread.h> 64 #include <semaphore.h> 65 #include <stdarg.h> 66 #include <stdlib.h> 67 #include <time.h> 68 #include <_semaphore.h> 69 #include "un-namespace.h" 70 #include "libc_private.h" 71 72 static sem_t sem_alloc(unsigned int value, semid_t semid, int system_sem); 73 static void sem_free(sem_t sem); 74 75 static LIST_HEAD(, sem) named_sems = LIST_HEAD_INITIALIZER(&named_sems); 76 static pthread_mutex_t named_sems_mtx = PTHREAD_MUTEX_INITIALIZER; 77 78 __weak_reference(__sem_init, sem_init); 79 __weak_reference(__sem_destroy, sem_destroy); 80 __weak_reference(__sem_open, sem_open); 81 __weak_reference(__sem_close, sem_close); 82 __weak_reference(__sem_unlink, sem_unlink); 83 __weak_reference(__sem_wait, sem_wait); 84 __weak_reference(__sem_trywait, sem_trywait); 85 __weak_reference(__sem_timedwait, sem_timedwait); 86 __weak_reference(__sem_post, sem_post); 87 __weak_reference(__sem_getvalue, sem_getvalue); 88 89 90 static inline int 91 sem_check_validity(sem_t *sem) 92 { 93 94 if ((sem != NULL) && ((*sem)->magic == SEM_MAGIC)) 95 return (0); 96 else { 97 errno = EINVAL; 98 return (-1); 99 } 100 } 101 102 static void 103 sem_free(sem_t sem) 104 { 105 106 _pthread_mutex_destroy(&sem->lock); 107 _pthread_cond_destroy(&sem->gtzero); 108 sem->magic = 0; 109 free(sem); 110 } 111 112 static sem_t 113 sem_alloc(unsigned int value, semid_t semid, int system_sem) 114 { 115 sem_t sem; 116 117 if (value > SEM_VALUE_MAX) { 118 errno = EINVAL; 119 return (NULL); 120 } 121 122 sem = (sem_t)malloc(sizeof(struct sem)); 123 if (sem == NULL) { 124 errno = ENOSPC; 125 return (NULL); 126 } 127 128 sem->count = (u_int32_t)value; 129 sem->nwaiters = 0; 130 sem->magic = SEM_MAGIC; 131 sem->semid = semid; 132 sem->syssem = system_sem; 133 sem->lock = PTHREAD_MUTEX_INITIALIZER; 134 sem->gtzero = PTHREAD_COND_INITIALIZER; 135 return (sem); 136 } 137 138 int 139 __sem_init(sem_t *sem, int pshared, unsigned int value) 140 { 141 semid_t semid; 142 143 /* 144 * We always have to create the kernel semaphore if the 145 * threads library isn't present since libc's version of 146 * pthread_cond_wait() is just a stub that doesn't really 147 * wait. 148 */ 149 if (ksem_init(&semid, value) != 0) 150 return (-1); 151 152 (*sem) = sem_alloc(value, semid, 1); 153 if ((*sem) == NULL) { 154 ksem_destroy(semid); 155 return (-1); 156 } 157 return (0); 158 } 159 160 int 161 __sem_destroy(sem_t *sem) 162 { 163 int retval; 164 165 if (sem_check_validity(sem) != 0) 166 return (-1); 167 168 _pthread_mutex_lock(&(*sem)->lock); 169 /* 170 * If this is a system semaphore let the kernel track it otherwise 171 * make sure there are no waiters. 172 */ 173 if ((*sem)->syssem != 0) 174 retval = ksem_destroy((*sem)->semid); 175 else if ((*sem)->nwaiters > 0) { 176 errno = EBUSY; 177 retval = -1; 178 } 179 else { 180 retval = 0; 181 (*sem)->magic = 0; 182 } 183 _pthread_mutex_unlock(&(*sem)->lock); 184 185 if (retval == 0) { 186 _pthread_mutex_destroy(&(*sem)->lock); 187 _pthread_cond_destroy(&(*sem)->gtzero); 188 sem_free(*sem); 189 } 190 return (retval); 191 } 192 193 sem_t * 194 __sem_open(const char *name, int oflag, ...) 195 { 196 sem_t *sem; 197 sem_t s; 198 semid_t semid; 199 mode_t mode; 200 unsigned int value; 201 202 mode = 0; 203 value = 0; 204 205 if ((oflag & O_CREAT) != 0) { 206 va_list ap; 207 208 va_start(ap, oflag); 209 mode = va_arg(ap, int); 210 value = va_arg(ap, unsigned int); 211 va_end(ap); 212 } 213 /* 214 * we can be lazy and let the kernel handle the "oflag", 215 * we'll just merge duplicate IDs into our list. 216 */ 217 if (ksem_open(&semid, name, oflag, mode, value) == -1) 218 return (SEM_FAILED); 219 /* 220 * search for a duplicate ID, we must return the same sem_t * 221 * if we locate one. 222 */ 223 _pthread_mutex_lock(&named_sems_mtx); 224 LIST_FOREACH(s, &named_sems, entry) { 225 if (s->semid == semid) { 226 sem = s->backpointer; 227 _pthread_mutex_unlock(&named_sems_mtx); 228 return (sem); 229 } 230 } 231 sem = (sem_t *)malloc(sizeof(*sem)); 232 if (sem == NULL) 233 goto err; 234 *sem = sem_alloc(value, semid, 1); 235 if ((*sem) == NULL) 236 goto err; 237 LIST_INSERT_HEAD(&named_sems, *sem, entry); 238 (*sem)->backpointer = sem; 239 _pthread_mutex_unlock(&named_sems_mtx); 240 return (sem); 241 err: 242 _pthread_mutex_unlock(&named_sems_mtx); 243 ksem_close(semid); 244 if (sem != NULL) { 245 if (*sem != NULL) 246 sem_free(*sem); 247 else 248 errno = ENOSPC; 249 free(sem); 250 } else { 251 errno = ENOSPC; 252 } 253 return (SEM_FAILED); 254 } 255 256 int 257 __sem_close(sem_t *sem) 258 { 259 260 if (sem_check_validity(sem) != 0) 261 return (-1); 262 263 if ((*sem)->syssem == 0) { 264 errno = EINVAL; 265 return (-1); 266 } 267 268 _pthread_mutex_lock(&named_sems_mtx); 269 if (ksem_close((*sem)->semid) != 0) { 270 _pthread_mutex_unlock(&named_sems_mtx); 271 return (-1); 272 } 273 LIST_REMOVE((*sem), entry); 274 _pthread_mutex_unlock(&named_sems_mtx); 275 sem_free(*sem); 276 *sem = NULL; 277 free(sem); 278 return (0); 279 } 280 281 int 282 __sem_unlink(const char *name) 283 { 284 285 return (ksem_unlink(name)); 286 } 287 288 int 289 __sem_wait(sem_t *sem) 290 { 291 292 if (sem_check_validity(sem) != 0) 293 return (-1); 294 295 return (ksem_wait((*sem)->semid)); 296 } 297 298 int 299 __sem_trywait(sem_t *sem) 300 { 301 int retval; 302 303 if (sem_check_validity(sem) != 0) 304 return (-1); 305 306 if ((*sem)->syssem != 0) 307 retval = ksem_trywait((*sem)->semid); 308 else { 309 _pthread_mutex_lock(&(*sem)->lock); 310 if ((*sem)->count > 0) { 311 (*sem)->count--; 312 retval = 0; 313 } else { 314 errno = EAGAIN; 315 retval = -1; 316 } 317 _pthread_mutex_unlock(&(*sem)->lock); 318 } 319 return (retval); 320 } 321 322 int 323 __sem_timedwait(sem_t * __restrict sem, 324 struct timespec * __restrict abs_timeout) 325 { 326 if (sem_check_validity(sem) != 0) 327 return (-1); 328 329 return (ksem_timedwait((*sem)->semid, abs_timeout)); 330 } 331 332 int 333 __sem_post(sem_t *sem) 334 { 335 336 if (sem_check_validity(sem) != 0) 337 return (-1); 338 339 return (ksem_post((*sem)->semid)); 340 } 341 342 int 343 __sem_getvalue(sem_t * __restrict sem, int * __restrict sval) 344 { 345 int retval; 346 347 if (sem_check_validity(sem) != 0) 348 return (-1); 349 350 if ((*sem)->syssem != 0) 351 retval = ksem_getvalue((*sem)->semid, sval); 352 else { 353 _pthread_mutex_lock(&(*sem)->lock); 354 *sval = (int)(*sem)->count; 355 _pthread_mutex_unlock(&(*sem)->lock); 356 357 retval = 0; 358 } 359 return (retval); 360 } 361