semaphore.c revision 519763265ec0b634bd9c264a0aca034882458ecc
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 <sys/atomics.h> 321dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project#include <time.h> 336c8a2f2a5bc8d612ee953f528f2b5eb35983656aDavid 'Digit' Turner#include <bionic_atomic_inline.h> 346304d8b21891fd0cb7b5a4c25159a3d3b1709d62David 'Digit' Turner#include <bionic_futex.h> 35519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner#include <limits.h> 366304d8b21891fd0cb7b5a4c25159a3d3b1709d62David 'Digit' Turner 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 1291dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectsem_t *sem_open(const char *name, int oflag, ...) 1301dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project{ 1311dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project name=name; 1321dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project oflag=oflag; 1331dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 1341dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project errno = ENOSYS; 1351dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return SEM_FAILED; 1361dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project} 1371dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 1381dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 1391dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectint sem_close(sem_t *sem) 1401dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project{ 1411dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if (sem == NULL) { 1421dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project errno = EINVAL; 1431dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return -1; 1441dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project } 1451dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project errno = ENOSYS; 1461dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return -1; 1471dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project} 1481dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 1491dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 1501dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectint sem_unlink(const char * name) 1511dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project{ 1521dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project errno = ENOSYS; 1531dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return -1; 1541dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project} 1551dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 1561dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 157519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner/* Decrement a semaphore's value atomically, 158519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * and return the old one. As a special case, 159519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * this returns immediately if the value is 160519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * negative (i.e. -1) 161519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner */ 162519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turnerstatic int 163519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner__sem_dec(volatile unsigned int *pvalue) 164519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner{ 165519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner unsigned int shared = (*pvalue & SEMCOUNT_SHARED_MASK); 166519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner unsigned int old, new; 167519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner int ret; 168519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner 169519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner do { 170519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner old = (*pvalue & SEMCOUNT_VALUE_MASK); 171519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner ret = SEMCOUNT_TO_VALUE(old); 172519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner if (ret < 0) 173519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner break; 174519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner 175519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner new = SEMCOUNT_DECREMENT(old); 176519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner } 177519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner while (__atomic_cmpxchg((int)(old|shared), 178519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner (int)(new|shared), 179519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner (volatile int *)pvalue) != 0); 180519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner return ret; 181519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner} 182519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner 183519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner/* Same as __sem_dec, but will not touch anything if the 184519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * value is already negative *or* 0. Returns the old value. 1856304d8b21891fd0cb7b5a4c25159a3d3b1709d62David 'Digit' Turner */ 1861dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectstatic int 187519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner__sem_trydec(volatile unsigned int *pvalue) 1881dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project{ 189519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner unsigned int shared = (*pvalue & SEMCOUNT_SHARED_MASK); 190519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner unsigned int old, new; 191519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner int ret; 1921dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 1931dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project do { 194519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner old = (*pvalue & SEMCOUNT_VALUE_MASK); 195519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner ret = SEMCOUNT_TO_VALUE(old); 196519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner if (ret <= 0) 197519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner break; 198519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner 199519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner new = SEMCOUNT_DECREMENT(old); 2001dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project } 201519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner while (__atomic_cmpxchg((int)(old|shared), 202519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner (int)(new|shared), 203519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner (volatile int *)pvalue) != 0); 2046304d8b21891fd0cb7b5a4c25159a3d3b1709d62David 'Digit' Turner 205519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner return ret; 2066304d8b21891fd0cb7b5a4c25159a3d3b1709d62David 'Digit' Turner} 2076304d8b21891fd0cb7b5a4c25159a3d3b1709d62David 'Digit' Turner 208519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner 209519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner/* "Increment" the value of a semaphore atomically and 210519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * return its old value. Note that this implements 211519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * the special case of "incrementing" any negative 212519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * value to +1 directly. 213519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * 214519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * NOTE: The value will _not_ wrap above SEM_VALUE_MAX 2156304d8b21891fd0cb7b5a4c25159a3d3b1709d62David 'Digit' Turner */ 2166304d8b21891fd0cb7b5a4c25159a3d3b1709d62David 'Digit' Turnerstatic int 2176304d8b21891fd0cb7b5a4c25159a3d3b1709d62David 'Digit' Turner__sem_inc(volatile unsigned int *pvalue) 2186304d8b21891fd0cb7b5a4c25159a3d3b1709d62David 'Digit' Turner{ 219519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner unsigned int shared = (*pvalue & SEMCOUNT_SHARED_MASK); 220519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner unsigned int old, new; 221519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner int ret; 2221dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 2236304d8b21891fd0cb7b5a4c25159a3d3b1709d62David 'Digit' Turner do { 224519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner old = (*pvalue & SEMCOUNT_VALUE_MASK); 225519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner ret = SEMCOUNT_TO_VALUE(old); 226519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner 227519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner /* Can't go higher than SEM_MAX_VALUE */ 228519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner if (ret == SEM_MAX_VALUE) 229519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner break; 230519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner 231519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner /* If the counter is negative, go directly to +1, 232519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * otherwise just increment */ 233519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner if (ret < 0) 234519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner new = SEMCOUNT_ONE; 235519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner else 236519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner new = SEMCOUNT_INCREMENT(old); 237519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner } 238519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner while ( __atomic_cmpxchg((int)(old|shared), 239519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner (int)(new|shared), 240519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner (volatile int*)pvalue) != 0); 241519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner 242519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner return ret; 2431dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project} 2441dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 245fcd00ebbdf3e7f4e1e7782a65ae10fb0fc03a1aaAndy McFadden/* lock a semaphore */ 2461dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectint sem_wait(sem_t *sem) 2471dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project{ 2486304d8b21891fd0cb7b5a4c25159a3d3b1709d62David 'Digit' Turner unsigned shared; 2496304d8b21891fd0cb7b5a4c25159a3d3b1709d62David 'Digit' Turner 2501dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if (sem == NULL) { 2511dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project errno = EINVAL; 2521dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return -1; 2531dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project } 2541dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 2556304d8b21891fd0cb7b5a4c25159a3d3b1709d62David 'Digit' Turner shared = SEM_GET_SHARED(sem); 2566304d8b21891fd0cb7b5a4c25159a3d3b1709d62David 'Digit' Turner 2571dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project for (;;) { 258519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner if (__sem_dec(&sem->count) > 0) 2591dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project break; 2601dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 261519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner __futex_wait_ex(&sem->count, shared, shared|SEMCOUNT_MINUS_ONE, NULL); 2621dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project } 263fcd00ebbdf3e7f4e1e7782a65ae10fb0fc03a1aaAndy McFadden ANDROID_MEMBAR_FULL(); 2641dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return 0; 2651dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project} 2661dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 2671dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectint sem_timedwait(sem_t *sem, const struct timespec *abs_timeout) 2681dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project{ 2691dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project int ret; 2706304d8b21891fd0cb7b5a4c25159a3d3b1709d62David 'Digit' Turner unsigned int shared; 2711dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 2721dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if (sem == NULL) { 2731dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project errno = EINVAL; 2741dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return -1; 2751dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project } 2761dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 2771dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project /* POSIX says we need to try to decrement the semaphore 278519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * before checking the timeout value. Note that if the 279519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * value is currently 0, __sem_trydec() does nothing. 280519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner */ 281519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner if (__sem_trydec(&sem->count) > 0) { 282fcd00ebbdf3e7f4e1e7782a65ae10fb0fc03a1aaAndy McFadden ANDROID_MEMBAR_FULL(); 2831dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return 0; 284fcd00ebbdf3e7f4e1e7782a65ae10fb0fc03a1aaAndy McFadden } 2851dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 286519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner /* Check it as per Posix */ 2871dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if (abs_timeout == NULL || 2881dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project abs_timeout->tv_sec < 0 || 2891dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project abs_timeout->tv_nsec < 0 || 2901dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project abs_timeout->tv_nsec >= 1000000000) 2911dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project { 2921dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project errno = EINVAL; 2931dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return -1; 2941dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project } 2951dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 2966304d8b21891fd0cb7b5a4c25159a3d3b1709d62David 'Digit' Turner shared = SEM_GET_SHARED(sem); 2976304d8b21891fd0cb7b5a4c25159a3d3b1709d62David 'Digit' Turner 2981dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project for (;;) { 2991dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project struct timespec ts; 3001dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project int ret; 3011dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 3021dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project /* Posix mandates CLOCK_REALTIME here */ 3031dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project clock_gettime( CLOCK_REALTIME, &ts ); 3041dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project ts.tv_sec = abs_timeout->tv_sec - ts.tv_sec; 3051dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project ts.tv_nsec = abs_timeout->tv_nsec - ts.tv_nsec; 3061dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if (ts.tv_nsec < 0) { 3071dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project ts.tv_nsec += 1000000000; 3081dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project ts.tv_sec -= 1; 3091dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project } 3101dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 3111dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if (ts.tv_sec < 0 || ts.tv_nsec < 0) { 3121dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project errno = ETIMEDOUT; 3131dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return -1; 3141dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project } 3151dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 316519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner /* Try to grab the semaphore. If the value was 0, this 317519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * will also change it to -1 */ 318519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner if (__sem_dec(&sem->count) > 0) { 319519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner ANDROID_MEMBAR_FULL(); 320519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner break; 321519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner } 322519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner 323519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner /* Contention detected. wait for a wakeup event */ 324519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner ret = __futex_wait_ex(&sem->count, shared, shared|SEMCOUNT_MINUS_ONE, &ts); 3251dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 3261dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project /* return in case of timeout or interrupt */ 3271dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if (ret == -ETIMEDOUT || ret == -EINTR) { 3281dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project errno = -ret; 3291dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return -1; 3301dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project } 3311dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project } 3321dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return 0; 3331dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project} 3341dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 335519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner/* Unlock a semaphore */ 3361dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectint sem_post(sem_t *sem) 3371dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project{ 3386304d8b21891fd0cb7b5a4c25159a3d3b1709d62David 'Digit' Turner unsigned int shared; 339519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner int old; 3406304d8b21891fd0cb7b5a4c25159a3d3b1709d62David 'Digit' Turner 3411dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if (sem == NULL) 3421dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return EINVAL; 3431dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 3446304d8b21891fd0cb7b5a4c25159a3d3b1709d62David 'Digit' Turner shared = SEM_GET_SHARED(sem); 3456304d8b21891fd0cb7b5a4c25159a3d3b1709d62David 'Digit' Turner 346fcd00ebbdf3e7f4e1e7782a65ae10fb0fc03a1aaAndy McFadden ANDROID_MEMBAR_FULL(); 347519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner old = __sem_inc(&sem->count); 348519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner if (old < 0) { 349519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner /* contention on the semaphore, wake up all waiters */ 350519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner __futex_wake_ex(&sem->count, shared, INT_MAX); 351519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner } 352519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner else if (old == SEM_MAX_VALUE) { 353519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner /* overflow detected */ 354519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner errno = EOVERFLOW; 355519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner return -1; 356519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner } 3571dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 3581dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return 0; 3591dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project} 3601dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 3611dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectint sem_trywait(sem_t *sem) 3621dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project{ 3631dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if (sem == NULL) { 3641dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project errno = EINVAL; 3651dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return -1; 3661dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project } 3671dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 368519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner if (__sem_trydec(&sem->count) > 0) { 369fcd00ebbdf3e7f4e1e7782a65ae10fb0fc03a1aaAndy McFadden ANDROID_MEMBAR_FULL(); 3701dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return 0; 3711dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project } else { 372294dd0b86b1484aec7549663aff5b19c98a4b7fdDavid 'Digit' Turner errno = EAGAIN; 373294dd0b86b1484aec7549663aff5b19c98a4b7fdDavid 'Digit' Turner return -1; 3741dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project } 3751dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project} 3761dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 377519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner/* Note that Posix requires that sem_getvalue() returns, in 378519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * case of contention, the negative of the number of waiting 379519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * threads. 380519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * 381519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * However, code that depends on this negative value to be 382519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * meaningful is most probably racy. The GLibc sem_getvalue() 383519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * only returns the semaphore value, which is 0, in case of 384519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * contention, so we will mimick this behaviour here instead 385519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner * for better compatibility. 386519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner */ 3871dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectint sem_getvalue(sem_t *sem, int *sval) 3881dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project{ 389519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner int val; 390519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner 3911dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if (sem == NULL || sval == NULL) { 3921dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project errno = EINVAL; 3931dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return -1; 3941dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project } 3951dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 396519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner val = SEMCOUNT_TO_VALUE(sem->count); 397519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner if (val < 0) 398519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner val = 0; 399519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner 400519763265ec0b634bd9c264a0aca034882458eccDavid 'Digit' Turner *sval = val; 4011dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return 0; 4021dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project} 403