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