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 */
19 #include "semaphore.h"
20 #include "internals.h"
24 #include <shlib-compat.h>
25 #include <not-cancel.h>
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
)
61 volatile pthread_descr self
= thread_self();
62 pthread_extricate_if extr
;
63 int already_canceled
= 0;
64 int spurious_wakeup_count
;
66 /* Set up extrication interface */
68 extr
.pu_extricate_func
= new_sem_extricate_func
;
70 __pthread_lock(&sem
->__sem_lock
, self
);
71 if (sem
->__sem_value
> 0) {
73 __pthread_unlock(&sem
->__sem_lock
);
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
);
85 __pthread_unlock(&sem
->__sem_lock
);
87 if (already_canceled
) {
88 __pthread_set_own_extricate_if(self
, 0);
89 __pthread_do_exit(PTHREAD_CANCELED
, CURRENT_STACK_FRAME
);
92 /* Wait for sem_post or cancellation, or fall through if already canceled */
93 spurious_wakeup_count
= 0;
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
))
101 /* Count resumes that don't belong to us. */
102 spurious_wakeup_count
++;
107 __pthread_set_own_extricate_if(self
, 0);
109 /* Terminate only if the wakeup came from cancellation. */
110 /* Otherwise ignore cancellation because we got the semaphore. */
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
);
117 /* We got the semaphore */
121 int __new_sem_trywait(sem_t
* sem
)
125 __pthread_lock(&sem
->__sem_lock
, NULL
);
126 if (sem
->__sem_value
== 0) {
133 __pthread_unlock(&sem
->__sem_lock
);
137 int __new_sem_post(sem_t
* sem
)
139 pthread_descr self
= thread_self();
141 struct pthread_request request
;
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
) {
149 __pthread_unlock(&sem
->__sem_lock
);
153 __pthread_unlock(&sem
->__sem_lock
);
155 th
= dequeue(&sem
->__sem_waiting
);
156 __pthread_unlock(&sem
->__sem_lock
);
158 WRITE_MEMORY_BARRIER();
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) {
170 request
.req_kind
= REQ_POST
;
171 request
.req_args
.post
= sem
;
172 TEMP_FAILURE_RETRY(write_not_cancel(__pthread_manager_request
,
173 (char *) &request
, sizeof(request
)));
178 int __new_sem_getvalue(sem_t
* sem
, int * sval
)
180 *sval
= sem
->__sem_value
;
184 int __new_sem_destroy(sem_t
* sem
)
186 if (sem
->__sem_waiting
!= NULL
) {
193 sem_t
*sem_open(const char *name
, int oflag
, ...)
195 __set_errno (ENOSYS
);
199 int sem_close(sem_t
*sem
)
201 __set_errno (ENOSYS
);
205 int sem_unlink(const char *name
)
207 __set_errno (ENOSYS
);
211 int sem_timedwait(sem_t
*sem
, const struct timespec
*abstime
)
213 pthread_descr self
= thread_self();
214 pthread_extricate_if extr
;
215 int already_canceled
= 0;
216 int spurious_wakeup_count
;
218 __pthread_lock(&sem
->__sem_lock
, self
);
219 if (sem
->__sem_value
> 0) {
221 __pthread_unlock(&sem
->__sem_lock
);
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
);
233 /* Set up extrication interface */
234 extr
.pu_object
= sem
;
235 extr
.pu_extricate_func
= new_sem_extricate_func
;
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
);
245 already_canceled
= 1;
246 __pthread_unlock(&sem
->__sem_lock
);
248 if (already_canceled
) {
249 __pthread_set_own_extricate_if(self
, 0);
250 __pthread_do_exit(PTHREAD_CANCELED
, CURRENT_STACK_FRAME
);
253 spurious_wakeup_count
= 0;
256 if (timedsuspend(self
, abstime
) == 0) {
259 /* __pthread_lock will queue back any spurious restarts that
262 __pthread_lock(&sem
->__sem_lock
, self
);
263 was_on_queue
= remove_from_queue(&sem
->__sem_waiting
, self
);
264 __pthread_unlock(&sem
->__sem_lock
);
267 __pthread_set_own_extricate_if(self
, 0);
268 __set_errno (ETIMEDOUT
);
272 /* Eat the outstanding restart() from the signaller */
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
))
280 /* Count resumes that don't belong to us. */
281 spurious_wakeup_count
++;
287 __pthread_set_own_extricate_if(self
, 0);
289 /* Terminate only if the wakeup came from cancellation. */
290 /* Otherwise ignore cancellation because we got the semaphore. */
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
);
297 /* We got the semaphore */
302 versioned_symbol (libpthread
, __new_sem_init
, sem_init
, GLIBC_2_1
);
303 versioned_symbol (libpthread
, __new_sem_wait
, sem_wait
, GLIBC_2_1
);
304 versioned_symbol (libpthread
, __new_sem_trywait
, sem_trywait
, GLIBC_2_1
);
305 versioned_symbol (libpthread
, __new_sem_post
, sem_post
, GLIBC_2_1
);
306 versioned_symbol (libpthread
, __new_sem_getvalue
, sem_getvalue
, GLIBC_2_1
);
307 versioned_symbol (libpthread
, __new_sem_destroy
, sem_destroy
, GLIBC_2_1
);