thr_sem.c revision 144518
1112918Sjeff/* 2144518Sdavidxu * Copyright (C) 2005 David Xu <davidxu@freebsd.org>. 3112918Sjeff * Copyright (C) 2000 Jason Evans <jasone@freebsd.org>. 4112918Sjeff * All rights reserved. 5112918Sjeff * 6112918Sjeff * Redistribution and use in source and binary forms, with or without 7112918Sjeff * modification, are permitted provided that the following conditions 8112918Sjeff * are met: 9112918Sjeff * 1. Redistributions of source code must retain the above copyright 10112918Sjeff * notice(s), this list of conditions and the following disclaimer as 11112918Sjeff * the first lines of this file unmodified other than the possible 12112918Sjeff * addition of one or more copyright notices. 13112918Sjeff * 2. Redistributions in binary form must reproduce the above copyright 14112918Sjeff * notice(s), this list of conditions and the following disclaimer in 15112918Sjeff * the documentation and/or other materials provided with the 16112918Sjeff * distribution. 17112918Sjeff * 18112918Sjeff * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY 19112918Sjeff * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20112918Sjeff * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 21112918Sjeff * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE 22112918Sjeff * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23112918Sjeff * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24112918Sjeff * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 25112918Sjeff * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26112918Sjeff * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 27112918Sjeff * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 28112918Sjeff * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29112918Sjeff * 30112918Sjeff * $FreeBSD: head/lib/libthr/thread/thr_sem.c 144518 2005-04-02 01:20:00Z davidxu $ 31112918Sjeff */ 32112918Sjeff 33144518Sdavidxu#include "namespace.h" 34144518Sdavidxu#include <sys/queue.h> 35112918Sjeff#include <errno.h> 36144518Sdavidxu#include <fcntl.h> 37144518Sdavidxu#include <pthread.h> 38112918Sjeff#include <semaphore.h> 39144518Sdavidxu#include <stdlib.h> 40144518Sdavidxu#include <time.h> 41144518Sdavidxu#include <_semaphore.h> 42144518Sdavidxu#include "un-namespace.h" 43144518Sdavidxu 44112918Sjeff#include "thr_private.h" 45112918Sjeff 46112918Sjeff 47112918Sjeff__weak_reference(_sem_init, sem_init); 48112918Sjeff__weak_reference(_sem_destroy, sem_destroy); 49144518Sdavidxu__weak_reference(_sem_getvalue, sem_getvalue); 50144518Sdavidxu__weak_reference(_sem_trywait, sem_trywait); 51112918Sjeff__weak_reference(_sem_wait, sem_wait); 52144518Sdavidxu__weak_reference(_sem_timedwait, sem_timedwait); 53112918Sjeff__weak_reference(_sem_post, sem_post); 54112918Sjeff 55112918Sjeff 56144518Sdavidxustatic inline int 57144518Sdavidxusem_check_validity(sem_t *sem) 58112918Sjeff{ 59112918Sjeff 60144518Sdavidxu if ((sem != NULL) && ((*sem)->magic == SEM_MAGIC)) 61144518Sdavidxu return (0); 62144518Sdavidxu else { 63144518Sdavidxu errno = EINVAL; 64144518Sdavidxu return (-1); 65112918Sjeff } 66144518Sdavidxu} 67112918Sjeff 68144518Sdavidxustatic sem_t 69144518Sdavidxusem_alloc(unsigned int value, semid_t semid, int system_sem) 70144518Sdavidxu{ 71144518Sdavidxu sem_t sem; 72144518Sdavidxu 73112918Sjeff if (value > SEM_VALUE_MAX) { 74112918Sjeff errno = EINVAL; 75144518Sdavidxu return (NULL); 76112918Sjeff } 77112918Sjeff 78144518Sdavidxu sem = (sem_t)malloc(sizeof(struct sem)); 79144518Sdavidxu if (sem == NULL) { 80112918Sjeff errno = ENOSPC; 81144518Sdavidxu return (NULL); 82112918Sjeff } 83144518Sdavidxu _thr_umtx_init((umtx_t *)&sem->lock); 84112918Sjeff /* 85144518Sdavidxu * Fortunatly count and nwaiters are adjacency, so we can 86144518Sdavidxu * use umtx_wait to wait on it, umtx_wait needs an address 87144518Sdavidxu * can be accessed as a long interger. 88112918Sjeff */ 89144518Sdavidxu sem->count = (u_int32_t)value; 90144518Sdavidxu sem->nwaiters = 0; 91144518Sdavidxu sem->magic = SEM_MAGIC; 92144518Sdavidxu sem->semid = semid; 93144518Sdavidxu sem->syssem = system_sem; 94144518Sdavidxu return (sem); 95144518Sdavidxu} 96112918Sjeff 97144518Sdavidxuint 98144518Sdavidxu_sem_init(sem_t *sem, int pshared, unsigned int value) 99144518Sdavidxu{ 100144518Sdavidxu semid_t semid; 101144518Sdavidxu 102144518Sdavidxu semid = (semid_t)SEM_USER; 103144518Sdavidxu if ((pshared != 0) && (ksem_init(&semid, value) != 0)) 104144518Sdavidxu return (-1); 105144518Sdavidxu 106144518Sdavidxu (*sem) = sem_alloc(value, semid, pshared); 107144518Sdavidxu if ((*sem) == NULL) { 108144518Sdavidxu if (pshared != 0) 109144518Sdavidxu ksem_destroy(semid); 110144518Sdavidxu return (-1); 111112918Sjeff } 112144518Sdavidxu return (0); 113112918Sjeff} 114112918Sjeff 115112918Sjeffint 116112918Sjeff_sem_destroy(sem_t *sem) 117112918Sjeff{ 118144518Sdavidxu int retval; 119112918Sjeff 120144518Sdavidxu if (sem_check_validity(sem) != 0) 121144518Sdavidxu return (-1); 122144518Sdavidxu 123144518Sdavidxu /* 124144518Sdavidxu * If this is a system semaphore let the kernel track it otherwise 125144518Sdavidxu * make sure there are no waiters. 126144518Sdavidxu */ 127144518Sdavidxu if ((*sem)->syssem != 0) 128144518Sdavidxu retval = ksem_destroy((*sem)->semid); 129144518Sdavidxu else { 130144518Sdavidxu retval = 0; 131144518Sdavidxu (*sem)->magic = 0; 132112918Sjeff } 133144518Sdavidxu if (retval == 0) 134144518Sdavidxu free(*sem); 135144518Sdavidxu return (retval); 136112918Sjeff} 137112918Sjeff 138112918Sjeffint 139144518Sdavidxu_sem_getvalue(sem_t * __restrict sem, int * __restrict sval) 140112918Sjeff{ 141144518Sdavidxu int retval; 142112918Sjeff 143144518Sdavidxu if (sem_check_validity(sem) != 0) 144144518Sdavidxu return (-1); 145144518Sdavidxu 146144518Sdavidxu if ((*sem)->syssem != 0) 147144518Sdavidxu retval = ksem_getvalue((*sem)->semid, sval); 148144518Sdavidxu else { 149144518Sdavidxu *sval = (int)(*sem)->count; 150144518Sdavidxu retval = 0; 151144518Sdavidxu } 152144518Sdavidxu return (retval); 153112918Sjeff} 154112918Sjeff 155112918Sjeffint 156144518Sdavidxu_sem_trywait(sem_t *sem) 157112918Sjeff{ 158144518Sdavidxu int val; 159112918Sjeff 160144518Sdavidxu if (sem_check_validity(sem) != 0) 161144518Sdavidxu return (-1); 162112918Sjeff 163144518Sdavidxu if ((*sem)->syssem != 0) 164144518Sdavidxu return (ksem_trywait((*sem)->semid)); 165112918Sjeff 166144518Sdavidxu while ((val = (*sem)->count) > 0) { 167144518Sdavidxu if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1)) 168144518Sdavidxu return (0); 169112918Sjeff } 170144518Sdavidxu errno = EAGAIN; 171144518Sdavidxu return (-1); 172112918Sjeff} 173112918Sjeff 174112918Sjeffint 175144518Sdavidxu_sem_wait(sem_t *sem) 176112918Sjeff{ 177144518Sdavidxu struct pthread *curthread; 178144518Sdavidxu int val, oldcancel, retval; 179112918Sjeff 180144518Sdavidxu if (sem_check_validity(sem) != 0) 181144518Sdavidxu return (-1); 182112918Sjeff 183144518Sdavidxu curthread = _get_curthread(); 184144518Sdavidxu if ((*sem)->syssem != 0) { 185144518Sdavidxu oldcancel = _thr_cancel_enter(curthread); 186144518Sdavidxu retval = ksem_wait((*sem)->semid); 187144518Sdavidxu _thr_cancel_leave(curthread, oldcancel); 188144518Sdavidxu return (retval); 189112918Sjeff } 190112918Sjeff 191144518Sdavidxu _pthread_testcancel(); 192144518Sdavidxu do { 193144518Sdavidxu while ((val = (*sem)->count) > 0) { 194144518Sdavidxu if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1)) 195144518Sdavidxu return (0); 196144518Sdavidxu } 197144518Sdavidxu oldcancel = _thr_cancel_enter(curthread); 198144518Sdavidxu retval = _thr_umtx_wait((umtx_t *)&(*sem)->count, 0, NULL); 199144518Sdavidxu _thr_cancel_leave(curthread, oldcancel); 200144518Sdavidxu } while (retval == 0); 201144518Sdavidxu errno = retval; 202144518Sdavidxu return (-1); 203112918Sjeff} 204112918Sjeff 205112918Sjeffint 206144518Sdavidxu_sem_timedwait(sem_t * __restrict sem, struct timespec * __restrict abstime) 207112918Sjeff{ 208144518Sdavidxu struct timespec ts, ts2; 209144518Sdavidxu struct pthread *curthread; 210144518Sdavidxu int val, oldcancel, retval; 211112918Sjeff 212144518Sdavidxu if (sem_check_validity(sem) != 0) 213144518Sdavidxu return (-1); 214112918Sjeff 215144518Sdavidxu curthread = _get_curthread(); 216144518Sdavidxu if ((*sem)->syssem != 0) { 217144518Sdavidxu oldcancel = _thr_cancel_enter(curthread); 218144518Sdavidxu retval = ksem_timedwait((*sem)->semid, abstime); 219144518Sdavidxu _thr_cancel_leave(curthread, oldcancel); 220144518Sdavidxu return (retval); 221144518Sdavidxu } 222144518Sdavidxu 223112918Sjeff /* 224144518Sdavidxu * The timeout argument is only supposed to 225144518Sdavidxu * be checked if the thread would have blocked. 226112918Sjeff */ 227144518Sdavidxu _pthread_testcancel(); 228144518Sdavidxu do { 229144518Sdavidxu while ((val = (*sem)->count) > 0) { 230144518Sdavidxu if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1)) 231144518Sdavidxu return (0); 232144518Sdavidxu } 233144518Sdavidxu if (abstime == NULL) { 234144518Sdavidxu errno = EINVAL; 235144518Sdavidxu return (-1); 236144518Sdavidxu } 237144518Sdavidxu clock_gettime(CLOCK_REALTIME, &ts); 238144518Sdavidxu TIMESPEC_SUB(&ts2, abstime, &ts); 239144518Sdavidxu oldcancel = _thr_cancel_enter(curthread); 240144518Sdavidxu retval = _thr_umtx_wait((umtx_t *)&(*sem)->count, 0, &ts2); 241144518Sdavidxu _thr_cancel_leave(curthread, oldcancel); 242144518Sdavidxu } while (retval == 0); 243144518Sdavidxu errno = retval; 244144518Sdavidxu return (-1); 245112918Sjeff} 246112918Sjeff 247112918Sjeffint 248144518Sdavidxu_sem_post(sem_t *sem) 249112918Sjeff{ 250144518Sdavidxu int val, retval; 251144518Sdavidxu 252144518Sdavidxu if (sem_check_validity(sem) != 0) 253144518Sdavidxu return (-1); 254112918Sjeff 255144518Sdavidxu if ((*sem)->syssem != 0) 256144518Sdavidxu return (ksem_post((*sem)->semid)); 257112918Sjeff 258144518Sdavidxu /* 259144518Sdavidxu * sem_post() is required to be safe to call from within 260144518Sdavidxu * signal handlers, these code should work as that. 261144518Sdavidxu */ 262144518Sdavidxu do { 263144518Sdavidxu val = (*sem)->count; 264144518Sdavidxu } while (!atomic_cmpset_acq_int(&(*sem)->count, val, val + 1)); 265144518Sdavidxu retval = _thr_umtx_wake((umtx_t *)&(*sem)->count, val + 1); 266144518Sdavidxu if (retval > 0) 267144518Sdavidxu retval = 0; 268144518Sdavidxu return (retval); 269112918Sjeff} 270