blob: c23eb752dff0bc6b44d10f3bdd8288e1721d224c [file] [log] [blame]
Elliott Hughes04303f52014-09-18 16:11:59 -07001/*
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
29#include <semaphore.h>
30#include <errno.h>
31#include <limits.h>
32#include <sys/time.h>
33#include <time.h>
34
35#include "private/bionic_atomic_inline.h"
36#include "private/bionic_constants.h"
37#include "private/bionic_futex.h"
38#include "private/bionic_time_conversions.h"
39
40// In this implementation, a semaphore contains a
41// 31-bit signed value and a 1-bit 'shared' flag
42// (for process-sharing purpose).
43//
44// We use the value -1 to indicate contention on the
45// semaphore, 0 or more to indicate uncontended state,
46// any value lower than -2 is invalid at runtime.
47//
48// State diagram:
49//
50// post(1) ==> 2
51// post(0) ==> 1
52// post(-1) ==> 1, then wake all waiters
53//
54// wait(2) ==> 1
55// wait(1) ==> 0
56// wait(0) ==> -1 then wait for a wake up + loop
57// wait(-1) ==> -1 then wait for a wake up + loop
58
59// Use the upper 31-bits for the counter, and the lower one
60// for the shared flag.
61#define SEMCOUNT_SHARED_MASK 0x00000001
62#define SEMCOUNT_VALUE_MASK 0xfffffffe
63#define SEMCOUNT_VALUE_SHIFT 1
64
65// Convert a value into the corresponding sem->count bit pattern.
66#define SEMCOUNT_FROM_VALUE(val) (((val) << SEMCOUNT_VALUE_SHIFT) & SEMCOUNT_VALUE_MASK)
67
68// Convert a sem->count bit pattern into the corresponding signed value.
69static inline int SEMCOUNT_TO_VALUE(uint32_t sval) {
70 return (static_cast<int>(sval) >> SEMCOUNT_VALUE_SHIFT);
71}
72
73// The value +1 as a sem->count bit-pattern.
74#define SEMCOUNT_ONE SEMCOUNT_FROM_VALUE(1)
75
76// The value -1 as a sem->count bit-pattern.
77#define SEMCOUNT_MINUS_ONE SEMCOUNT_FROM_VALUE(-1)
78
79#define SEMCOUNT_DECREMENT(sval) (((sval) - (1U << SEMCOUNT_VALUE_SHIFT)) & SEMCOUNT_VALUE_MASK)
80#define SEMCOUNT_INCREMENT(sval) (((sval) + (1U << SEMCOUNT_VALUE_SHIFT)) & SEMCOUNT_VALUE_MASK)
81
82// Return the shared bitflag from a semaphore.
83static inline uint32_t SEM_GET_SHARED(sem_t* sem) {
84 return (sem->count & SEMCOUNT_SHARED_MASK);
85}
86
87
88int sem_init(sem_t* sem, int pshared, unsigned int value) {
89 if (sem == NULL) {
90 errno = EINVAL;
91 return -1;
92 }
93
94 // Ensure that 'value' can be stored in the semaphore.
95 if (value > SEM_VALUE_MAX) {
96 errno = EINVAL;
97 return -1;
98 }
99
100 sem->count = SEMCOUNT_FROM_VALUE(value);
101 if (pshared != 0) {
102 sem->count |= SEMCOUNT_SHARED_MASK;
103 }
104 return 0;
105}
106
107int sem_destroy(sem_t* sem) {
108 if (sem == NULL) {
109 errno = EINVAL;
110 return -1;
111 }
112 return 0;
113}
114
115sem_t* sem_open(const char*, int, ...) {
116 errno = ENOSYS;
117 return SEM_FAILED;
118}
119
120int sem_close(sem_t*) {
121 errno = ENOSYS;
122 return -1;
123}
124
125int sem_unlink(const char*) {
126 errno = ENOSYS;
127 return -1;
128}
129
130// Decrement a semaphore's value atomically,
131// and return the old one. As a special case,
132// this returns immediately if the value is
133// negative (i.e. -1)
134static int __sem_dec(volatile uint32_t* sem) {
135 volatile int32_t* ptr = reinterpret_cast<volatile int32_t*>(sem);
136 uint32_t shared = (*sem & SEMCOUNT_SHARED_MASK);
137 uint32_t old_value, new_value;
138 int ret;
139
140 do {
141 old_value = (*sem & SEMCOUNT_VALUE_MASK);
142 ret = SEMCOUNT_TO_VALUE(old_value);
143 if (ret < 0) {
144 break;
145 }
146
147 new_value = SEMCOUNT_DECREMENT(old_value);
148 } while (__bionic_cmpxchg((old_value|shared), (new_value|shared), ptr) != 0);
149
150 return ret;
151}
152
153// Same as __sem_dec, but will not touch anything if the
154// value is already negative *or* 0. Returns the old value.
155static int __sem_trydec(volatile uint32_t* sem) {
156 volatile int32_t* ptr = reinterpret_cast<volatile int32_t*>(sem);
157 uint32_t shared = (*sem & SEMCOUNT_SHARED_MASK);
158 uint32_t old_value, new_value;
159 int ret;
160
161 do {
162 old_value = (*sem & SEMCOUNT_VALUE_MASK);
163 ret = SEMCOUNT_TO_VALUE(old_value);
164 if (ret <= 0) {
165 break;
166 }
167
168 new_value = SEMCOUNT_DECREMENT(old_value);
169 } while (__bionic_cmpxchg((old_value|shared), (new_value|shared), ptr) != 0);
170
171 return ret;
172}
173
174
175// "Increment" the value of a semaphore atomically and
176// return its old value. Note that this implements
177// the special case of "incrementing" any negative
178// value to +1 directly.
179//
180// NOTE: The value will _not_ wrap above SEM_VALUE_MAX
181static int __sem_inc(volatile uint32_t* sem) {
182 volatile int32_t* ptr = reinterpret_cast<volatile int32_t*>(sem);
183 uint32_t shared = (*sem & SEMCOUNT_SHARED_MASK);
184 uint32_t old_value, new_value;
185 int ret;
186
187 do {
188 old_value = (*sem & SEMCOUNT_VALUE_MASK);
189 ret = SEMCOUNT_TO_VALUE(old_value);
190
191 // Can't go higher than SEM_VALUE_MAX.
192 if (ret == SEM_VALUE_MAX) {
193 break;
194 }
195
196 // If the counter is negative, go directly to +1, otherwise just increment.
197 if (ret < 0) {
198 new_value = SEMCOUNT_ONE;
199 } else {
200 new_value = SEMCOUNT_INCREMENT(old_value);
201 }
202 } while (__bionic_cmpxchg((old_value|shared), (new_value|shared), ptr) != 0);
203
204 return ret;
205}
206
207int sem_wait(sem_t* sem) {
208 if (sem == NULL) {
209 errno = EINVAL;
210 return -1;
211 }
212
213 uint32_t shared = SEM_GET_SHARED(sem);
214
215 while (true) {
216 if (__sem_dec(&sem->count) > 0) {
217 ANDROID_MEMBAR_FULL();
218 return 0;
219 }
220
221 __futex_wait_ex(&sem->count, shared, shared|SEMCOUNT_MINUS_ONE, NULL);
222 }
223}
224
225int sem_timedwait(sem_t* sem, const timespec* abs_timeout) {
226 if (sem == NULL) {
227 errno = EINVAL;
228 return -1;
229 }
230
231 // POSIX says we need to try to decrement the semaphore
232 // before checking the timeout value. Note that if the
233 // value is currently 0, __sem_trydec() does nothing.
234 if (__sem_trydec(&sem->count) > 0) {
235 ANDROID_MEMBAR_FULL();
236 return 0;
237 }
238
239 // Check it as per POSIX.
240 if (abs_timeout == NULL || abs_timeout->tv_sec < 0 || abs_timeout->tv_nsec < 0 || abs_timeout->tv_nsec >= NS_PER_S) {
241 errno = EINVAL;
242 return -1;
243 }
244
245 uint32_t shared = SEM_GET_SHARED(sem);
246
247 while (true) {
248 // POSIX mandates CLOCK_REALTIME here.
249 timespec ts;
250 if (!timespec_from_absolute_timespec(ts, *abs_timeout, CLOCK_REALTIME)) {
251 errno = ETIMEDOUT;
252 return -1;
253 }
254
255 // Try to grab the semaphore. If the value was 0, this will also change it to -1.
256 if (__sem_dec(&sem->count) > 0) {
257 ANDROID_MEMBAR_FULL();
258 break;
259 }
260
261 // Contention detected. Wait for a wakeup event.
262 int ret = __futex_wait_ex(&sem->count, shared, shared|SEMCOUNT_MINUS_ONE, &ts);
263
264 // Return in case of timeout or interrupt.
265 if (ret == -ETIMEDOUT || ret == -EINTR) {
266 errno = -ret;
267 return -1;
268 }
269 }
270 return 0;
271}
272
273int sem_post(sem_t* sem) {
274 if (sem == NULL) {
275 return EINVAL;
276 }
277
278 uint32_t shared = SEM_GET_SHARED(sem);
279
280 ANDROID_MEMBAR_FULL();
281 int old_value = __sem_inc(&sem->count);
282 if (old_value < 0) {
283 // Contention on the semaphore. Wake up all waiters.
284 __futex_wake_ex(&sem->count, shared, INT_MAX);
285 } else if (old_value == SEM_VALUE_MAX) {
286 // Overflow detected.
287 errno = EOVERFLOW;
288 return -1;
289 }
290
291 return 0;
292}
293
294int sem_trywait(sem_t* sem) {
295 if (sem == NULL) {
296 errno = EINVAL;
297 return -1;
298 }
299
300 if (__sem_trydec(&sem->count) > 0) {
301 ANDROID_MEMBAR_FULL();
302 return 0;
303 } else {
304 errno = EAGAIN;
305 return -1;
306 }
307}
308
309int sem_getvalue(sem_t* sem, int* sval) {
310 if (sem == NULL || sval == NULL) {
311 errno = EINVAL;
312 return -1;
313 }
314
315 int val = SEMCOUNT_TO_VALUE(sem->count);
316 if (val < 0) {
317 val = 0;
318 }
319
320 *sval = val;
321 return 0;
322}