]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/uclibc/lib/contrib/uclibc/libpthread/linuxthreads.old/semaphore.c
update
[l4.git] / l4 / pkg / uclibc / lib / contrib / uclibc / libpthread / linuxthreads.old / semaphore.c
1 /* Linuxthreads - a simple clone()-based implementation of Posix        */
2 /* threads for Linux.                                                   */
3 /* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr)              */
4 /*                                                                      */
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.               */
9 /*                                                                      */
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.                 */
14
15 /* Semaphores a la POSIX 1003.1b */
16
17 #include <features.h>
18 #include <limits.h>
19 #include <errno.h>
20 #include "pthread.h"
21 #include "semaphore.h"
22 #include "internals.h"
23 #include "spinlock.h"
24 #include "restart.h"
25 #include "queue.h"
26
27 int sem_init(sem_t *sem, int pshared, unsigned int value)
28 {
29   if (value > SEM_VALUE_MAX) {
30     errno = EINVAL;
31     return -1;
32   }
33   if (pshared) {
34     errno = ENOSYS;
35     return -1;
36   }
37   __pthread_init_lock(&sem->__sem_lock);
38   sem->__sem_value = value;
39   sem->__sem_waiting = NULL;
40   return 0;
41 }
42
43 /* Function called by pthread_cancel to remove the thread from
44    waiting inside sem_wait. */
45
46 static int new_sem_extricate_func(void *obj, pthread_descr th)
47 {
48   volatile pthread_descr self = thread_self();
49   sem_t *sem = obj;
50   int did_remove = 0;
51
52   __pthread_lock(&sem->__sem_lock, self);
53   did_remove = remove_from_queue(&sem->__sem_waiting, th);
54   __pthread_unlock(&sem->__sem_lock);
55
56   return did_remove;
57 }
58
59 int sem_wait(sem_t * sem)
60 {
61   volatile pthread_descr self = thread_self();
62   pthread_extricate_if extr;
63   int already_canceled = 0;
64   int spurious_wakeup_count;
65
66   /* Set up extrication interface */
67   extr.pu_object = sem;
68   extr.pu_extricate_func = new_sem_extricate_func;
69
70   __pthread_lock(&sem->__sem_lock, self);
71   if (sem->__sem_value > 0) {
72     sem->__sem_value--;
73     __pthread_unlock(&sem->__sem_lock);
74     return 0;
75   }
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);
83   else
84     already_canceled = 1;
85   __pthread_unlock(&sem->__sem_lock);
86
87   if (already_canceled) {
88     __pthread_set_own_extricate_if(self, 0);
89     __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
90   }
91
92   /* Wait for sem_post or cancellation, or fall through if already canceled */
93   spurious_wakeup_count = 0;
94   while (1)
95     {
96       suspend(self);
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))
100         {
101           /* Count resumes that don't belong to us. */
102           spurious_wakeup_count++;
103           continue;
104         }
105       break;
106     }
107   __pthread_set_own_extricate_if(self, 0);
108
109   /* Terminate only if the wakeup came from cancellation. */
110   /* Otherwise ignore cancellation because we got the semaphore. */
111
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);
116   }
117   /* We got the semaphore */
118   return 0;
119 }
120
121 int sem_trywait(sem_t * sem)
122 {
123   int retval;
124
125   __pthread_lock(&sem->__sem_lock, NULL);
126   if (sem->__sem_value == 0) {
127     errno = EAGAIN;
128     retval = -1;
129   } else {
130     sem->__sem_value--;
131     retval = 0;
132   }
133   __pthread_unlock(&sem->__sem_lock);
134   return retval;
135 }
136
137 int sem_post(sem_t * sem)
138 {
139   pthread_descr self = thread_self();
140   pthread_descr th;
141   struct pthread_request request;
142
143   if (THREAD_GETMEM(self, p_in_sighandler) == NULL) {
144     __pthread_lock(&sem->__sem_lock, self);
145     if (sem->__sem_waiting == NULL) {
146       if (sem->__sem_value >= SEM_VALUE_MAX) {
147         /* Overflow */
148         errno = ERANGE;
149         __pthread_unlock(&sem->__sem_lock);
150         return -1;
151       }
152       sem->__sem_value++;
153       __pthread_unlock(&sem->__sem_lock);
154     } else {
155       th = dequeue(&sem->__sem_waiting);
156       __pthread_unlock(&sem->__sem_lock);
157       th->p_sem_avail = 1;
158       WRITE_MEMORY_BARRIER();
159       restart(th);
160     }
161   } else {
162     /* If we're in signal handler, delegate post operation to
163        the thread manager. */
164     if (__pthread_manager_request < 0) {
165       if (__pthread_initialize_manager() < 0) {
166         errno = EAGAIN;
167         return -1;
168       }
169     }
170     request.req_kind = REQ_POST;
171     request.req_args.post = sem;
172     TEMP_FAILURE_RETRY(write(__pthread_manager_request,
173                                     (char *) &request, sizeof(request)));
174   }
175   return 0;
176 }
177
178 int sem_getvalue(sem_t * sem, int * sval)
179 {
180   *sval = sem->__sem_value;
181   return 0;
182 }
183
184 int sem_destroy(sem_t * sem)
185 {
186   if (sem->__sem_waiting != NULL) {
187     __set_errno (EBUSY);
188     return -1;
189   }
190   return 0;
191 }
192
193 sem_t *sem_open(const char *name attribute_unused, int oflag attribute_unused, ...)
194 {
195   __set_errno (ENOSYS);
196   return SEM_FAILED;
197 }
198
199 int sem_close(sem_t *sem attribute_unused)
200 {
201   __set_errno (ENOSYS);
202   return -1;
203 }
204
205 int sem_unlink(const char *name attribute_unused)
206 {
207   __set_errno (ENOSYS);
208   return -1;
209 }
210
211 int sem_timedwait(sem_t *sem, const struct timespec *abstime)
212 {
213   pthread_descr self = thread_self();
214   pthread_extricate_if extr;
215   int already_canceled = 0;
216   int spurious_wakeup_count;
217
218   __pthread_lock(&sem->__sem_lock, self);
219   if (sem->__sem_value > 0) {
220     --sem->__sem_value;
221     __pthread_unlock(&sem->__sem_lock);
222     return 0;
223   }
224
225   if (abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000) {
226     /* The standard requires that if the function would block and the
227        time value is illegal, the function returns with an error.  */
228     __pthread_unlock(&sem->__sem_lock);
229     __set_errno (EINVAL);
230     return -1;
231   }
232
233   /* Set up extrication interface */
234   extr.pu_object = sem;
235   extr.pu_extricate_func = new_sem_extricate_func;
236
237   /* Register extrication interface */
238   THREAD_SETMEM(self, p_sem_avail, 0);
239   __pthread_set_own_extricate_if(self, &extr);
240   /* Enqueue only if not already cancelled. */
241   if (!(THREAD_GETMEM(self, p_canceled)
242       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE))
243     enqueue(&sem->__sem_waiting, self);
244   else
245     already_canceled = 1;
246   __pthread_unlock(&sem->__sem_lock);
247
248   if (already_canceled) {
249     __pthread_set_own_extricate_if(self, 0);
250     __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
251   }
252
253   spurious_wakeup_count = 0;
254   while (1)
255     {
256       if (timedsuspend(self, abstime) == 0) {
257         int was_on_queue;
258
259         /* __pthread_lock will queue back any spurious restarts that
260            may happen to it. */
261
262         __pthread_lock(&sem->__sem_lock, self);
263         was_on_queue = remove_from_queue(&sem->__sem_waiting, self);
264         __pthread_unlock(&sem->__sem_lock);
265
266         if (was_on_queue) {
267           __pthread_set_own_extricate_if(self, 0);
268           __set_errno (ETIMEDOUT);
269           return -1;
270         }
271
272         /* Eat the outstanding restart() from the signaller */
273         suspend(self);
274       }
275
276       if (THREAD_GETMEM(self, p_sem_avail) == 0
277           && (THREAD_GETMEM(self, p_woken_by_cancel) == 0
278               || THREAD_GETMEM(self, p_cancelstate) != PTHREAD_CANCEL_ENABLE))
279         {
280           /* Count resumes that don't belong to us. */
281           spurious_wakeup_count++;
282           continue;
283         }
284       break;
285     }
286
287  __pthread_set_own_extricate_if(self, 0);
288
289   /* Terminate only if the wakeup came from cancellation. */
290   /* Otherwise ignore cancellation because we got the semaphore. */
291
292   if (THREAD_GETMEM(self, p_woken_by_cancel)
293       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) {
294     THREAD_SETMEM(self, p_woken_by_cancel, 0);
295     __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
296   }
297   /* We got the semaphore */
298   return 0;
299 }