blob: 39d049aea6bd04f73389da85ee1d73f277d955fd [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>
Andy McFaddenfcd00eb2010-05-28 13:31:45 -070033#include <cutils/atomic-inline.h>
David 'Digit' Turner6304d8b2010-06-02 18:12:12 -070034#include <bionic_futex.h>
35
36/* Use the lower 31-bits for the counter, and the high bit for
37 * the shared flag.
38 */
39#define SEM_VALUE_MASK 0x7fffffff
40#define SEM_SHARED_MASK 0x80000000
41
42#define SEM_GET_SHARED(sem) ((sem)->count & SEM_SHARED_MASK)
43#define SEM_GET_VALUE(sem) ((sem)->count & SEM_VALUE_MASK)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080044
45int sem_init(sem_t *sem, int pshared, unsigned int value)
46{
47 if (sem == NULL) {
48 errno = EINVAL;
49 return -1;
50 }
51
David 'Digit' Turner6304d8b2010-06-02 18:12:12 -070052 /* ensure that 'value' can be stored in the semaphore */
53 if ((value & SEM_VALUE_MASK) != value) {
54 errno = EINVAL;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080055 return -1;
56 }
57
58 sem->count = value;
David 'Digit' Turner6304d8b2010-06-02 18:12:12 -070059 if (pshared != 0)
60 sem->count |= SEM_SHARED_MASK;
61
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080062 return 0;
63}
64
65
66int sem_destroy(sem_t *sem)
67{
68 if (sem == NULL) {
69 errno = EINVAL;
70 return -1;
71 }
David 'Digit' Turner6304d8b2010-06-02 18:12:12 -070072 if ((sem->count & SEM_VALUE_MASK) == 0) {
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080073 errno = EBUSY;
74 return -1;
75 }
David 'Digit' Turner6304d8b2010-06-02 18:12:12 -070076 sem->count = 0;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080077 return 0;
78}
79
80
81sem_t *sem_open(const char *name, int oflag, ...)
82{
83 name=name;
84 oflag=oflag;
85
86 errno = ENOSYS;
87 return SEM_FAILED;
88}
89
90
91int sem_close(sem_t *sem)
92{
93 if (sem == NULL) {
94 errno = EINVAL;
95 return -1;
96 }
97 errno = ENOSYS;
98 return -1;
99}
100
101
102int sem_unlink(const char * name)
103{
104 errno = ENOSYS;
105 return -1;
106}
107
108
David 'Digit' Turner6304d8b2010-06-02 18:12:12 -0700109/* Return 0 if a semaphore's value is 0
110 * Otherwise, decrement the value and return the old value.
111 */
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800112static int
David 'Digit' Turner6304d8b2010-06-02 18:12:12 -0700113__sem_dec_if_positive(volatile unsigned int *pvalue)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800114{
David 'Digit' Turner6304d8b2010-06-02 18:12:12 -0700115 unsigned int shared = (*pvalue & SEM_SHARED_MASK);
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800116 unsigned int old;
117
118 do {
David 'Digit' Turner6304d8b2010-06-02 18:12:12 -0700119 old = (*pvalue & SEM_VALUE_MASK);
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800120 }
David 'Digit' Turner6304d8b2010-06-02 18:12:12 -0700121 while ( old != 0 &&
122 __atomic_cmpxchg((int)(old|shared),
123 (int)((old-1)|shared),
124 (volatile int*)pvalue) != 0 );
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800125
126 return old;
127}
128
David 'Digit' Turner6304d8b2010-06-02 18:12:12 -0700129/* Increment the value of a semaphore atomically.
130 * NOTE: the value will wrap above SEM_VALUE_MASK
131 */
132static int
133__sem_inc(volatile unsigned int *pvalue)
134{
135 unsigned int shared = (*pvalue & SEM_SHARED_MASK);
136 unsigned int old;
137
138 do {
139 old = (*pvalue & SEM_VALUE_MASK);
140 } while ( __atomic_cmpxchg((int)(old|shared),
141 (int)(((old+1)&SEM_VALUE_MASK)|shared),
142 (volatile int*)pvalue) != 0);
143 return old;
144}
145
Andy McFaddenfcd00eb2010-05-28 13:31:45 -0700146/* lock a semaphore */
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800147int sem_wait(sem_t *sem)
148{
David 'Digit' Turner6304d8b2010-06-02 18:12:12 -0700149 unsigned shared;
150
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800151 if (sem == NULL) {
152 errno = EINVAL;
153 return -1;
154 }
155
David 'Digit' Turner6304d8b2010-06-02 18:12:12 -0700156 shared = SEM_GET_SHARED(sem);
157
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800158 for (;;) {
David 'Digit' Turner6304d8b2010-06-02 18:12:12 -0700159 if (__sem_dec_if_positive(&sem->count))
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800160 break;
161
David 'Digit' Turner6304d8b2010-06-02 18:12:12 -0700162 __futex_wait_ex(&sem->count, shared, shared, NULL);
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800163 }
Andy McFaddenfcd00eb2010-05-28 13:31:45 -0700164 ANDROID_MEMBAR_FULL();
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800165 return 0;
166}
167
168int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout)
169{
170 int ret;
David 'Digit' Turner6304d8b2010-06-02 18:12:12 -0700171 unsigned int shared;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800172
173 if (sem == NULL) {
174 errno = EINVAL;
175 return -1;
176 }
177
178 /* POSIX says we need to try to decrement the semaphore
179 * before checking the timeout value */
David 'Digit' Turner6304d8b2010-06-02 18:12:12 -0700180 if (__sem_dec_if_positive(&sem->count)) {
Andy McFaddenfcd00eb2010-05-28 13:31:45 -0700181 ANDROID_MEMBAR_FULL();
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800182 return 0;
Andy McFaddenfcd00eb2010-05-28 13:31:45 -0700183 }
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800184
185 /* check it as per Posix */
186 if (abs_timeout == NULL ||
187 abs_timeout->tv_sec < 0 ||
188 abs_timeout->tv_nsec < 0 ||
189 abs_timeout->tv_nsec >= 1000000000)
190 {
191 errno = EINVAL;
192 return -1;
193 }
194
David 'Digit' Turner6304d8b2010-06-02 18:12:12 -0700195 shared = SEM_GET_SHARED(sem);
196
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800197 for (;;) {
198 struct timespec ts;
199 int ret;
200
201 /* Posix mandates CLOCK_REALTIME here */
202 clock_gettime( CLOCK_REALTIME, &ts );
203 ts.tv_sec = abs_timeout->tv_sec - ts.tv_sec;
204 ts.tv_nsec = abs_timeout->tv_nsec - ts.tv_nsec;
205 if (ts.tv_nsec < 0) {
206 ts.tv_nsec += 1000000000;
207 ts.tv_sec -= 1;
208 }
209
210 if (ts.tv_sec < 0 || ts.tv_nsec < 0) {
211 errno = ETIMEDOUT;
212 return -1;
213 }
214
David 'Digit' Turner6304d8b2010-06-02 18:12:12 -0700215 ret = __futex_wait_ex(&sem->count, shared, shared, &ts);
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800216
217 /* return in case of timeout or interrupt */
218 if (ret == -ETIMEDOUT || ret == -EINTR) {
219 errno = -ret;
220 return -1;
221 }
222
David 'Digit' Turner6304d8b2010-06-02 18:12:12 -0700223 if (__sem_dec_if_positive(&sem->count)) {
Andy McFaddenfcd00eb2010-05-28 13:31:45 -0700224 ANDROID_MEMBAR_FULL();
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800225 break;
Andy McFaddenfcd00eb2010-05-28 13:31:45 -0700226 }
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800227 }
228 return 0;
229}
230
Andy McFaddenfcd00eb2010-05-28 13:31:45 -0700231/* unlock a semaphore */
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800232int sem_post(sem_t *sem)
233{
David 'Digit' Turner6304d8b2010-06-02 18:12:12 -0700234 unsigned int shared;
235
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800236 if (sem == NULL)
237 return EINVAL;
238
David 'Digit' Turner6304d8b2010-06-02 18:12:12 -0700239 shared = SEM_GET_SHARED(sem);
240
Andy McFaddenfcd00eb2010-05-28 13:31:45 -0700241 ANDROID_MEMBAR_FULL();
David 'Digit' Turner6304d8b2010-06-02 18:12:12 -0700242 if (__sem_inc(&sem->count) >= 0)
243 __futex_wake_ex(&sem->count, shared, 1);
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800244
245 return 0;
246}
247
248int sem_trywait(sem_t *sem)
249{
250 if (sem == NULL) {
251 errno = EINVAL;
252 return -1;
253 }
254
David 'Digit' Turner6304d8b2010-06-02 18:12:12 -0700255 if (__sem_dec_if_positive(&sem->count) > 0) {
Andy McFaddenfcd00eb2010-05-28 13:31:45 -0700256 ANDROID_MEMBAR_FULL();
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800257 return 0;
258 } else {
David 'Digit' Turner294dd0b2010-02-12 12:18:37 -0800259 errno = EAGAIN;
260 return -1;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800261 }
262}
263
264int sem_getvalue(sem_t *sem, int *sval)
265{
266 if (sem == NULL || sval == NULL) {
267 errno = EINVAL;
268 return -1;
269 }
270
David 'Digit' Turner6304d8b2010-06-02 18:12:12 -0700271 *sval = SEM_GET_VALUE(sem);
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800272 return 0;
273}