thr_sem.c revision 144518
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: head/lib/libthr/thread/thr_sem.c 144518 2005-04-02 01:20:00Z davidxu $ 31 */ 32 33#include "namespace.h" 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#include <_semaphore.h> 42#include "un-namespace.h" 43 44#include "thr_private.h" 45 46 47__weak_reference(_sem_init, sem_init); 48__weak_reference(_sem_destroy, sem_destroy); 49__weak_reference(_sem_getvalue, sem_getvalue); 50__weak_reference(_sem_trywait, sem_trywait); 51__weak_reference(_sem_wait, sem_wait); 52__weak_reference(_sem_timedwait, sem_timedwait); 53__weak_reference(_sem_post, sem_post); 54 55 56static inline int 57sem_check_validity(sem_t *sem) 58{ 59 60 if ((sem != NULL) && ((*sem)->magic == SEM_MAGIC)) 61 return (0); 62 else { 63 errno = EINVAL; 64 return (-1); 65 } 66} 67 68static sem_t 69sem_alloc(unsigned int value, semid_t semid, int system_sem) 70{ 71 sem_t sem; 72 73 if (value > SEM_VALUE_MAX) { 74 errno = EINVAL; 75 return (NULL); 76 } 77 78 sem = (sem_t)malloc(sizeof(struct sem)); 79 if (sem == NULL) { 80 errno = ENOSPC; 81 return (NULL); 82 } 83 _thr_umtx_init((umtx_t *)&sem->lock); 84 /* 85 * Fortunatly count and nwaiters are adjacency, so we can 86 * use umtx_wait to wait on it, umtx_wait needs an address 87 * can be accessed as a long interger. 88 */ 89 sem->count = (u_int32_t)value; 90 sem->nwaiters = 0; 91 sem->magic = SEM_MAGIC; 92 sem->semid = semid; 93 sem->syssem = system_sem; 94 return (sem); 95} 96 97int 98_sem_init(sem_t *sem, int pshared, unsigned int value) 99{ 100 semid_t semid; 101 102 semid = (semid_t)SEM_USER; 103 if ((pshared != 0) && (ksem_init(&semid, value) != 0)) 104 return (-1); 105 106 (*sem) = sem_alloc(value, semid, pshared); 107 if ((*sem) == NULL) { 108 if (pshared != 0) 109 ksem_destroy(semid); 110 return (-1); 111 } 112 return (0); 113} 114 115int 116_sem_destroy(sem_t *sem) 117{ 118 int retval; 119 120 if (sem_check_validity(sem) != 0) 121 return (-1); 122 123 /* 124 * If this is a system semaphore let the kernel track it otherwise 125 * make sure there are no waiters. 126 */ 127 if ((*sem)->syssem != 0) 128 retval = ksem_destroy((*sem)->semid); 129 else { 130 retval = 0; 131 (*sem)->magic = 0; 132 } 133 if (retval == 0) 134 free(*sem); 135 return (retval); 136} 137 138int 139_sem_getvalue(sem_t * __restrict sem, int * __restrict sval) 140{ 141 int retval; 142 143 if (sem_check_validity(sem) != 0) 144 return (-1); 145 146 if ((*sem)->syssem != 0) 147 retval = ksem_getvalue((*sem)->semid, sval); 148 else { 149 *sval = (int)(*sem)->count; 150 retval = 0; 151 } 152 return (retval); 153} 154 155int 156_sem_trywait(sem_t *sem) 157{ 158 int val; 159 160 if (sem_check_validity(sem) != 0) 161 return (-1); 162 163 if ((*sem)->syssem != 0) 164 return (ksem_trywait((*sem)->semid)); 165 166 while ((val = (*sem)->count) > 0) { 167 if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1)) 168 return (0); 169 } 170 errno = EAGAIN; 171 return (-1); 172} 173 174int 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 if ((*sem)->syssem != 0) { 185 oldcancel = _thr_cancel_enter(curthread); 186 retval = ksem_wait((*sem)->semid); 187 _thr_cancel_leave(curthread, oldcancel); 188 return (retval); 189 } 190 191 _pthread_testcancel(); 192 do { 193 while ((val = (*sem)->count) > 0) { 194 if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1)) 195 return (0); 196 } 197 oldcancel = _thr_cancel_enter(curthread); 198 retval = _thr_umtx_wait((umtx_t *)&(*sem)->count, 0, NULL); 199 _thr_cancel_leave(curthread, oldcancel); 200 } while (retval == 0); 201 errno = retval; 202 return (-1); 203} 204 205int 206_sem_timedwait(sem_t * __restrict sem, struct timespec * __restrict abstime) 207{ 208 struct timespec ts, ts2; 209 struct pthread *curthread; 210 int val, oldcancel, retval; 211 212 if (sem_check_validity(sem) != 0) 213 return (-1); 214 215 curthread = _get_curthread(); 216 if ((*sem)->syssem != 0) { 217 oldcancel = _thr_cancel_enter(curthread); 218 retval = ksem_timedwait((*sem)->semid, abstime); 219 _thr_cancel_leave(curthread, oldcancel); 220 return (retval); 221 } 222 223 /* 224 * The timeout argument is only supposed to 225 * be checked if the thread would have blocked. 226 */ 227 _pthread_testcancel(); 228 do { 229 while ((val = (*sem)->count) > 0) { 230 if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1)) 231 return (0); 232 } 233 if (abstime == NULL) { 234 errno = EINVAL; 235 return (-1); 236 } 237 clock_gettime(CLOCK_REALTIME, &ts); 238 TIMESPEC_SUB(&ts2, abstime, &ts); 239 oldcancel = _thr_cancel_enter(curthread); 240 retval = _thr_umtx_wait((umtx_t *)&(*sem)->count, 0, &ts2); 241 _thr_cancel_leave(curthread, oldcancel); 242 } while (retval == 0); 243 errno = retval; 244 return (-1); 245} 246 247int 248_sem_post(sem_t *sem) 249{ 250 int val, retval; 251 252 if (sem_check_validity(sem) != 0) 253 return (-1); 254 255 if ((*sem)->syssem != 0) 256 return (ksem_post((*sem)->semid)); 257 258 /* 259 * sem_post() is required to be safe to call from within 260 * signal handlers, these code should work as that. 261 */ 262 do { 263 val = (*sem)->count; 264 } while (!atomic_cmpset_acq_int(&(*sem)->count, val, val + 1)); 265 retval = _thr_umtx_wake((umtx_t *)&(*sem)->count, val + 1); 266 if (retval > 0) 267 retval = 0; 268 return (retval); 269} 270