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