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 31 #include "namespace.h" 32 #include <machine/tls.h> 33 #include <sys/semaphore.h> 34 #include <sys/mman.h> 35 36 #include <errno.h> 37 #include <fcntl.h> 38 #include <pthread.h> 39 #include <stdlib.h> 40 #include <time.h> 41 #include "un-namespace.h" 42 #include "thr_private.h" 43 44 /* 45 * Semaphore definitions. 46 */ 47 struct sem { 48 u_int32_t magic; 49 volatile umtx_t count; 50 int semid; 51 int unused; /* pad */ 52 }; 53 54 #define SEM_MAGIC ((u_int32_t) 0x09fa4012) 55 56 #define SEMID_LWP 0 57 #define SEMID_FORK 1 58 59 static inline int 60 sem_check_validity(sem_t *sem) 61 { 62 63 if ((sem != NULL) && (*sem != NULL) && ((*sem)->magic == SEM_MAGIC)) { 64 return (0); 65 } else { 66 errno = EINVAL; 67 return (-1); 68 } 69 } 70 71 static sem_t 72 sem_alloc(unsigned int value, int pshared) 73 { 74 sem_t sem; 75 int semid; 76 77 if (value > SEM_VALUE_MAX) { 78 errno = EINVAL; 79 return (NULL); 80 } 81 if (pshared) { 82 static __thread sem_t sem_base; 83 static __thread int sem_count; 84 85 if (sem_base == NULL) { 86 sem_base = mmap(NULL, getpagesize(), 87 PROT_READ | PROT_WRITE, 88 MAP_ANON | MAP_SHARED, 89 -1, 0); 90 sem_count = getpagesize() / sizeof(*sem); 91 } 92 sem = sem_base++; 93 if (--sem_count == 0) 94 sem_base = NULL; 95 semid = SEMID_FORK; 96 } else { 97 sem = malloc(sizeof(struct sem)); 98 semid = SEMID_LWP; 99 } 100 if (sem == NULL) { 101 errno = ENOSPC; 102 return (NULL); 103 } 104 sem->magic = SEM_MAGIC; 105 sem->count = (u_int32_t)value; 106 sem->semid = semid; 107 return (sem); 108 } 109 110 int 111 _sem_init(sem_t *sem, int pshared, unsigned int value) 112 { 113 (*sem) = sem_alloc(value, pshared); 114 if ((*sem) == NULL) 115 return (-1); 116 return (0); 117 } 118 119 int 120 _sem_destroy(sem_t *sem) 121 { 122 if (sem_check_validity(sem) != 0) 123 return (-1); 124 125 (*sem)->magic = 0; 126 127 switch ((*sem)->semid) { 128 case SEMID_LWP: 129 free(*sem); 130 break; 131 case SEMID_FORK: 132 /* memory is left intact */ 133 break; 134 } 135 return (0); 136 } 137 138 int 139 _sem_getvalue(sem_t * __restrict sem, int * __restrict sval) 140 { 141 if (sem_check_validity(sem) != 0) 142 return (-1); 143 144 *sval = (*sem)->count; 145 return (0); 146 } 147 148 int 149 _sem_trywait(sem_t *sem) 150 { 151 int val; 152 153 if (sem_check_validity(sem) != 0) 154 return (-1); 155 156 while ((val = (*sem)->count) > 0) { 157 if (atomic_cmpset_int(&(*sem)->count, val, val - 1)) 158 return (0); 159 } 160 errno = EAGAIN; 161 return (-1); 162 } 163 164 int 165 _sem_wait(sem_t *sem) 166 { 167 struct pthread *curthread; 168 int val, oldcancel, retval; 169 170 if (sem_check_validity(sem) != 0) 171 return (-1); 172 173 curthread = tls_get_curthread(); 174 _pthread_testcancel(); 175 do { 176 while ((val = (*sem)->count) > 0) { 177 if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1)) 178 return (0); 179 } 180 oldcancel = _thr_cancel_enter(curthread); 181 retval = _thr_umtx_wait(&(*sem)->count, 0, NULL, 0); 182 _thr_cancel_leave(curthread, oldcancel); 183 } while (retval == 0); 184 errno = retval; 185 return (-1); 186 } 187 188 #if 0 189 int 190 _sem_timedwait(sem_t * __restrict sem, struct timespec * __restrict abstime) 191 { 192 struct timespec ts, ts2; 193 struct pthread *curthread; 194 int val, oldcancel, retval; 195 196 if (sem_check_validity(sem) != 0) 197 return (-1); 198 199 curthread = tls_get_curthread(); 200 201 /* 202 * The timeout argument is only supposed to 203 * be checked if the thread would have blocked. 204 */ 205 _pthread_testcancel(); 206 do { 207 while ((val = (*sem)->count) > 0) { 208 if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1)) 209 return (0); 210 } 211 if (abstime == NULL) { 212 errno = EINVAL; 213 return (-1); 214 } 215 clock_gettime(CLOCK_REALTIME, &ts); 216 TIMESPEC_SUB(&ts2, abstime, &ts); 217 oldcancel = _thr_cancel_enter(curthread); 218 retval = _thr_umtx_wait(&(*sem)->count, 0, &ts2, 219 CLOCK_REALTIME); 220 _thr_cancel_leave(curthread, oldcancel); 221 } while (retval == 0); 222 errno = retval; 223 return (-1); 224 } 225 #endif 226 227 int 228 _sem_post(sem_t *sem) 229 { 230 int val; 231 232 if (sem_check_validity(sem) != 0) 233 return (-1); 234 235 /* 236 * sem_post() is required to be safe to call from within 237 * signal handlers, these code should work as that. 238 */ 239 do { 240 val = (*sem)->count; 241 } while (!atomic_cmpset_acq_int(&(*sem)->count, val, val + 1)); 242 _thr_umtx_wake(&(*sem)->count, val + 1); 243 return (0); 244 } 245 246 sem_t * 247 _sem_open(__unused const char *name, __unused int oflag, ...) 248 { 249 errno = ENOSYS; 250 return (SEM_FAILED); 251 } 252 253 int 254 _sem_close(__unused sem_t *sem) 255 { 256 errno = ENOSYS; 257 return (-1); 258 } 259 260 int 261 _sem_unlink(__unused const char *name) 262 { 263 errno = ENOSYS; 264 return (-1); 265 } 266 267 __strong_reference(_sem_destroy, sem_destroy); 268 __strong_reference(_sem_getvalue, sem_getvalue); 269 __strong_reference(_sem_init, sem_init); 270 __strong_reference(_sem_trywait, sem_trywait); 271 __strong_reference(_sem_wait, sem_wait); 272 #if 0 273 __strong_reference(_sem_timedwait, sem_timedwait); 274 #endif 275 __strong_reference(_sem_post, sem_post); 276 __strong_reference(_sem_open, sem_open); 277 __strong_reference(_sem_close, sem_close); 278 __strong_reference(_sem_unlink, sem_unlink); 279 280