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