1 /* 2 * Copyright (C) 2005 David Xu <davidxu@freebsd.org>. 3 * Copyright (C) 2000 Jason Evans <jasone@freebsd.org>. 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice(s), this list of conditions and the following disclaimer as 11 * the first lines of this file unmodified other than the possible 12 * addition of one or more copyright notices. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice(s), this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY 19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 21 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE 22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 25 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 27 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 28 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 * 30 * $FreeBSD: src/lib/libpthread/thread/thr_sem.c,v 1.16 2004/12/18 18:07:37 deischen Exp $ 31 * $DragonFly: src/lib/libthread_xu/thread/thr_sem.c,v 1.3 2005/03/29 19:26:20 joerg Exp $ 32 */ 33 34 #include <sys/queue.h> 35 36 #include <machine/tls.h> 37 38 #include <errno.h> 39 #include <fcntl.h> 40 #include <pthread.h> 41 #include <semaphore.h> 42 #include <stdlib.h> 43 #include <time.h> 44 45 #include "thr_private.h" 46 47 __weak_reference(_sem_close, sem_close); 48 __weak_reference(_sem_destroy, sem_destroy); 49 __weak_reference(_sem_getvalue, sem_getvalue); 50 __weak_reference(_sem_init, sem_init); 51 __weak_reference(_sem_open, sem_open); 52 __weak_reference(_sem_trywait, sem_trywait); 53 __weak_reference(_sem_wait, sem_wait); 54 __weak_reference(_sem_timedwait, sem_timedwait); 55 __weak_reference(_sem_post, sem_post); 56 __weak_reference(_sem_unlink, sem_unlink); 57 58 /* 59 * Semaphore definitions. 60 */ 61 struct sem { 62 #define SEM_MAGIC ((u_int32_t) 0x09fa4012) 63 u_int32_t magic; 64 volatile umtx_t count; 65 int semid; /* kernel based semaphore id. */ 66 }; 67 68 static inline int 69 sem_check_validity(sem_t *sem) 70 { 71 72 if ((sem != NULL) && ((*sem)->magic == SEM_MAGIC)) 73 return (0); 74 else { 75 errno = EINVAL; 76 return (-1); 77 } 78 } 79 80 static sem_t 81 sem_alloc(unsigned int value, int semid) 82 { 83 sem_t sem; 84 85 if (value > SEM_VALUE_MAX) { 86 errno = EINVAL; 87 return (NULL); 88 } 89 90 sem = (sem_t)malloc(sizeof(struct sem)); 91 if (sem == NULL) { 92 errno = ENOSPC; 93 return (NULL); 94 } 95 sem->magic = SEM_MAGIC; 96 sem->count = (u_int32_t)value; 97 sem->semid = semid; 98 return (sem); 99 } 100 101 int 102 _sem_init(sem_t *sem, int pshared, unsigned int value) 103 { 104 if (pshared != 0) { 105 /* 106 * We really can support pshared, but sem_t was 107 * defined as a pointer, if it is a structure, 108 * it will work between processes. 109 */ 110 errno = EPERM; 111 return (-1); 112 } 113 114 (*sem) = sem_alloc(value, -1); 115 if ((*sem) == NULL) 116 return (-1); 117 return (0); 118 } 119 120 int 121 _sem_destroy(sem_t *sem) 122 { 123 if (sem_check_validity(sem) != 0) 124 return (-1); 125 126 free(*sem); 127 return (0); 128 } 129 130 sem_t * 131 _sem_open(const char *name, int oflag, ...) 132 { 133 errno = ENOSYS; 134 return SEM_FAILED; 135 } 136 137 int 138 _sem_close(sem_t *sem) 139 { 140 errno = ENOSYS; 141 return -1; 142 } 143 144 int 145 _sem_unlink(const char *name) 146 { 147 errno = ENOSYS; 148 return -1; 149 } 150 151 int 152 _sem_getvalue(sem_t * __restrict sem, int * __restrict sval) 153 { 154 if (sem_check_validity(sem) != 0) 155 return (-1); 156 157 *sval = (*sem)->count; 158 return (0); 159 } 160 161 int 162 _sem_trywait(sem_t *sem) 163 { 164 int val; 165 166 if (sem_check_validity(sem) != 0) 167 return (-1); 168 169 while ((val = (*sem)->count) > 0) { 170 if (atomic_cmpset_int(&(*sem)->count, val, val - 1)) 171 return (0); 172 } 173 errno = EAGAIN; 174 return (-1); 175 } 176 177 int 178 _sem_wait(sem_t *sem) 179 { 180 struct pthread *curthread; 181 int val, oldcancel, retval; 182 183 if (sem_check_validity(sem) != 0) 184 return (-1); 185 186 curthread = tls_get_curthread(); 187 _pthread_testcancel(); 188 do { 189 while ((val = (*sem)->count) > 0) { 190 if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1)) 191 return (0); 192 } 193 oldcancel = _thr_cancel_enter(curthread); 194 retval = _thr_umtx_wait(&(*sem)->count, 0, NULL, 0); 195 _thr_cancel_leave(curthread, oldcancel); 196 } while (retval == 0); 197 errno = retval; 198 return (-1); 199 } 200 201 int 202 _sem_timedwait(sem_t * __restrict sem, struct timespec * __restrict abstime) 203 { 204 struct timespec ts, ts2; 205 struct pthread *curthread; 206 int val, oldcancel, retval; 207 208 if (sem_check_validity(sem) != 0) 209 return (-1); 210 211 curthread = tls_get_curthread(); 212 213 /* 214 * The timeout argument is only supposed to 215 * be checked if the thread would have blocked. 216 */ 217 _pthread_testcancel(); 218 do { 219 while ((val = (*sem)->count) > 0) { 220 if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1)) 221 return (0); 222 } 223 if (abstime == NULL) { 224 errno = EINVAL; 225 return (-1); 226 } 227 clock_gettime(CLOCK_REALTIME, &ts); 228 TIMESPEC_SUB(&ts2, abstime, &ts); 229 oldcancel = _thr_cancel_enter(curthread); 230 retval = _thr_umtx_wait(&(*sem)->count, 0, &ts2, 231 CLOCK_REALTIME); 232 _thr_cancel_leave(curthread, oldcancel); 233 } while (retval == 0); 234 errno = retval; 235 return (-1); 236 } 237 238 int 239 _sem_post(sem_t *sem) 240 { 241 int val; 242 243 if (sem_check_validity(sem) != 0) 244 return (-1); 245 246 /* 247 * sem_post() is required to be safe to call from within 248 * signal handlers, these code should work as that. 249 */ 250 do { 251 val = (*sem)->count; 252 } while (!atomic_cmpset_acq_int(&(*sem)->count, val, val + 1)); 253 _thr_umtx_wake(&(*sem)->count, val + 1); 254 return (0); 255 } 256