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 */
20 #include "semaphore.h"
21 #include "internals.h"
26 int __new_sem_init(sem_t *sem, int pshared, unsigned int value);
27 int __new_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 __new_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 __new_sem_wait(sem_t * sem);
60 int __new_sem_wait(sem_t * sem)
62 volatile pthread_descr self = thread_self();
63 pthread_extricate_if extr;
64 int already_canceled = 0;
65 int spurious_wakeup_count;
67 /* Set up extrication interface */
69 extr.pu_extricate_func = new_sem_extricate_func;
71 __pthread_lock(&sem->__sem_lock, self);
72 if (sem->__sem_value > 0) {
74 __pthread_unlock(&sem->__sem_lock);
77 /* Register extrication interface */
78 THREAD_SETMEM(self, p_sem_avail, 0);
79 __pthread_set_own_extricate_if(self, &extr);
80 /* Enqueue only if not already cancelled. */
81 if (!(THREAD_GETMEM(self, p_canceled)
82 && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE))
83 enqueue(&sem->__sem_waiting, self);
86 __pthread_unlock(&sem->__sem_lock);
88 if (already_canceled) {
89 __pthread_set_own_extricate_if(self, 0);
90 __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
93 /* Wait for sem_post or cancellation, or fall through if already canceled */
94 spurious_wakeup_count = 0;
98 if (THREAD_GETMEM(self, p_sem_avail) == 0
99 && (THREAD_GETMEM(self, p_woken_by_cancel) == 0
100 || THREAD_GETMEM(self, p_cancelstate) != PTHREAD_CANCEL_ENABLE))
102 /* Count resumes that don't belong to us. */
103 spurious_wakeup_count++;
108 __pthread_set_own_extricate_if(self, 0);
110 /* Terminate only if the wakeup came from cancellation. */
111 /* Otherwise ignore cancellation because we got the semaphore. */
113 if (THREAD_GETMEM(self, p_woken_by_cancel)
114 && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) {
115 THREAD_SETMEM(self, p_woken_by_cancel, 0);
116 __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
118 /* We got the semaphore */
122 int __new_sem_trywait(sem_t * sem);
123 int __new_sem_trywait(sem_t * sem)
127 __pthread_lock(&sem->__sem_lock, NULL);
128 if (sem->__sem_value == 0) {
135 __pthread_unlock(&sem->__sem_lock);
139 int __new_sem_post(sem_t * sem);
140 int __new_sem_post(sem_t * sem)
142 pthread_descr self = thread_self();
144 struct pthread_request request;
146 if (THREAD_GETMEM(self, p_in_sighandler) == NULL) {
147 __pthread_lock(&sem->__sem_lock, self);
148 if (sem->__sem_waiting == NULL) {
149 if (sem->__sem_value >= SEM_VALUE_MAX) {
152 __pthread_unlock(&sem->__sem_lock);
156 __pthread_unlock(&sem->__sem_lock);
158 th = dequeue(&sem->__sem_waiting);
159 __pthread_unlock(&sem->__sem_lock);
161 WRITE_MEMORY_BARRIER();
165 /* If we're in signal handler, delegate post operation to
166 the thread manager. */
167 if (__pthread_manager_request < 0) {
168 if (__pthread_initialize_manager() < 0) {
173 request.req_kind = REQ_POST;
174 request.req_args.post = sem;
175 TEMP_FAILURE_RETRY(write(__pthread_manager_request,
176 (char *) &request, sizeof(request)));
181 int __new_sem_getvalue(sem_t * sem, int * sval);
182 int __new_sem_getvalue(sem_t * sem, int * sval)
184 *sval = sem->__sem_value;
188 int __new_sem_destroy(sem_t * sem);
189 int __new_sem_destroy(sem_t * sem)
191 if (sem->__sem_waiting != NULL) {
198 sem_t *sem_open(const char *name attribute_unused, int oflag attribute_unused, ...)
200 __set_errno (ENOSYS);
204 int sem_close(sem_t *sem attribute_unused)
206 __set_errno (ENOSYS);
210 int sem_unlink(const char *name attribute_unused)
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 */
307 weak_alias (__new_sem_init, sem_init)
308 weak_alias (__new_sem_wait, sem_wait)
309 weak_alias (__new_sem_trywait, sem_trywait)
310 weak_alias (__new_sem_post, sem_post)
311 weak_alias (__new_sem_getvalue, sem_getvalue)
312 weak_alias (__new_sem_destroy, sem_destroy)