blob: 28fcb1b359297a7d8be2c7ce94c21f8f609a46ec [file] [log] [blame]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in
12 * the documentation and/or other materials provided with the
13 * distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28#include <semaphore.h>
29#include <errno.h>
30#include <sys/time.h>
31#include <sys/atomics.h>
32#include <time.h>
David 'Digit' Turner51976322010-06-28 14:10:14 -070033#include <limits.h>
David 'Digit' Turner6304d8b2010-06-02 18:12:12 -070034
Elliott Hugheseb847bc2013-10-09 15:50:50 -070035#include "private/bionic_atomic_inline.h"
36#include "private/bionic_futex.h"
37
David 'Digit' Turner51976322010-06-28 14:10:14 -070038/* In this implementation, a semaphore contains a
39 * 31-bit signed value and a 1-bit 'shared' flag
40 * (for process-sharing purpose).
41 *
42 * We use the value -1 to indicate contention on the
43 * semaphore, 0 or more to indicate uncontended state,
44 * any value lower than -2 is invalid at runtime.
45 *
46 * State diagram:
47 *
48 * post(1) ==> 2
49 * post(0) ==> 1
50 * post(-1) ==> 1, then wake all waiters
51 *
52 * wait(2) ==> 1
53 * wait(1) ==> 0
54 * wait(0) ==> -1 then wait for a wake up + loop
55 * wait(-1) ==> -1 then wait for a wake up + loop
56 *
David 'Digit' Turner6304d8b2010-06-02 18:12:12 -070057 */
David 'Digit' Turner6304d8b2010-06-02 18:12:12 -070058
David 'Digit' Turner51976322010-06-28 14:10:14 -070059/* Use the upper 31-bits for the counter, and the lower one
60 * for the shared flag.
61 */
62#define SEMCOUNT_SHARED_MASK 0x00000001
63#define SEMCOUNT_VALUE_MASK 0xfffffffe
64#define SEMCOUNT_VALUE_SHIFT 1
65
66/* Maximum unsigned value that can be stored in the semaphore.
67 * One bit is used for the shared flag, another one for the
68 * sign bit, leaving us with only 30 bits.
69 */
70#define SEM_MAX_VALUE 0x3fffffff
71
72/* convert a value into the corresponding sem->count bit pattern */
73#define SEMCOUNT_FROM_VALUE(val) (((val) << SEMCOUNT_VALUE_SHIFT) & SEMCOUNT_VALUE_MASK)
74
75/* convert a sem->count bit pattern into the corresponding signed value */
76#define SEMCOUNT_TO_VALUE(sval) ((int)(sval) >> SEMCOUNT_VALUE_SHIFT)
77
78/* the value +1 as a sem->count bit-pattern. */
79#define SEMCOUNT_ONE SEMCOUNT_FROM_VALUE(1)
80
81/* the value -1 as a sem->count bit-pattern. */
82#define SEMCOUNT_MINUS_ONE SEMCOUNT_FROM_VALUE(-1)
83
84#define SEMCOUNT_DECREMENT(sval) (((sval) - (1U << SEMCOUNT_VALUE_SHIFT)) & SEMCOUNT_VALUE_MASK)
85#define SEMCOUNT_INCREMENT(sval) (((sval) + (1U << SEMCOUNT_VALUE_SHIFT)) & SEMCOUNT_VALUE_MASK)
86
87/* return the shared bitflag from a semaphore */
88#define SEM_GET_SHARED(sem) ((sem)->count & SEMCOUNT_SHARED_MASK)
89
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080090
91int sem_init(sem_t *sem, int pshared, unsigned int value)
92{
93 if (sem == NULL) {
94 errno = EINVAL;
95 return -1;
96 }
97
David 'Digit' Turner6304d8b2010-06-02 18:12:12 -070098 /* ensure that 'value' can be stored in the semaphore */
David 'Digit' Turner51976322010-06-28 14:10:14 -070099 if (value > SEM_MAX_VALUE) {
David 'Digit' Turner6304d8b2010-06-02 18:12:12 -0700100 errno = EINVAL;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800101 return -1;
102 }
103
David 'Digit' Turner51976322010-06-28 14:10:14 -0700104 sem->count = SEMCOUNT_FROM_VALUE(value);
David 'Digit' Turner6304d8b2010-06-02 18:12:12 -0700105 if (pshared != 0)
David 'Digit' Turner51976322010-06-28 14:10:14 -0700106 sem->count |= SEMCOUNT_SHARED_MASK;
David 'Digit' Turner6304d8b2010-06-02 18:12:12 -0700107
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800108 return 0;
109}
110
111
112int sem_destroy(sem_t *sem)
113{
David 'Digit' Turner51976322010-06-28 14:10:14 -0700114 int count;
115
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800116 if (sem == NULL) {
117 errno = EINVAL;
118 return -1;
119 }
David 'Digit' Turner51976322010-06-28 14:10:14 -0700120 count = SEMCOUNT_TO_VALUE(sem->count);
121 if (count < 0) {
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800122 errno = EBUSY;
123 return -1;
124 }
David 'Digit' Turner6304d8b2010-06-02 18:12:12 -0700125 sem->count = 0;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800126 return 0;
127}
128
129
130sem_t *sem_open(const char *name, int oflag, ...)
131{
132 name=name;
133 oflag=oflag;
134
135 errno = ENOSYS;
136 return SEM_FAILED;
137}
138
139
140int sem_close(sem_t *sem)
141{
142 if (sem == NULL) {
143 errno = EINVAL;
144 return -1;
145 }
146 errno = ENOSYS;
147 return -1;
148}
149
150
151int sem_unlink(const char * name)
152{
153 errno = ENOSYS;
154 return -1;
155}
156
157
David 'Digit' Turner51976322010-06-28 14:10:14 -0700158/* Decrement a semaphore's value atomically,
159 * and return the old one. As a special case,
160 * this returns immediately if the value is
161 * negative (i.e. -1)
David 'Digit' Turner6304d8b2010-06-02 18:12:12 -0700162 */
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800163static int
David 'Digit' Turner51976322010-06-28 14:10:14 -0700164__sem_dec(volatile unsigned int *pvalue)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800165{
David 'Digit' Turner51976322010-06-28 14:10:14 -0700166 unsigned int shared = (*pvalue & SEMCOUNT_SHARED_MASK);
167 unsigned int old, new;
168 int ret;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800169
170 do {
David 'Digit' Turner51976322010-06-28 14:10:14 -0700171 old = (*pvalue & SEMCOUNT_VALUE_MASK);
172 ret = SEMCOUNT_TO_VALUE(old);
173 if (ret < 0)
174 break;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800175
David 'Digit' Turner51976322010-06-28 14:10:14 -0700176 new = SEMCOUNT_DECREMENT(old);
177 }
David 'Digit' Turnere31bfae2011-11-15 15:47:02 +0100178 while (__bionic_cmpxchg((int)(old|shared),
David 'Digit' Turner51976322010-06-28 14:10:14 -0700179 (int)(new|shared),
180 (volatile int *)pvalue) != 0);
181 return ret;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800182}
183
David 'Digit' Turner51976322010-06-28 14:10:14 -0700184/* Same as __sem_dec, but will not touch anything if the
185 * value is already negative *or* 0. Returns the old value.
186 */
187static int
188__sem_trydec(volatile unsigned int *pvalue)
189{
190 unsigned int shared = (*pvalue & SEMCOUNT_SHARED_MASK);
191 unsigned int old, new;
192 int ret;
193
194 do {
195 old = (*pvalue & SEMCOUNT_VALUE_MASK);
196 ret = SEMCOUNT_TO_VALUE(old);
197 if (ret <= 0)
198 break;
199
200 new = SEMCOUNT_DECREMENT(old);
201 }
David 'Digit' Turnere31bfae2011-11-15 15:47:02 +0100202 while (__bionic_cmpxchg((int)(old|shared),
David 'Digit' Turner51976322010-06-28 14:10:14 -0700203 (int)(new|shared),
204 (volatile int *)pvalue) != 0);
205
206 return ret;
207}
208
209
210/* "Increment" the value of a semaphore atomically and
211 * return its old value. Note that this implements
212 * the special case of "incrementing" any negative
213 * value to +1 directly.
214 *
215 * NOTE: The value will _not_ wrap above SEM_VALUE_MAX
David 'Digit' Turner6304d8b2010-06-02 18:12:12 -0700216 */
217static int
218__sem_inc(volatile unsigned int *pvalue)
219{
David 'Digit' Turner51976322010-06-28 14:10:14 -0700220 unsigned int shared = (*pvalue & SEMCOUNT_SHARED_MASK);
221 unsigned int old, new;
222 int ret;
David 'Digit' Turner6304d8b2010-06-02 18:12:12 -0700223
224 do {
David 'Digit' Turner51976322010-06-28 14:10:14 -0700225 old = (*pvalue & SEMCOUNT_VALUE_MASK);
226 ret = SEMCOUNT_TO_VALUE(old);
227
228 /* Can't go higher than SEM_MAX_VALUE */
229 if (ret == SEM_MAX_VALUE)
230 break;
231
232 /* If the counter is negative, go directly to +1,
233 * otherwise just increment */
234 if (ret < 0)
235 new = SEMCOUNT_ONE;
236 else
237 new = SEMCOUNT_INCREMENT(old);
238 }
David 'Digit' Turnere31bfae2011-11-15 15:47:02 +0100239 while ( __bionic_cmpxchg((int)(old|shared),
David 'Digit' Turner51976322010-06-28 14:10:14 -0700240 (int)(new|shared),
241 (volatile int*)pvalue) != 0);
242
243 return ret;
David 'Digit' Turner6304d8b2010-06-02 18:12:12 -0700244}
245
Andy McFaddenfcd00eb2010-05-28 13:31:45 -0700246/* lock a semaphore */
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800247int sem_wait(sem_t *sem)
248{
David 'Digit' Turner6304d8b2010-06-02 18:12:12 -0700249 unsigned shared;
250
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800251 if (sem == NULL) {
252 errno = EINVAL;
253 return -1;
254 }
255
David 'Digit' Turner6304d8b2010-06-02 18:12:12 -0700256 shared = SEM_GET_SHARED(sem);
257
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800258 for (;;) {
David 'Digit' Turner51976322010-06-28 14:10:14 -0700259 if (__sem_dec(&sem->count) > 0)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800260 break;
261
David 'Digit' Turner51976322010-06-28 14:10:14 -0700262 __futex_wait_ex(&sem->count, shared, shared|SEMCOUNT_MINUS_ONE, NULL);
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800263 }
Andy McFaddenfcd00eb2010-05-28 13:31:45 -0700264 ANDROID_MEMBAR_FULL();
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800265 return 0;
266}
267
268int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout)
269{
270 int ret;
David 'Digit' Turner6304d8b2010-06-02 18:12:12 -0700271 unsigned int shared;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800272
273 if (sem == NULL) {
274 errno = EINVAL;
275 return -1;
276 }
277
278 /* POSIX says we need to try to decrement the semaphore
David 'Digit' Turner51976322010-06-28 14:10:14 -0700279 * before checking the timeout value. Note that if the
280 * value is currently 0, __sem_trydec() does nothing.
281 */
282 if (__sem_trydec(&sem->count) > 0) {
Andy McFaddenfcd00eb2010-05-28 13:31:45 -0700283 ANDROID_MEMBAR_FULL();
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800284 return 0;
Andy McFaddenfcd00eb2010-05-28 13:31:45 -0700285 }
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800286
David 'Digit' Turner51976322010-06-28 14:10:14 -0700287 /* Check it as per Posix */
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800288 if (abs_timeout == NULL ||
289 abs_timeout->tv_sec < 0 ||
290 abs_timeout->tv_nsec < 0 ||
291 abs_timeout->tv_nsec >= 1000000000)
292 {
293 errno = EINVAL;
294 return -1;
295 }
296
David 'Digit' Turner6304d8b2010-06-02 18:12:12 -0700297 shared = SEM_GET_SHARED(sem);
298
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800299 for (;;) {
300 struct timespec ts;
301 int ret;
302
303 /* Posix mandates CLOCK_REALTIME here */
304 clock_gettime( CLOCK_REALTIME, &ts );
305 ts.tv_sec = abs_timeout->tv_sec - ts.tv_sec;
306 ts.tv_nsec = abs_timeout->tv_nsec - ts.tv_nsec;
307 if (ts.tv_nsec < 0) {
308 ts.tv_nsec += 1000000000;
309 ts.tv_sec -= 1;
310 }
311
312 if (ts.tv_sec < 0 || ts.tv_nsec < 0) {
313 errno = ETIMEDOUT;
314 return -1;
315 }
316
David 'Digit' Turner51976322010-06-28 14:10:14 -0700317 /* Try to grab the semaphore. If the value was 0, this
318 * will also change it to -1 */
319 if (__sem_dec(&sem->count) > 0) {
320 ANDROID_MEMBAR_FULL();
321 break;
322 }
323
324 /* Contention detected. wait for a wakeup event */
325 ret = __futex_wait_ex(&sem->count, shared, shared|SEMCOUNT_MINUS_ONE, &ts);
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800326
327 /* return in case of timeout or interrupt */
328 if (ret == -ETIMEDOUT || ret == -EINTR) {
329 errno = -ret;
330 return -1;
331 }
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800332 }
333 return 0;
334}
335
David 'Digit' Turner51976322010-06-28 14:10:14 -0700336/* Unlock a semaphore */
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800337int sem_post(sem_t *sem)
338{
David 'Digit' Turner6304d8b2010-06-02 18:12:12 -0700339 unsigned int shared;
David 'Digit' Turner51976322010-06-28 14:10:14 -0700340 int old;
David 'Digit' Turner6304d8b2010-06-02 18:12:12 -0700341
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800342 if (sem == NULL)
343 return EINVAL;
344
David 'Digit' Turner6304d8b2010-06-02 18:12:12 -0700345 shared = SEM_GET_SHARED(sem);
346
Andy McFaddenfcd00eb2010-05-28 13:31:45 -0700347 ANDROID_MEMBAR_FULL();
David 'Digit' Turner51976322010-06-28 14:10:14 -0700348 old = __sem_inc(&sem->count);
349 if (old < 0) {
350 /* contention on the semaphore, wake up all waiters */
351 __futex_wake_ex(&sem->count, shared, INT_MAX);
352 }
353 else if (old == SEM_MAX_VALUE) {
354 /* overflow detected */
355 errno = EOVERFLOW;
356 return -1;
357 }
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800358
359 return 0;
360}
361
362int sem_trywait(sem_t *sem)
363{
364 if (sem == NULL) {
365 errno = EINVAL;
366 return -1;
367 }
368
David 'Digit' Turner51976322010-06-28 14:10:14 -0700369 if (__sem_trydec(&sem->count) > 0) {
Andy McFaddenfcd00eb2010-05-28 13:31:45 -0700370 ANDROID_MEMBAR_FULL();
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800371 return 0;
372 } else {
David 'Digit' Turner294dd0b2010-02-12 12:18:37 -0800373 errno = EAGAIN;
374 return -1;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800375 }
376}
377
David 'Digit' Turner51976322010-06-28 14:10:14 -0700378/* Note that Posix requires that sem_getvalue() returns, in
379 * case of contention, the negative of the number of waiting
380 * threads.
381 *
382 * However, code that depends on this negative value to be
383 * meaningful is most probably racy. The GLibc sem_getvalue()
384 * only returns the semaphore value, which is 0, in case of
385 * contention, so we will mimick this behaviour here instead
386 * for better compatibility.
387 */
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800388int sem_getvalue(sem_t *sem, int *sval)
389{
David 'Digit' Turner51976322010-06-28 14:10:14 -0700390 int val;
391
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800392 if (sem == NULL || sval == NULL) {
393 errno = EINVAL;
394 return -1;
395 }
396
David 'Digit' Turner51976322010-06-28 14:10:14 -0700397 val = SEMCOUNT_TO_VALUE(sem->count);
398 if (val < 0)
399 val = 0;
400
401 *sval = val;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800402 return 0;
403}