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