semaphore.c revision fcd00ebbdf3e7f4e1e7782a65ae10fb0fc03a1aa
1/*
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>
33#include <cutils/atomic-inline.h>
34
35int sem_init(sem_t *sem, int pshared, unsigned int value)
36{
37    if (sem == NULL) {
38        errno = EINVAL;
39        return -1;
40    }
41
42    if (pshared != 0) {
43        errno = ENOSYS;
44        return -1;
45    }
46
47    sem->count = value;
48    return 0;
49}
50
51
52int sem_destroy(sem_t *sem)
53{
54    if (sem == NULL) {
55        errno = EINVAL;
56        return -1;
57    }
58    if (sem->count == 0) {
59        errno = EBUSY;
60        return -1;
61    }
62    return 0;
63}
64
65
66sem_t *sem_open(const char *name, int oflag, ...)
67{
68    name=name;
69    oflag=oflag;
70
71    errno = ENOSYS;
72    return SEM_FAILED;
73}
74
75
76int sem_close(sem_t *sem)
77{
78    if (sem == NULL) {
79        errno = EINVAL;
80        return -1;
81    }
82    errno = ENOSYS;
83    return -1;
84}
85
86
87int sem_unlink(const char * name)
88{
89    errno = ENOSYS;
90    return -1;
91}
92
93
94static int
95__atomic_dec_if_positive( volatile unsigned int*  pvalue )
96{
97    unsigned int  old;
98
99    do {
100        old = *pvalue;
101    }
102    while ( old != 0 && __atomic_cmpxchg( (int)old, (int)old-1, (volatile int*)pvalue ) != 0 );
103
104    return old;
105}
106
107/* lock a semaphore */
108int sem_wait(sem_t *sem)
109{
110    if (sem == NULL) {
111        errno = EINVAL;
112        return -1;
113    }
114
115    for (;;) {
116        if (__atomic_dec_if_positive(&sem->count))
117            break;
118
119        __futex_wait(&sem->count, 0, 0);
120    }
121    ANDROID_MEMBAR_FULL();
122    return 0;
123}
124
125int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout)
126{
127    int  ret;
128
129    if (sem == NULL) {
130        errno = EINVAL;
131        return -1;
132    }
133
134    /* POSIX says we need to try to decrement the semaphore
135     * before checking the timeout value */
136    if (__atomic_dec_if_positive(&sem->count)) {
137        ANDROID_MEMBAR_FULL();
138        return 0;
139    }
140
141    /* check it as per Posix */
142    if (abs_timeout == NULL    ||
143        abs_timeout->tv_sec < 0 ||
144        abs_timeout->tv_nsec < 0 ||
145        abs_timeout->tv_nsec >= 1000000000)
146    {
147        errno = EINVAL;
148        return -1;
149    }
150
151    for (;;) {
152        struct timespec ts;
153        int             ret;
154
155        /* Posix mandates CLOCK_REALTIME here */
156        clock_gettime( CLOCK_REALTIME, &ts );
157        ts.tv_sec  = abs_timeout->tv_sec - ts.tv_sec;
158        ts.tv_nsec = abs_timeout->tv_nsec - ts.tv_nsec;
159        if (ts.tv_nsec < 0) {
160            ts.tv_nsec += 1000000000;
161            ts.tv_sec  -= 1;
162        }
163
164        if (ts.tv_sec < 0 || ts.tv_nsec < 0) {
165            errno = ETIMEDOUT;
166            return -1;
167        }
168
169        ret = __futex_wait(&sem->count, 0, &ts);
170
171        /* return in case of timeout or interrupt */
172        if (ret == -ETIMEDOUT || ret == -EINTR) {
173            errno = -ret;
174            return -1;
175        }
176
177        if (__atomic_dec_if_positive(&sem->count)) {
178            ANDROID_MEMBAR_FULL();
179            break;
180        }
181    }
182    return 0;
183}
184
185/* unlock a semaphore */
186int sem_post(sem_t *sem)
187{
188    if (sem == NULL)
189        return EINVAL;
190
191    ANDROID_MEMBAR_FULL();
192    if (__atomic_inc((volatile int*)&sem->count) >= 0)
193        __futex_wake(&sem->count, 1);
194
195    return 0;
196}
197
198int  sem_trywait(sem_t *sem)
199{
200    if (sem == NULL) {
201        errno = EINVAL;
202        return -1;
203    }
204
205    if (__atomic_dec_if_positive(&sem->count) > 0) {
206        ANDROID_MEMBAR_FULL();
207        return 0;
208    } else {
209        errno = EAGAIN;
210        return -1;
211    }
212}
213
214int  sem_getvalue(sem_t *sem, int *sval)
215{
216    if (sem == NULL || sval == NULL) {
217        errno = EINVAL;
218        return -1;
219    }
220
221    *sval = sem->count;
222    return 0;
223}
224