1 /* Linuxthreads - a simple clone()-based implementation of Posix */
2 /* threads for Linux. */
3 /* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr) */
5 /* This program is free software; you can redistribute it and/or */
6 /* modify it under the terms of the GNU Library General Public License */
7 /* as published by the Free Software Foundation; either version 2 */
8 /* of the License, or (at your option) any later version. */
10 /* This program is distributed in the hope that it will be useful, */
11 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
12 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
13 /* GNU Library General Public License for more details. */
15 /* Semaphores a la POSIX 1003.1b */
19 #include "semaphore.h"
20 #include "internals.h"
25 #include <bits/uClibc_errno.h>
27 int sem_init(sem_t *sem, int pshared, unsigned int value)
29 if (value > SEM_VALUE_MAX) {
37 __pthread_init_lock(&sem->__sem_lock);
38 sem->__sem_value = value;
39 sem->__sem_waiting = NULL;
43 /* Function called by pthread_cancel to remove the thread from
44 waiting inside sem_wait. */
46 static int new_sem_extricate_func(void *obj, pthread_descr th)
48 __volatile__ pthread_descr self = thread_self();
52 __pthread_lock(&sem->__sem_lock, self);
53 did_remove = remove_from_queue(&sem->__sem_waiting, th);
54 __pthread_unlock(&sem->__sem_lock);
59 int sem_wait(sem_t * sem)
61 __volatile__ pthread_descr self = thread_self();
62 pthread_extricate_if extr;
63 int already_canceled = 0;
64 int spurious_wakeup_count;
66 /* Set up extrication interface */
68 extr.pu_extricate_func = new_sem_extricate_func;
70 __pthread_lock(&sem->__sem_lock, self);
71 if (sem->__sem_value > 0) {
73 __pthread_unlock(&sem->__sem_lock);
76 /* Register extrication interface */
77 THREAD_SETMEM(self, p_sem_avail, 0);
78 __pthread_set_own_extricate_if(self, &extr);
79 /* Enqueue only if not already cancelled. */
80 if (!(THREAD_GETMEM(self, p_canceled)
81 && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE))
82 enqueue(&sem->__sem_waiting, self);
85 __pthread_unlock(&sem->__sem_lock);
87 if (already_canceled) {
88 __pthread_set_own_extricate_if(self, 0);
89 __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
92 /* Wait for sem_post or cancellation, or fall through if already canceled */
93 spurious_wakeup_count = 0;
97 if (THREAD_GETMEM(self, p_sem_avail) == 0
98 && (THREAD_GETMEM(self, p_woken_by_cancel) == 0
99 || THREAD_GETMEM(self, p_cancelstate) != PTHREAD_CANCEL_ENABLE))
101 /* Count resumes that don't belong to us. */
102 spurious_wakeup_count++;
107 __pthread_set_own_extricate_if(self, 0);
109 /* Terminate only if the wakeup came from cancellation. */
110 /* Otherwise ignore cancellation because we got the semaphore. */
112 if (THREAD_GETMEM(self, p_woken_by_cancel)
113 && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) {
114 THREAD_SETMEM(self, p_woken_by_cancel, 0);
115 __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
117 /* We got the semaphore */
121 int sem_trywait(sem_t * sem)
125 __pthread_lock(&sem->__sem_lock, NULL);
126 if (sem->__sem_value == 0) {
133 __pthread_unlock(&sem->__sem_lock);
137 int sem_post(sem_t * sem)
139 pthread_descr self = thread_self();
142 struct pthread_request request;
143 if (THREAD_GETMEM(self, p_in_sighandler) == NULL)
146 __pthread_lock(&sem->__sem_lock, self);
147 if (sem->__sem_waiting == NULL) {
148 if (sem->__sem_value >= SEM_VALUE_MAX) {
151 __pthread_unlock(&sem->__sem_lock);
155 __pthread_unlock(&sem->__sem_lock);
157 th = dequeue(&sem->__sem_waiting);
158 __pthread_unlock(&sem->__sem_lock);
160 WRITE_MEMORY_BARRIER();
166 /* If we're in signal handler, delegate post operation to
167 the thread manager. */
168 if (__pthread_manager_request < 0) {
169 if (__pthread_initialize_manager() < 0) {
174 request.req_kind = REQ_POST;
175 request.req_args.post = sem;
176 TEMP_FAILURE_RETRY(write_not_cancel(__pthread_manager_request,
177 (char *) &request, sizeof(request)));
183 int sem_getvalue(sem_t * sem, int * sval)
185 *sval = sem->__sem_value;
189 int sem_destroy(sem_t * sem)
191 if (sem->__sem_waiting != NULL) {
198 sem_t *sem_open(const char *name, int oflag, ...)
200 __set_errno (ENOSYS);
204 int sem_close(sem_t *sem)
206 __set_errno (ENOSYS);
210 int sem_unlink(const char *name)
212 __set_errno (ENOSYS);
216 int sem_timedwait(sem_t *sem, const struct timespec *abstime)
218 pthread_descr self = thread_self();
219 pthread_extricate_if extr;
220 int already_canceled = 0;
221 int spurious_wakeup_count;
223 __pthread_lock(&sem->__sem_lock, self);
224 if (sem->__sem_value > 0) {
226 __pthread_unlock(&sem->__sem_lock);
230 if (abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000) {
231 /* The standard requires that if the function would block and the
232 time value is illegal, the function returns with an error. */
233 __pthread_unlock(&sem->__sem_lock);
234 __set_errno (EINVAL);
238 /* Set up extrication interface */
239 extr.pu_object = sem;
240 extr.pu_extricate_func = new_sem_extricate_func;
242 /* Register extrication interface */
243 THREAD_SETMEM(self, p_sem_avail, 0);
244 __pthread_set_own_extricate_if(self, &extr);
245 /* Enqueue only if not already cancelled. */
246 if (!(THREAD_GETMEM(self, p_canceled)
247 && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE))
248 enqueue(&sem->__sem_waiting, self);
250 already_canceled = 1;
251 __pthread_unlock(&sem->__sem_lock);
253 if (already_canceled) {
254 __pthread_set_own_extricate_if(self, 0);
255 __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
258 spurious_wakeup_count = 0;
261 if (timedsuspend(self, abstime) == 0) {
264 /* __pthread_lock will queue back any spurious restarts that
267 __pthread_lock(&sem->__sem_lock, self);
268 was_on_queue = remove_from_queue(&sem->__sem_waiting, self);
269 __pthread_unlock(&sem->__sem_lock);
272 __pthread_set_own_extricate_if(self, 0);
273 __set_errno (ETIMEDOUT);
277 /* Eat the outstanding restart() from the signaller */
281 if (THREAD_GETMEM(self, p_sem_avail) == 0
282 && (THREAD_GETMEM(self, p_woken_by_cancel) == 0
283 || THREAD_GETMEM(self, p_cancelstate) != PTHREAD_CANCEL_ENABLE))
285 /* Count resumes that don't belong to us. */
286 spurious_wakeup_count++;
292 __pthread_set_own_extricate_if(self, 0);
294 /* Terminate only if the wakeup came from cancellation. */
295 /* Otherwise ignore cancellation because we got the semaphore. */
297 if (THREAD_GETMEM(self, p_woken_by_cancel)
298 && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) {
299 THREAD_SETMEM(self, p_woken_by_cancel, 0);
300 __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
302 /* We got the semaphore */