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