Redesigned how cancellation unblocks a thread from internal
[kopensolaris-gnu/glibc.git] / linuxthreads / 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 <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
25 int __new_sem_init(sem_t *sem, int pshared, unsigned int value)
26 {
27   if (value > SEM_VALUE_MAX) {
28     errno = EINVAL;
29     return -1;
30   }
31   if (pshared) {
32     errno = ENOSYS;
33     return -1;
34   }
35   __pthread_init_lock((struct _pthread_fastlock *) &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 __new_sem_wait. */
43
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((struct _pthread_fastlock *) &sem->__sem_lock, self);
51   did_remove = remove_from_queue(&sem->__sem_waiting, th);
52   __pthread_unlock((struct _pthread_fastlock *) &sem->__sem_lock);
53
54   return did_remove;
55 }
56
57 int __new_sem_wait(sem_t * sem)
58 {
59   volatile pthread_descr self = thread_self();
60   pthread_extricate_if extr;
61   int already_canceled = 0;
62
63   /* Set up extrication interface */
64   extr.pu_object = sem;
65   extr.pu_extricate_func = new_sem_extricate_func;
66
67   __pthread_lock((struct _pthread_fastlock *) &sem->__sem_lock, self);
68   if (sem->__sem_value > 0) {
69     sem->__sem_value--;
70     __pthread_unlock((struct _pthread_fastlock *) &sem->__sem_lock);
71     return 0;
72   }
73   /* Register extrication interface */
74   __pthread_set_own_extricate_if(self, &extr); 
75   /* Enqueue only if not already cancelled. */
76   if (!(THREAD_GETMEM(self, p_canceled)
77       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE))
78     enqueue(&sem->__sem_waiting, self);
79   else
80     already_canceled = 1;
81   __pthread_unlock((struct _pthread_fastlock *) &sem->__sem_lock);
82
83   if (already_canceled) {
84     __pthread_set_own_extricate_if(self, 0); 
85     pthread_exit(PTHREAD_CANCELED);
86   } 
87
88   /* Wait for sem_post or cancellation, or fall through if already canceled */
89   suspend(self);
90   __pthread_set_own_extricate_if(self, 0); 
91
92   /* Terminate only if the wakeup came from cancellation. */
93   /* Otherwise ignore cancellation because we got the semaphore. */
94
95   if (THREAD_GETMEM(self, p_woken_by_cancel)
96       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) {
97     THREAD_SETMEM(self, p_woken_by_cancel, 0);
98     pthread_exit(PTHREAD_CANCELED);
99   }
100   /* We got the semaphore */
101   return 0;
102 }
103
104 int __new_sem_trywait(sem_t * sem)
105 {
106   int retval;
107
108   __pthread_lock((struct _pthread_fastlock *) &sem->__sem_lock, NULL);
109   if (sem->__sem_value == 0) {
110     errno = EAGAIN;
111     retval = -1;
112   } else {
113     sem->__sem_value--;
114     retval = 0;
115   }
116   __pthread_unlock((struct _pthread_fastlock *) &sem->__sem_lock);
117   return retval;
118 }
119
120 int __new_sem_post(sem_t * sem)
121 {
122   pthread_descr self = thread_self();
123   pthread_descr th;
124   struct pthread_request request;
125
126   if (THREAD_GETMEM(self, p_in_sighandler) == NULL) {
127     __pthread_lock((struct _pthread_fastlock *) &sem->__sem_lock, self);
128     if (sem->__sem_waiting == NULL) {
129       if (sem->__sem_value >= SEM_VALUE_MAX) {
130         /* Overflow */
131         errno = ERANGE;
132         __pthread_unlock((struct _pthread_fastlock *) &sem->__sem_lock);
133         return -1;
134       }
135       sem->__sem_value++;
136       __pthread_unlock((struct _pthread_fastlock *) &sem->__sem_lock);
137     } else {
138       th = dequeue(&sem->__sem_waiting);
139       __pthread_unlock((struct _pthread_fastlock *) &sem->__sem_lock);
140       restart(th);
141     }
142   } else {
143     /* If we're in signal handler, delegate post operation to
144        the thread manager. */
145     if (__pthread_manager_request < 0) {
146       if (__pthread_initialize_manager() < 0) {
147         errno = EAGAIN;
148         return -1;
149       }
150     }
151     request.req_kind = REQ_POST;
152     request.req_args.post = sem;
153     __libc_write(__pthread_manager_request,
154                  (char *) &request, sizeof(request));
155   }
156   return 0;
157 }
158
159 int __new_sem_getvalue(sem_t * sem, int * sval)
160 {
161   *sval = sem->__sem_value;
162   return 0;
163 }
164
165 int __new_sem_destroy(sem_t * sem)
166 {
167   if (sem->__sem_waiting != NULL) {
168     __set_errno (EBUSY);
169     return -1;
170   }
171   return 0;
172 }
173
174 sem_t *sem_open(const char *name, int oflag, ...)
175 {
176   __set_errno (ENOSYS);
177   return SEM_FAILED;
178 }
179
180 int sem_close(sem_t *sem)
181 {
182   __set_errno (ENOSYS);
183   return -1;
184 }
185
186 int sem_unlink(const char *name)
187 {
188   __set_errno (ENOSYS);
189   return -1;
190 }
191
192 #if defined PIC && DO_VERSIONING
193 default_symbol_version (__new_sem_init, sem_init, GLIBC_2.1);
194 default_symbol_version (__new_sem_wait, sem_wait, GLIBC_2.1);
195 default_symbol_version (__new_sem_trywait, sem_trywait, GLIBC_2.1);
196 default_symbol_version (__new_sem_post, sem_post, GLIBC_2.1);
197 default_symbol_version (__new_sem_getvalue, sem_getvalue, GLIBC_2.1);
198 default_symbol_version (__new_sem_destroy, sem_destroy, GLIBC_2.1);
199 #else
200 # ifdef weak_alias
201 weak_alias (__new_sem_init, sem_init)
202 weak_alias (__new_sem_wait, sem_wait)
203 weak_alias (__new_sem_trywait, sem_trywait)
204 weak_alias (__new_sem_post, sem_post)
205 weak_alias (__new_sem_getvalue, sem_getvalue)
206 weak_alias (__new_sem_destroy, sem_destroy)
207 # endif
208 #endif
209