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