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