semaphore.c revision 668da74ef1d4558f699374cb28a9df1a011db122
11dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project/* 21dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * Copyright (C) 2008 The Android Open Source Project 31dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * All rights reserved. 41dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * 51dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * Redistribution and use in source and binary forms, with or without 61dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * modification, are permitted provided that the following conditions 71dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * are met: 81dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * * Redistributions of source code must retain the above copyright 91dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * notice, this list of conditions and the following disclaimer. 101dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * * Redistributions in binary form must reproduce the above copyright 111dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * notice, this list of conditions and the following disclaimer in 121dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * the documentation and/or other materials provided with the 131dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * distribution. 141dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * 151dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 161dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 171dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 181dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 191dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 201dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 211dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 221dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 231dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 241dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 251dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 261dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * SUCH DAMAGE. 271dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project */ 281dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project#include <semaphore.h> 291dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project#include <errno.h> 301dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project#include <sys/time.h> 311dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project#include <time.h> 32519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner#include <limits.h> 336304d8b21891fd0cb7b5a4c25159a3d3b1709d62David 'Digit' Turner 34eb847bc8666842a3cfc9c06e8458ad1abebebaf0Elliott Hughes#include "private/bionic_atomic_inline.h" 35eb847bc8666842a3cfc9c06e8458ad1abebebaf0Elliott Hughes#include "private/bionic_futex.h" 36eb847bc8666842a3cfc9c06e8458ad1abebebaf0Elliott Hughes 37519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner/* In this implementation, a semaphore contains a 38519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * 31-bit signed value and a 1-bit 'shared' flag 39519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * (for process-sharing purpose). 40519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * 41519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * We use the value -1 to indicate contention on the 42519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * semaphore, 0 or more to indicate uncontended state, 43519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * any value lower than -2 is invalid at runtime. 44519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * 45519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * State diagram: 46519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * 47519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * post(1) ==> 2 48519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * post(0) ==> 1 49519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * post(-1) ==> 1, then wake all waiters 50519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * 51519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * wait(2) ==> 1 52519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * wait(1) ==> 0 53519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * wait(0) ==> -1 then wait for a wake up + loop 54519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * wait(-1) ==> -1 then wait for a wake up + loop 55519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * 56519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner */ 57519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner 58519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner/* Use the upper 31-bits for the counter, and the lower one 59519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * for the shared flag. 606304d8b21891fd0cb7b5a4c25159a3d3b1709d62David 'Digit' Turner */ 61519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner#define SEMCOUNT_SHARED_MASK 0x00000001 62519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner#define SEMCOUNT_VALUE_MASK 0xfffffffe 63519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner#define SEMCOUNT_VALUE_SHIFT 1 64519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner 65519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner/* Maximum unsigned value that can be stored in the semaphore. 66519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * One bit is used for the shared flag, another one for the 67519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * sign bit, leaving us with only 30 bits. 68519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner */ 69519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner#define SEM_MAX_VALUE 0x3fffffff 70519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner 71519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner/* convert a value into the corresponding sem->count bit pattern */ 72519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner#define SEMCOUNT_FROM_VALUE(val) (((val) << SEMCOUNT_VALUE_SHIFT) & SEMCOUNT_VALUE_MASK) 73519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner 74519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner/* convert a sem->count bit pattern into the corresponding signed value */ 75519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner#define SEMCOUNT_TO_VALUE(sval) ((int)(sval) >> SEMCOUNT_VALUE_SHIFT) 76519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner 77519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner/* the value +1 as a sem->count bit-pattern. */ 78519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner#define SEMCOUNT_ONE SEMCOUNT_FROM_VALUE(1) 79519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner 80519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner/* the value -1 as a sem->count bit-pattern. */ 81519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner#define SEMCOUNT_MINUS_ONE SEMCOUNT_FROM_VALUE(-1) 82519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner 83519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner#define SEMCOUNT_DECREMENT(sval) (((sval) - (1U << SEMCOUNT_VALUE_SHIFT)) & SEMCOUNT_VALUE_MASK) 84519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner#define SEMCOUNT_INCREMENT(sval) (((sval) + (1U << SEMCOUNT_VALUE_SHIFT)) & SEMCOUNT_VALUE_MASK) 85519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner 86519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner/* return the shared bitflag from a semaphore */ 87519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner#define SEM_GET_SHARED(sem) ((sem)->count & SEMCOUNT_SHARED_MASK) 886304d8b21891fd0cb7b5a4c25159a3d3b1709d62David 'Digit' Turner 891dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 901dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectint sem_init(sem_t *sem, int pshared, unsigned int value) 911dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project{ 921dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if (sem == NULL) { 931dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project errno = EINVAL; 941dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return -1; 951dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project } 961dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 976304d8b21891fd0cb7b5a4c25159a3d3b1709d62David 'Digit' Turner /* ensure that 'value' can be stored in the semaphore */ 98519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner if (value > SEM_MAX_VALUE) { 996304d8b21891fd0cb7b5a4c25159a3d3b1709d62David 'Digit' Turner errno = EINVAL; 1001dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return -1; 1011dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project } 1021dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 103519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner sem->count = SEMCOUNT_FROM_VALUE(value); 1046304d8b21891fd0cb7b5a4c25159a3d3b1709d62David 'Digit' Turner if (pshared != 0) 105519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner sem->count |= SEMCOUNT_SHARED_MASK; 1066304d8b21891fd0cb7b5a4c25159a3d3b1709d62David 'Digit' Turner 1071dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return 0; 1081dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project} 1091dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 1101dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 1111dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectint sem_destroy(sem_t *sem) 1121dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project{ 113519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner int count; 114519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner 1151dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if (sem == NULL) { 1161dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project errno = EINVAL; 1171dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return -1; 1181dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project } 119519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner count = SEMCOUNT_TO_VALUE(sem->count); 120519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner if (count < 0) { 1211dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project errno = EBUSY; 1221dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return -1; 1231dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project } 1246304d8b21891fd0cb7b5a4c25159a3d3b1709d62David 'Digit' Turner sem->count = 0; 1251dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return 0; 1261dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project} 1271dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 1281dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 129668da74ef1d4558f699374cb28a9df1a011db122Elliott Hughessem_t *sem_open(const char *name __unused, int oflag __unused, ...) 1301dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project{ 1311dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project errno = ENOSYS; 1321dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return SEM_FAILED; 1331dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project} 1341dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 1351dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 1361dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectint sem_close(sem_t *sem) 1371dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project{ 1381dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if (sem == NULL) { 1391dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project errno = EINVAL; 1401dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return -1; 1411dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project } 1421dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project errno = ENOSYS; 1431dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return -1; 1441dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project} 1451dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 1461dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 14795d3cd0b85724d3702cfb71942f9aa0a5ee27c74Elliott Hughesint sem_unlink(const char* name __unused) 1481dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project{ 1491dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project errno = ENOSYS; 1501dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return -1; 1511dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project} 1521dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 1531dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 154519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner/* Decrement a semaphore's value atomically, 155519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * and return the old one. As a special case, 156519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * this returns immediately if the value is 157519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * negative (i.e. -1) 158519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner */ 159519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turnerstatic int 160519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner__sem_dec(volatile unsigned int *pvalue) 161519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner{ 162519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner unsigned int shared = (*pvalue & SEMCOUNT_SHARED_MASK); 163519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner unsigned int old, new; 164519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner int ret; 165519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner 166519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner do { 167519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner old = (*pvalue & SEMCOUNT_VALUE_MASK); 168519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner ret = SEMCOUNT_TO_VALUE(old); 169519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner if (ret < 0) 170519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner break; 171519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner 172519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner new = SEMCOUNT_DECREMENT(old); 173519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner } 174e31bfae2baa96742f998155ee26e56c826a8ce3aDavid 'Digit' Turner while (__bionic_cmpxchg((int)(old|shared), 175519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner (int)(new|shared), 176519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner (volatile int *)pvalue) != 0); 177519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner return ret; 178519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner} 179519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner 180519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner/* Same as __sem_dec, but will not touch anything if the 181519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * value is already negative *or* 0. Returns the old value. 1826304d8b21891fd0cb7b5a4c25159a3d3b1709d62David 'Digit' Turner */ 1831dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectstatic int 184519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner__sem_trydec(volatile unsigned int *pvalue) 1851dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project{ 186519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner unsigned int shared = (*pvalue & SEMCOUNT_SHARED_MASK); 187519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner unsigned int old, new; 188519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner int ret; 1891dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 1901dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project do { 191519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner old = (*pvalue & SEMCOUNT_VALUE_MASK); 192519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner ret = SEMCOUNT_TO_VALUE(old); 193519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner if (ret <= 0) 194519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner break; 195519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner 196519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner new = SEMCOUNT_DECREMENT(old); 1971dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project } 198e31bfae2baa96742f998155ee26e56c826a8ce3aDavid 'Digit' Turner while (__bionic_cmpxchg((int)(old|shared), 199519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner (int)(new|shared), 200519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner (volatile int *)pvalue) != 0); 2016304d8b21891fd0cb7b5a4c25159a3d3b1709d62David 'Digit' Turner 202519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner return ret; 2036304d8b21891fd0cb7b5a4c25159a3d3b1709d62David 'Digit' Turner} 2046304d8b21891fd0cb7b5a4c25159a3d3b1709d62David 'Digit' Turner 205519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner 206519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner/* "Increment" the value of a semaphore atomically and 207519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * return its old value. Note that this implements 208519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * the special case of "incrementing" any negative 209519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * value to +1 directly. 210519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * 211519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * NOTE: The value will _not_ wrap above SEM_VALUE_MAX 2126304d8b21891fd0cb7b5a4c25159a3d3b1709d62David 'Digit' Turner */ 2136304d8b21891fd0cb7b5a4c25159a3d3b1709d62David 'Digit' Turnerstatic int 2146304d8b21891fd0cb7b5a4c25159a3d3b1709d62David 'Digit' Turner__sem_inc(volatile unsigned int *pvalue) 2156304d8b21891fd0cb7b5a4c25159a3d3b1709d62David 'Digit' Turner{ 216519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner unsigned int shared = (*pvalue & SEMCOUNT_SHARED_MASK); 217519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner unsigned int old, new; 218519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner int ret; 2191dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 2206304d8b21891fd0cb7b5a4c25159a3d3b1709d62David 'Digit' Turner do { 221519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner old = (*pvalue & SEMCOUNT_VALUE_MASK); 222519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner ret = SEMCOUNT_TO_VALUE(old); 223519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner 224519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner /* Can't go higher than SEM_MAX_VALUE */ 225519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner if (ret == SEM_MAX_VALUE) 226519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner break; 227519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner 228519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner /* If the counter is negative, go directly to +1, 229519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * otherwise just increment */ 230519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner if (ret < 0) 231519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner new = SEMCOUNT_ONE; 232519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner else 233519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner new = SEMCOUNT_INCREMENT(old); 234519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner } 235e31bfae2baa96742f998155ee26e56c826a8ce3aDavid 'Digit' Turner while ( __bionic_cmpxchg((int)(old|shared), 236519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner (int)(new|shared), 237519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner (volatile int*)pvalue) != 0); 238519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner 239519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner return ret; 2401dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project} 2411dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 242fcd00ebbdf3e7f4e1e7782a65ae10fb0fc03a1aaAndy McFadden/* lock a semaphore */ 2431dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectint sem_wait(sem_t *sem) 2441dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project{ 2456304d8b21891fd0cb7b5a4c25159a3d3b1709d62David 'Digit' Turner unsigned shared; 2466304d8b21891fd0cb7b5a4c25159a3d3b1709d62David 'Digit' Turner 2471dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if (sem == NULL) { 2481dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project errno = EINVAL; 2491dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return -1; 2501dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project } 2511dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 2526304d8b21891fd0cb7b5a4c25159a3d3b1709d62David 'Digit' Turner shared = SEM_GET_SHARED(sem); 2536304d8b21891fd0cb7b5a4c25159a3d3b1709d62David 'Digit' Turner 2541dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project for (;;) { 255519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner if (__sem_dec(&sem->count) > 0) 2561dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project break; 2571dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 258519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner __futex_wait_ex(&sem->count, shared, shared|SEMCOUNT_MINUS_ONE, NULL); 2591dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project } 260fcd00ebbdf3e7f4e1e7782a65ae10fb0fc03a1aaAndy McFadden ANDROID_MEMBAR_FULL(); 2611dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return 0; 2621dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project} 2631dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 2641dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectint sem_timedwait(sem_t *sem, const struct timespec *abs_timeout) 2651dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project{ 2666304d8b21891fd0cb7b5a4c25159a3d3b1709d62David 'Digit' Turner unsigned int shared; 2671dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 2681dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if (sem == NULL) { 2691dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project errno = EINVAL; 2701dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return -1; 2711dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project } 2721dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 2731dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project /* POSIX says we need to try to decrement the semaphore 274519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * before checking the timeout value. Note that if the 275519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * value is currently 0, __sem_trydec() does nothing. 276519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner */ 277519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner if (__sem_trydec(&sem->count) > 0) { 278fcd00ebbdf3e7f4e1e7782a65ae10fb0fc03a1aaAndy McFadden ANDROID_MEMBAR_FULL(); 2791dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return 0; 280fcd00ebbdf3e7f4e1e7782a65ae10fb0fc03a1aaAndy McFadden } 2811dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 282519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner /* Check it as per Posix */ 2831dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if (abs_timeout == NULL || 2841dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project abs_timeout->tv_sec < 0 || 2851dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project abs_timeout->tv_nsec < 0 || 2861dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project abs_timeout->tv_nsec >= 1000000000) 2871dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project { 2881dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project errno = EINVAL; 2891dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return -1; 2901dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project } 2911dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 2926304d8b21891fd0cb7b5a4c25159a3d3b1709d62David 'Digit' Turner shared = SEM_GET_SHARED(sem); 2936304d8b21891fd0cb7b5a4c25159a3d3b1709d62David 'Digit' Turner 2941dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project for (;;) { 2951dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project struct timespec ts; 2961dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project int ret; 2971dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 2981dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project /* Posix mandates CLOCK_REALTIME here */ 2991dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project clock_gettime( CLOCK_REALTIME, &ts ); 3001dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project ts.tv_sec = abs_timeout->tv_sec - ts.tv_sec; 3011dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project ts.tv_nsec = abs_timeout->tv_nsec - ts.tv_nsec; 3021dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if (ts.tv_nsec < 0) { 3031dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project ts.tv_nsec += 1000000000; 3041dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project ts.tv_sec -= 1; 3051dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project } 3061dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 3071dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if (ts.tv_sec < 0 || ts.tv_nsec < 0) { 3081dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project errno = ETIMEDOUT; 3091dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return -1; 3101dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project } 3111dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 312519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner /* Try to grab the semaphore. If the value was 0, this 313519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * will also change it to -1 */ 314519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner if (__sem_dec(&sem->count) > 0) { 315519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner ANDROID_MEMBAR_FULL(); 316519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner break; 317519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner } 318519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner 319519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner /* Contention detected. wait for a wakeup event */ 320519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner ret = __futex_wait_ex(&sem->count, shared, shared|SEMCOUNT_MINUS_ONE, &ts); 3211dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 3221dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project /* return in case of timeout or interrupt */ 3231dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if (ret == -ETIMEDOUT || ret == -EINTR) { 3241dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project errno = -ret; 3251dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return -1; 3261dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project } 3271dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project } 3281dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return 0; 3291dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project} 3301dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 331519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner/* Unlock a semaphore */ 3321dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectint sem_post(sem_t *sem) 3331dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project{ 3346304d8b21891fd0cb7b5a4c25159a3d3b1709d62David 'Digit' Turner unsigned int shared; 335519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner int old; 3366304d8b21891fd0cb7b5a4c25159a3d3b1709d62David 'Digit' Turner 3371dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if (sem == NULL) 3381dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return EINVAL; 3391dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 3406304d8b21891fd0cb7b5a4c25159a3d3b1709d62David 'Digit' Turner shared = SEM_GET_SHARED(sem); 3416304d8b21891fd0cb7b5a4c25159a3d3b1709d62David 'Digit' Turner 342fcd00ebbdf3e7f4e1e7782a65ae10fb0fc03a1aaAndy McFadden ANDROID_MEMBAR_FULL(); 343519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner old = __sem_inc(&sem->count); 344519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner if (old < 0) { 345519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner /* contention on the semaphore, wake up all waiters */ 346519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner __futex_wake_ex(&sem->count, shared, INT_MAX); 347519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner } 348519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner else if (old == SEM_MAX_VALUE) { 349519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner /* overflow detected */ 350519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner errno = EOVERFLOW; 351519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner return -1; 352519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner } 3531dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 3541dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return 0; 3551dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project} 3561dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 3571dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectint sem_trywait(sem_t *sem) 3581dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project{ 3591dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if (sem == NULL) { 3601dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project errno = EINVAL; 3611dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return -1; 3621dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project } 3631dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 364519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner if (__sem_trydec(&sem->count) > 0) { 365fcd00ebbdf3e7f4e1e7782a65ae10fb0fc03a1aaAndy McFadden ANDROID_MEMBAR_FULL(); 3661dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return 0; 3671dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project } else { 368294dd0b86b1484aec7549663aff5b19c98a4b7fdDavid 'Digit' Turner errno = EAGAIN; 369294dd0b86b1484aec7549663aff5b19c98a4b7fdDavid 'Digit' Turner return -1; 3701dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project } 3711dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project} 3721dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 373519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner/* Note that Posix requires that sem_getvalue() returns, in 374519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * case of contention, the negative of the number of waiting 375519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * threads. 376519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * 377519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * However, code that depends on this negative value to be 378519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * meaningful is most probably racy. The GLibc sem_getvalue() 379519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * only returns the semaphore value, which is 0, in case of 380519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * contention, so we will mimick this behaviour here instead 381519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * for better compatibility. 382519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner */ 3831dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectint sem_getvalue(sem_t *sem, int *sval) 3841dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project{ 385519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner int val; 386519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner 3871dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if (sem == NULL || sval == NULL) { 3881dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project errno = EINVAL; 3891dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return -1; 3901dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project } 3911dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 392519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner val = SEMCOUNT_TO_VALUE(sem->count); 393519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner if (val < 0) 394519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner val = 0; 395519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner 396519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner *sval = val; 3971dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return 0; 3981dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project} 399