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 <limits.h>
19 #include <errno.h>
20 #include "pthread.h"
21 #include "semaphore.h"
22 #include "internals.h"
23 #include "spinlock.h"
24 #include "restart.h"
25 #include "queue.h"
26 
sem_init(sem_t * sem,int pshared,unsigned int value)27 int 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 sem_wait. */
45 
new_sem_extricate_func(void * obj,pthread_descr th)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 
sem_wait(sem_t * sem)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 
65   /* Set up extrication interface */
66   extr.pu_object = sem;
67   extr.pu_extricate_func = new_sem_extricate_func;
68 
69   __pthread_lock(&sem->__sem_lock, self);
70   if (sem->__sem_value > 0) {
71     sem->__sem_value--;
72     __pthread_unlock(&sem->__sem_lock);
73     return 0;
74   }
75   /* Register extrication interface */
76   THREAD_SETMEM(self, p_sem_avail, 0);
77   __pthread_set_own_extricate_if(self, &extr);
78   /* Enqueue only if not already cancelled. */
79   if (!(THREAD_GETMEM(self, p_canceled)
80       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE))
81     enqueue(&sem->__sem_waiting, self);
82   else
83     already_canceled = 1;
84   __pthread_unlock(&sem->__sem_lock);
85 
86   if (already_canceled) {
87     __pthread_set_own_extricate_if(self, 0);
88     __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
89   }
90 
91   /* Wait for sem_post or cancellation, or fall through if already canceled */
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 	  /* Resume does not belong to us. */
100 	  continue;
101 	}
102       break;
103     }
104   __pthread_set_own_extricate_if(self, 0);
105 
106   /* Terminate only if the wakeup came from cancellation. */
107   /* Otherwise ignore cancellation because we got the semaphore. */
108 
109   if (THREAD_GETMEM(self, p_woken_by_cancel)
110       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) {
111     THREAD_SETMEM(self, p_woken_by_cancel, 0);
112     __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
113   }
114   /* We got the semaphore */
115   return 0;
116 }
117 
sem_trywait(sem_t * sem)118 int sem_trywait(sem_t * sem)
119 {
120   int retval;
121 
122   __pthread_lock(&sem->__sem_lock, NULL);
123   if (sem->__sem_value == 0) {
124     errno = EAGAIN;
125     retval = -1;
126   } else {
127     sem->__sem_value--;
128     retval = 0;
129   }
130   __pthread_unlock(&sem->__sem_lock);
131   return retval;
132 }
133 
sem_post(sem_t * sem)134 int sem_post(sem_t * sem)
135 {
136   pthread_descr self = thread_self();
137   pthread_descr th;
138   struct pthread_request request;
139 
140   if (THREAD_GETMEM(self, p_in_sighandler) == NULL) {
141     __pthread_lock(&sem->__sem_lock, self);
142     if (sem->__sem_waiting == NULL) {
143       if (sem->__sem_value >= SEM_VALUE_MAX) {
144         /* Overflow */
145         errno = ERANGE;
146         __pthread_unlock(&sem->__sem_lock);
147         return -1;
148       }
149       sem->__sem_value++;
150       __pthread_unlock(&sem->__sem_lock);
151     } else {
152       th = dequeue(&sem->__sem_waiting);
153       __pthread_unlock(&sem->__sem_lock);
154       th->p_sem_avail = 1;
155       WRITE_MEMORY_BARRIER();
156       restart(th);
157     }
158   } else {
159     /* If we're in signal handler, delegate post operation to
160        the thread manager. */
161     if (__pthread_manager_request < 0) {
162       if (__pthread_initialize_manager() < 0) {
163         errno = EAGAIN;
164         return -1;
165       }
166     }
167     request.req_kind = REQ_POST;
168     request.req_args.post = sem;
169     TEMP_FAILURE_RETRY(write(__pthread_manager_request,
170 				    (char *) &request, sizeof(request)));
171   }
172   return 0;
173 }
174 
sem_getvalue(sem_t * sem,int * sval)175 int sem_getvalue(sem_t * sem, int * sval)
176 {
177   *sval = sem->__sem_value;
178   return 0;
179 }
180 
sem_destroy(sem_t * sem)181 int sem_destroy(sem_t * sem)
182 {
183   if (sem->__sem_waiting != NULL) {
184     __set_errno (EBUSY);
185     return -1;
186   }
187   return 0;
188 }
189 
sem_open(const char * name attribute_unused,int oflag attribute_unused,...)190 sem_t *sem_open(const char *name attribute_unused, int oflag attribute_unused, ...)
191 {
192   __set_errno (ENOSYS);
193   return SEM_FAILED;
194 }
195 
sem_close(sem_t * sem attribute_unused)196 int sem_close(sem_t *sem attribute_unused)
197 {
198   __set_errno (ENOSYS);
199   return -1;
200 }
201 
sem_unlink(const char * name attribute_unused)202 int sem_unlink(const char *name attribute_unused)
203 {
204   __set_errno (ENOSYS);
205   return -1;
206 }
207 
sem_timedwait(sem_t * sem,const struct timespec * abstime)208 int sem_timedwait(sem_t *sem, const struct timespec *abstime)
209 {
210   pthread_descr self = thread_self();
211   pthread_extricate_if extr;
212   int already_canceled = 0;
213 
214   __pthread_lock(&sem->__sem_lock, self);
215   if (sem->__sem_value > 0) {
216     --sem->__sem_value;
217     __pthread_unlock(&sem->__sem_lock);
218     return 0;
219   }
220 
221   if (abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000) {
222     /* The standard requires that if the function would block and the
223        time value is illegal, the function returns with an error.  */
224     __pthread_unlock(&sem->__sem_lock);
225     __set_errno (EINVAL);
226     return -1;
227   }
228 
229   /* Set up extrication interface */
230   extr.pu_object = sem;
231   extr.pu_extricate_func = new_sem_extricate_func;
232 
233   /* Register extrication interface */
234   THREAD_SETMEM(self, p_sem_avail, 0);
235   __pthread_set_own_extricate_if(self, &extr);
236   /* Enqueue only if not already cancelled. */
237   if (!(THREAD_GETMEM(self, p_canceled)
238       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE))
239     enqueue(&sem->__sem_waiting, self);
240   else
241     already_canceled = 1;
242   __pthread_unlock(&sem->__sem_lock);
243 
244   if (already_canceled) {
245     __pthread_set_own_extricate_if(self, 0);
246     __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
247   }
248 
249   while (1)
250     {
251       if (timedsuspend(self, abstime) == 0) {
252 	int was_on_queue;
253 
254 	/* __pthread_lock will queue back any spurious restarts that
255 	   may happen to it. */
256 
257 	__pthread_lock(&sem->__sem_lock, self);
258 	was_on_queue = remove_from_queue(&sem->__sem_waiting, self);
259 	__pthread_unlock(&sem->__sem_lock);
260 
261 	if (was_on_queue) {
262 	  __pthread_set_own_extricate_if(self, 0);
263 	  __set_errno (ETIMEDOUT);
264 	  return -1;
265 	}
266 
267 	/* Eat the outstanding restart() from the signaller */
268 	suspend(self);
269       }
270 
271       if (THREAD_GETMEM(self, p_sem_avail) == 0
272 	  && (THREAD_GETMEM(self, p_woken_by_cancel) == 0
273 	      || THREAD_GETMEM(self, p_cancelstate) != PTHREAD_CANCEL_ENABLE))
274 	{
275 	  /* Resume does not belong to us. */
276 	  continue;
277 	}
278       break;
279     }
280 
281  __pthread_set_own_extricate_if(self, 0);
282 
283   /* Terminate only if the wakeup came from cancellation. */
284   /* Otherwise ignore cancellation because we got the semaphore. */
285 
286   if (THREAD_GETMEM(self, p_woken_by_cancel)
287       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) {
288     THREAD_SETMEM(self, p_woken_by_cancel, 0);
289     __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
290   }
291   /* We got the semaphore */
292   return 0;
293 }
294