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