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