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 int 189 _sem_timedwait(sem_t * __restrict sem, const struct timespec * __restrict abstime) 190 { 191 struct timespec ts, ts2; 192 struct pthread *curthread; 193 int val, oldcancel, retval; 194 195 if (sem_check_validity(sem) != 0) 196 return (-1); 197 198 curthread = tls_get_curthread(); 199 200 /* 201 * The timeout argument is only supposed to 202 * be checked if the thread would have blocked. 203 */ 204 _pthread_testcancel(); 205 do { 206 while ((val = (*sem)->count) > 0) { 207 if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1)) 208 return (0); 209 } 210 if (abstime == NULL || 211 abstime->tv_nsec >= 1000000000 || abstime->tv_nsec < 0) { 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 226 int 227 _sem_post(sem_t *sem) 228 { 229 int val; 230 231 if (sem_check_validity(sem) != 0) 232 return (-1); 233 234 /* 235 * sem_post() is required to be safe to call from within 236 * signal handlers, these code should work as that. 237 */ 238 do { 239 val = (*sem)->count; 240 } while (!atomic_cmpset_acq_int(&(*sem)->count, val, val + 1)); 241 _thr_umtx_wake(&(*sem)->count, val + 1); 242 return (0); 243 } 244 245 sem_t * 246 _sem_open(__unused const char *name, __unused int oflag, ...) 247 { 248 errno = ENOSYS; 249 return (SEM_FAILED); 250 } 251 252 int 253 _sem_close(__unused sem_t *sem) 254 { 255 errno = ENOSYS; 256 return (-1); 257 } 258 259 int 260 _sem_unlink(__unused const char *name) 261 { 262 errno = ENOSYS; 263 return (-1); 264 } 265 266 __strong_reference(_sem_destroy, sem_destroy); 267 __strong_reference(_sem_getvalue, sem_getvalue); 268 __strong_reference(_sem_init, sem_init); 269 __strong_reference(_sem_trywait, sem_trywait); 270 __strong_reference(_sem_wait, sem_wait); 271 __strong_reference(_sem_timedwait, sem_timedwait); 272 __strong_reference(_sem_post, sem_post); 273 __strong_reference(_sem_open, sem_open); 274 __strong_reference(_sem_close, sem_close); 275 __strong_reference(_sem_unlink, sem_unlink); 276 277