]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/uclibc/lib/libpthread/src/semaphore.c
update
[l4.git] / l4 / pkg / uclibc / lib / libpthread / src / 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 <errno.h>
18 #include "pthread.h"
19 #include "semaphore.h"
20 #include "internals.h"
21 #include "spinlock.h"
22 #include "restart.h"
23 #include "queue.h"
24
25 #include <bits/uClibc_errno.h>
26
27 int sem_init(sem_t *sem, int pshared, unsigned int value)
28 {
29   if (value > SEM_VALUE_MAX) {
30     __set_errno(EINVAL);
31     return -1;
32   }
33   if (pshared) {
34     __set_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     __set_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 #if 0 // AW11
142   struct pthread_request request;
143   if (THREAD_GETMEM(self, p_in_sighandler) == NULL)
144 #endif
145   {
146     __pthread_lock(&sem->__sem_lock, self);
147     if (sem->__sem_waiting == NULL) {
148       if (sem->__sem_value >= SEM_VALUE_MAX) {
149         /* Overflow */
150         __set_errno(ERANGE);
151         __pthread_unlock(&sem->__sem_lock);
152         return -1;
153       }
154       sem->__sem_value++;
155       __pthread_unlock(&sem->__sem_lock);
156     } else {
157       th = dequeue(&sem->__sem_waiting);
158       __pthread_unlock(&sem->__sem_lock);
159       th->p_sem_avail = 1;
160       WRITE_MEMORY_BARRIER();
161       restart(th);
162     }
163   }
164 #if 0 // AW11
165   else {
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) {
170         __set_errno(EAGAIN);
171         return -1;
172       }
173     }
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)));
178   }
179 #endif
180   return 0;
181 }
182
183 int sem_getvalue(sem_t * sem, int * sval)
184 {
185   *sval = sem->__sem_value;
186   return 0;
187 }
188
189 int sem_destroy(sem_t * sem)
190 {
191   if (sem->__sem_waiting != NULL) {
192     __set_errno (EBUSY);
193     return -1;
194   }
195   return 0;
196 }
197
198 sem_t *sem_open(const char *name, int oflag, ...)
199 {
200   __set_errno (ENOSYS);
201   return SEM_FAILED;
202 }
203
204 int sem_close(sem_t *sem)
205 {
206   __set_errno (ENOSYS);
207   return -1;
208 }
209
210 int sem_unlink(const char *name)
211 {
212   __set_errno (ENOSYS);
213   return -1;
214 }
215
216 int sem_timedwait(sem_t *sem, const struct timespec *abstime)
217 {
218   pthread_descr self = thread_self();
219   pthread_extricate_if extr;
220   int already_canceled = 0;
221   int spurious_wakeup_count;
222
223   __pthread_lock(&sem->__sem_lock, self);
224   if (sem->__sem_value > 0) {
225     --sem->__sem_value;
226     __pthread_unlock(&sem->__sem_lock);
227     return 0;
228   }
229
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);
235     return -1;
236   }
237
238   /* Set up extrication interface */
239   extr.pu_object = sem;
240   extr.pu_extricate_func = new_sem_extricate_func;
241
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);
249   else
250     already_canceled = 1;
251   __pthread_unlock(&sem->__sem_lock);
252
253   if (already_canceled) {
254     __pthread_set_own_extricate_if(self, 0);
255     __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
256   }
257
258   spurious_wakeup_count = 0;
259   while (1)
260     {
261       if (timedsuspend(self, abstime) == 0) {
262         int was_on_queue;
263
264         /* __pthread_lock will queue back any spurious restarts that
265            may happen to it. */
266
267         __pthread_lock(&sem->__sem_lock, self);
268         was_on_queue = remove_from_queue(&sem->__sem_waiting, self);
269         __pthread_unlock(&sem->__sem_lock);
270
271         if (was_on_queue) {
272           __pthread_set_own_extricate_if(self, 0);
273           __set_errno (ETIMEDOUT);
274           return -1;
275         }
276
277         /* Eat the outstanding restart() from the signaller */
278         suspend(self);
279       }
280
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))
284         {
285           /* Count resumes that don't belong to us. */
286           spurious_wakeup_count++;
287           continue;
288         }
289       break;
290     }
291
292  __pthread_set_own_extricate_if(self, 0);
293
294   /* Terminate only if the wakeup came from cancellation. */
295   /* Otherwise ignore cancellation because we got the semaphore. */
296
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);
301   }
302   /* We got the semaphore */
303   return 0;
304 }