Update.
[glibc.git] / linuxthreads / semaphore.c
bloba772ea5091d4639e8bf9d5d920e7a9bc9a3b1386
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. */
15 /* Semaphores a la POSIX 1003.1b */
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 #include <shlib-compat.h>
26 int __new_sem_init(sem_t *sem, int pshared, unsigned int value)
28 if (value > SEM_VALUE_MAX) {
29 errno = EINVAL;
30 return -1;
32 if (pshared) {
33 errno = ENOSYS;
34 return -1;
36 __pthread_init_lock((struct _pthread_fastlock *) &sem->__sem_lock);
37 sem->__sem_value = value;
38 sem->__sem_waiting = NULL;
39 return 0;
42 /* Function called by pthread_cancel to remove the thread from
43 waiting inside __new_sem_wait. */
45 static int new_sem_extricate_func(void *obj, pthread_descr th)
47 volatile pthread_descr self = thread_self();
48 sem_t *sem = obj;
49 int did_remove = 0;
51 __pthread_lock((struct _pthread_fastlock *) &sem->__sem_lock, self);
52 did_remove = remove_from_queue(&sem->__sem_waiting, th);
53 __pthread_unlock((struct _pthread_fastlock *) &sem->__sem_lock);
55 return did_remove;
58 int __new_sem_wait(sem_t * sem)
60 volatile pthread_descr self = thread_self();
61 pthread_extricate_if extr;
62 int already_canceled = 0;
64 /* Set up extrication interface */
65 extr.pu_object = sem;
66 extr.pu_extricate_func = new_sem_extricate_func;
68 __pthread_lock((struct _pthread_fastlock *) &sem->__sem_lock, self);
69 if (sem->__sem_value > 0) {
70 sem->__sem_value--;
71 __pthread_unlock((struct _pthread_fastlock *) &sem->__sem_lock);
72 return 0;
74 /* Register extrication interface */
75 __pthread_set_own_extricate_if(self, &extr);
76 /* Enqueue only if not already cancelled. */
77 if (!(THREAD_GETMEM(self, p_canceled)
78 && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE))
79 enqueue(&sem->__sem_waiting, self);
80 else
81 already_canceled = 1;
82 __pthread_unlock((struct _pthread_fastlock *) &sem->__sem_lock);
84 if (already_canceled) {
85 __pthread_set_own_extricate_if(self, 0);
86 pthread_exit(PTHREAD_CANCELED);
89 /* Wait for sem_post or cancellation, or fall through if already canceled */
90 suspend(self);
91 __pthread_set_own_extricate_if(self, 0);
93 /* Terminate only if the wakeup came from cancellation. */
94 /* Otherwise ignore cancellation because we got the semaphore. */
96 if (THREAD_GETMEM(self, p_woken_by_cancel)
97 && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) {
98 THREAD_SETMEM(self, p_woken_by_cancel, 0);
99 pthread_exit(PTHREAD_CANCELED);
101 /* We got the semaphore */
102 return 0;
105 int __new_sem_trywait(sem_t * sem)
107 int retval;
109 __pthread_lock((struct _pthread_fastlock *) &sem->__sem_lock, NULL);
110 if (sem->__sem_value == 0) {
111 errno = EAGAIN;
112 retval = -1;
113 } else {
114 sem->__sem_value--;
115 retval = 0;
117 __pthread_unlock((struct _pthread_fastlock *) &sem->__sem_lock);
118 return retval;
121 int __new_sem_post(sem_t * sem)
123 pthread_descr self = thread_self();
124 pthread_descr th;
125 struct pthread_request request;
127 if (THREAD_GETMEM(self, p_in_sighandler) == NULL) {
128 __pthread_lock((struct _pthread_fastlock *) &sem->__sem_lock, self);
129 if (sem->__sem_waiting == NULL) {
130 if (sem->__sem_value >= SEM_VALUE_MAX) {
131 /* Overflow */
132 errno = ERANGE;
133 __pthread_unlock((struct _pthread_fastlock *) &sem->__sem_lock);
134 return -1;
136 sem->__sem_value++;
137 __pthread_unlock((struct _pthread_fastlock *) &sem->__sem_lock);
138 } else {
139 th = dequeue(&sem->__sem_waiting);
140 __pthread_unlock((struct _pthread_fastlock *) &sem->__sem_lock);
141 restart(th);
143 } else {
144 /* If we're in signal handler, delegate post operation to
145 the thread manager. */
146 if (__pthread_manager_request < 0) {
147 if (__pthread_initialize_manager() < 0) {
148 errno = EAGAIN;
149 return -1;
152 request.req_kind = REQ_POST;
153 request.req_args.post = sem;
154 __libc_write(__pthread_manager_request,
155 (char *) &request, sizeof(request));
157 return 0;
160 int __new_sem_getvalue(sem_t * sem, int * sval)
162 *sval = sem->__sem_value;
163 return 0;
166 int __new_sem_destroy(sem_t * sem)
168 if (sem->__sem_waiting != NULL) {
169 __set_errno (EBUSY);
170 return -1;
172 return 0;
175 sem_t *sem_open(const char *name, int oflag, ...)
177 __set_errno (ENOSYS);
178 return SEM_FAILED;
181 int sem_close(sem_t *sem)
183 __set_errno (ENOSYS);
184 return -1;
187 int sem_unlink(const char *name)
189 __set_errno (ENOSYS);
190 return -1;
193 int sem_timedwait(sem_t *sem, const struct timespec *abstime)
195 pthread_descr self = thread_self();
196 pthread_extricate_if extr;
197 int already_canceled = 0;
199 __pthread_lock((struct _pthread_fastlock *) &sem->__sem_lock, self);
200 if (sem->__sem_value > 0) {
201 --sem->__sem_value;
202 __pthread_unlock((struct _pthread_fastlock *) &sem->__sem_lock);
203 return 0;
206 if (abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000) {
207 /* The standard requires that if the function would block and the
208 time value is illegal, the function returns with an error. */
209 __pthread_unlock((struct _pthread_fastlock *) &sem->__sem_lock);
210 return EINVAL;
213 /* Set up extrication interface */
214 extr.pu_object = sem;
215 extr.pu_extricate_func = new_sem_extricate_func;
217 /* Register extrication interface */
218 __pthread_set_own_extricate_if(self, &extr);
219 /* Enqueue only if not already cancelled. */
220 if (!(THREAD_GETMEM(self, p_canceled)
221 && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE))
222 enqueue(&sem->__sem_waiting, self);
223 else
224 already_canceled = 1;
225 __pthread_unlock((struct _pthread_fastlock *) &sem->__sem_lock);
227 if (already_canceled) {
228 __pthread_set_own_extricate_if(self, 0);
229 pthread_exit(PTHREAD_CANCELED);
232 if (timedsuspend(self, abstime) == 0) {
233 int was_on_queue;
235 /* __pthread_lock will queue back any spurious restarts that
236 may happen to it. */
238 __pthread_lock((struct _pthread_fastlock *)&sem->__sem_lock, self);
239 was_on_queue = remove_from_queue(&sem->__sem_waiting, self);
240 __pthread_unlock((struct _pthread_fastlock *)&sem->__sem_lock);
242 if (was_on_queue) {
243 __pthread_set_own_extricate_if(self, 0);
244 return ETIMEDOUT;
247 /* Eat the outstanding restart() from the signaller */
248 suspend(self);
250 __pthread_set_own_extricate_if(self, 0);
252 /* Terminate only if the wakeup came from cancellation. */
253 /* Otherwise ignore cancellation because we got the semaphore. */
255 if (THREAD_GETMEM(self, p_woken_by_cancel)
256 && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) {
257 THREAD_SETMEM(self, p_woken_by_cancel, 0);
258 pthread_exit(PTHREAD_CANCELED);
260 /* We got the semaphore */
261 return 0;
265 versioned_symbol (libpthread, __new_sem_init, sem_init, GLIBC_2_1);
266 versioned_symbol (libpthread, __new_sem_wait, sem_wait, GLIBC_2_1);
267 versioned_symbol (libpthread, __new_sem_trywait, sem_trywait, GLIBC_2_1);
268 versioned_symbol (libpthread, __new_sem_post, sem_post, GLIBC_2_1);
269 versioned_symbol (libpthread, __new_sem_getvalue, sem_getvalue, GLIBC_2_1);
270 versioned_symbol (libpthread, __new_sem_destroy, sem_destroy, GLIBC_2_1);