sem.c revision 125509
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: head/lib/libc/gen/sem.c 125509 2004-02-05 23:32:45Z deischen $ 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 72static sem_t sem_alloc(unsigned int value, semid_t semid, int system_sem); 73static void sem_free(sem_t sem); 74 75static LIST_HEAD(, sem) named_sems = LIST_HEAD_INITIALIZER(&named_sems); 76static 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 90static inline int 91sem_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 102static void 103sem_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 112static sem_t 113sem_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 138int 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 160int 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 sem_free(*sem); 187 return (retval); 188} 189 190sem_t * 191__sem_open(const char *name, int oflag, ...) 192{ 193 sem_t *sem; 194 sem_t s; 195 semid_t semid; 196 mode_t mode; 197 unsigned int value; 198 199 mode = 0; 200 value = 0; 201 202 if ((oflag & O_CREAT) != 0) { 203 va_list ap; 204 205 va_start(ap, oflag); 206 mode = va_arg(ap, int); 207 value = va_arg(ap, unsigned int); 208 va_end(ap); 209 } 210 /* 211 * we can be lazy and let the kernel handle the "oflag", 212 * we'll just merge duplicate IDs into our list. 213 */ 214 if (ksem_open(&semid, name, oflag, mode, value) == -1) 215 return (SEM_FAILED); 216 /* 217 * search for a duplicate ID, we must return the same sem_t * 218 * if we locate one. 219 */ 220 _pthread_mutex_lock(&named_sems_mtx); 221 LIST_FOREACH(s, &named_sems, entry) { 222 if (s->semid == semid) { 223 sem = s->backpointer; 224 _pthread_mutex_unlock(&named_sems_mtx); 225 return (sem); 226 } 227 } 228 sem = (sem_t *)malloc(sizeof(*sem)); 229 if (sem == NULL) 230 goto err; 231 *sem = sem_alloc(value, semid, 1); 232 if ((*sem) == NULL) 233 goto err; 234 LIST_INSERT_HEAD(&named_sems, *sem, entry); 235 (*sem)->backpointer = sem; 236 _pthread_mutex_unlock(&named_sems_mtx); 237 return (sem); 238err: 239 _pthread_mutex_unlock(&named_sems_mtx); 240 ksem_close(semid); 241 if (sem != NULL) { 242 if (*sem != NULL) 243 sem_free(*sem); 244 else 245 errno = ENOSPC; 246 free(sem); 247 } else { 248 errno = ENOSPC; 249 } 250 return (SEM_FAILED); 251} 252 253int 254__sem_close(sem_t *sem) 255{ 256 257 if (sem_check_validity(sem) != 0) 258 return (-1); 259 260 if ((*sem)->syssem == 0) { 261 errno = EINVAL; 262 return (-1); 263 } 264 265 _pthread_mutex_lock(&named_sems_mtx); 266 if (ksem_close((*sem)->semid) != 0) { 267 _pthread_mutex_unlock(&named_sems_mtx); 268 return (-1); 269 } 270 LIST_REMOVE((*sem), entry); 271 _pthread_mutex_unlock(&named_sems_mtx); 272 sem_free(*sem); 273 *sem = NULL; 274 free(sem); 275 return (0); 276} 277 278int 279__sem_unlink(const char *name) 280{ 281 282 return (ksem_unlink(name)); 283} 284 285int 286__sem_wait(sem_t *sem) 287{ 288 289 if (sem_check_validity(sem) != 0) 290 return (-1); 291 292 return (ksem_wait((*sem)->semid)); 293} 294 295int 296__sem_trywait(sem_t *sem) 297{ 298 int retval; 299 300 if (sem_check_validity(sem) != 0) 301 return (-1); 302 303 if ((*sem)->syssem != 0) 304 retval = ksem_trywait((*sem)->semid); 305 else { 306 _pthread_mutex_lock(&(*sem)->lock); 307 if ((*sem)->count != 0) { 308 (*sem)->count--; 309 retval = 0; 310 } else { 311 errno = EAGAIN; 312 retval = -1; 313 } 314 (*sem)->count--; 315 _pthread_mutex_unlock(&(*sem)->lock); 316 } 317 return (retval); 318} 319 320int 321__sem_timedwait(sem_t * __restrict sem, 322 struct timespec * __restrict abs_timeout) 323{ 324 if (sem_check_validity(sem) != 0) 325 return (-1); 326 327 return (ksem_timedwait((*sem)->semid, abs_timeout)); 328} 329 330int 331__sem_post(sem_t *sem) 332{ 333 334 if (sem_check_validity(sem) != 0) 335 return (-1); 336 337 return (ksem_post((*sem)->semid)); 338} 339 340int 341__sem_getvalue(sem_t * __restrict sem, int * __restrict sval) 342{ 343 int retval; 344 345 if (sem_check_validity(sem) != 0) 346 return (-1); 347 348 if ((*sem)->syssem != 0) 349 retval = ksem_getvalue((*sem)->semid, sval); 350 else { 351 _pthread_mutex_lock(&(*sem)->lock); 352 *sval = (int)(*sem)->count; 353 _pthread_mutex_unlock(&(*sem)->lock); 354 355 retval = 0; 356 } 357 return (retval); 358} 359