OSDN Git Service

check if USE_TLS is defined before use
[uclinux-h8/uClibc.git] / 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 <errno.h>
19 #include "pthread.h"
20 #include "semaphore.h"
21 #include "internals.h"
22 #include "spinlock.h"
23 #include "restart.h"
24 #include "queue.h"
25
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)
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 __new_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 __new_sem_wait(sem_t * sem);
60 int __new_sem_wait(sem_t * sem)
61 {
62   volatile pthread_descr self = thread_self();
63   pthread_extricate_if extr;
64   int already_canceled = 0;
65   int spurious_wakeup_count;
66
67   /* Set up extrication interface */
68   extr.pu_object = sem;
69   extr.pu_extricate_func = new_sem_extricate_func;
70
71   __pthread_lock(&sem->__sem_lock, self);
72   if (sem->__sem_value > 0) {
73     sem->__sem_value--;
74     __pthread_unlock(&sem->__sem_lock);
75     return 0;
76   }
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);
84   else
85     already_canceled = 1;
86   __pthread_unlock(&sem->__sem_lock);
87
88   if (already_canceled) {
89     __pthread_set_own_extricate_if(self, 0);
90     __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
91   }
92
93   /* Wait for sem_post or cancellation, or fall through if already canceled */
94   spurious_wakeup_count = 0;
95   while (1)
96     {
97       suspend(self);
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))
101         {
102           /* Count resumes that don't belong to us. */
103           spurious_wakeup_count++;
104           continue;
105         }
106       break;
107     }
108   __pthread_set_own_extricate_if(self, 0);
109
110   /* Terminate only if the wakeup came from cancellation. */
111   /* Otherwise ignore cancellation because we got the semaphore. */
112
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);
117   }
118   /* We got the semaphore */
119   return 0;
120 }
121
122 int __new_sem_trywait(sem_t * sem);
123 int __new_sem_trywait(sem_t * sem)
124 {
125   int retval;
126
127   __pthread_lock(&sem->__sem_lock, NULL);
128   if (sem->__sem_value == 0) {
129     errno = EAGAIN;
130     retval = -1;
131   } else {
132     sem->__sem_value--;
133     retval = 0;
134   }
135   __pthread_unlock(&sem->__sem_lock);
136   return retval;
137 }
138
139 int __new_sem_post(sem_t * sem);
140 int __new_sem_post(sem_t * sem)
141 {
142   pthread_descr self = thread_self();
143   pthread_descr th;
144   struct pthread_request request;
145
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) {
150         /* Overflow */
151         errno = ERANGE;
152         __pthread_unlock(&sem->__sem_lock);
153         return -1;
154       }
155       sem->__sem_value++;
156       __pthread_unlock(&sem->__sem_lock);
157     } else {
158       th = dequeue(&sem->__sem_waiting);
159       __pthread_unlock(&sem->__sem_lock);
160       th->p_sem_avail = 1;
161       WRITE_MEMORY_BARRIER();
162       restart(th);
163     }
164   } else {
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) {
169         errno = EAGAIN;
170         return -1;
171       }
172     }
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)));
177   }
178   return 0;
179 }
180
181 int __new_sem_getvalue(sem_t * sem, int * sval);
182 int __new_sem_getvalue(sem_t * sem, int * sval)
183 {
184   *sval = sem->__sem_value;
185   return 0;
186 }
187
188 int __new_sem_destroy(sem_t * sem);
189 int __new_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 attribute_unused, int oflag attribute_unused, ...)
199 {
200   __set_errno (ENOSYS);
201   return SEM_FAILED;
202 }
203
204 int sem_close(sem_t *sem attribute_unused)
205 {
206   __set_errno (ENOSYS);
207   return -1;
208 }
209
210 int sem_unlink(const char *name attribute_unused)
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 }
305
306
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)
313