2002-08-02 Roland McGrath <roland@redhat.com>
[kopensolaris-gnu/glibc.git] / linuxthreads / cancel.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 /* Thread cancellation */
16
17 #include <errno.h>
18 #include <rpc/rpc.h>
19 #include "pthread.h"
20 #include "internals.h"
21 #include "spinlock.h"
22 #include "restart.h"
23
24 #ifdef _STACK_GROWS_DOWN
25 # define FRAME_LEFT(frame, other) ((char *) frame >= (char *) other)
26 #elif _STACK_GROWS_UP
27 # define FRAME_LEFT(frame, other) ((char *) frame <= (char *) other)
28 #else
29 # error "Define either _STACK_GROWS_DOWN or _STACK_GROWS_UP"
30 #endif
31
32
33 int pthread_setcancelstate(int state, int * oldstate)
34 {
35   pthread_descr self = thread_self();
36   if (state < PTHREAD_CANCEL_ENABLE || state > PTHREAD_CANCEL_DISABLE)
37     return EINVAL;
38   if (oldstate != NULL) *oldstate = THREAD_GETMEM(self, p_cancelstate);
39   THREAD_SETMEM(self, p_cancelstate, state);
40   if (THREAD_GETMEM(self, p_canceled) &&
41       THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE &&
42       THREAD_GETMEM(self, p_canceltype) == PTHREAD_CANCEL_ASYNCHRONOUS)
43     __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
44   return 0;
45 }
46
47 int pthread_setcanceltype(int type, int * oldtype)
48 {
49   pthread_descr self = thread_self();
50   if (type < PTHREAD_CANCEL_DEFERRED || type > PTHREAD_CANCEL_ASYNCHRONOUS)
51     return EINVAL;
52   if (oldtype != NULL) *oldtype = THREAD_GETMEM(self, p_canceltype);
53   THREAD_SETMEM(self, p_canceltype, type);
54   if (THREAD_GETMEM(self, p_canceled) &&
55       THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE &&
56       THREAD_GETMEM(self, p_canceltype) == PTHREAD_CANCEL_ASYNCHRONOUS)
57     __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
58   return 0;
59 }
60
61 int pthread_cancel(pthread_t thread)
62 {
63   pthread_handle handle = thread_handle(thread);
64   int pid;
65   int dorestart = 0;
66   pthread_descr th;
67   pthread_extricate_if *pextricate;
68   int already_canceled;
69
70   __pthread_lock(&handle->h_lock, NULL);
71   if (invalid_handle(handle, thread)) {
72     __pthread_unlock(&handle->h_lock);
73     return ESRCH;
74   }
75
76   th = handle->h_descr;
77
78   already_canceled = th->p_canceled;
79   th->p_canceled = 1;
80
81   if (th->p_cancelstate == PTHREAD_CANCEL_DISABLE || already_canceled) {
82     __pthread_unlock(&handle->h_lock);
83     return 0;
84   }
85
86   pextricate = th->p_extricate;
87   pid = th->p_pid;
88
89   /* If the thread has registered an extrication interface, then
90      invoke the interface. If it returns 1, then we succeeded in
91      dequeuing the thread from whatever waiting object it was enqueued
92      with. In that case, it is our responsibility to wake it up.
93      And also to set the p_woken_by_cancel flag so the woken thread
94      can tell that it was woken by cancellation. */
95
96   if (pextricate != NULL) {
97     dorestart = pextricate->pu_extricate_func(pextricate->pu_object, th);
98     th->p_woken_by_cancel = dorestart;
99   }
100
101   __pthread_unlock(&handle->h_lock);
102
103   /* If the thread has suspended or is about to, then we unblock it by
104      issuing a restart, instead of a cancel signal. Otherwise we send
105      the cancel signal to unblock the thread from a cancellation point,
106      or to initiate asynchronous cancellation. The restart is needed so
107      we have proper accounting of restarts; suspend decrements the thread's
108      resume count, and restart() increments it.  This also means that suspend's
109      handling of the cancel signal is obsolete. */
110
111   if (dorestart)
112     restart(th);
113   else
114     kill(pid, __pthread_sig_cancel);
115
116   return 0;
117 }
118
119 void pthread_testcancel(void)
120 {
121   pthread_descr self = thread_self();
122   if (THREAD_GETMEM(self, p_canceled)
123       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE)
124     __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
125 }
126
127 void _pthread_cleanup_push(struct _pthread_cleanup_buffer * buffer,
128                            void (*routine)(void *), void * arg)
129 {
130   pthread_descr self = thread_self();
131   buffer->__routine = routine;
132   buffer->__arg = arg;
133   buffer->__prev = THREAD_GETMEM(self, p_cleanup);
134   if (buffer->__prev != NULL && FRAME_LEFT (buffer, buffer->__prev))
135     buffer->__prev = NULL;
136   THREAD_SETMEM(self, p_cleanup, buffer);
137 }
138
139 void _pthread_cleanup_pop(struct _pthread_cleanup_buffer * buffer,
140                           int execute)
141 {
142   pthread_descr self = thread_self();
143   if (execute) buffer->__routine(buffer->__arg);
144   THREAD_SETMEM(self, p_cleanup, buffer->__prev);
145 }
146
147 void _pthread_cleanup_push_defer(struct _pthread_cleanup_buffer * buffer,
148                                  void (*routine)(void *), void * arg)
149 {
150   pthread_descr self = thread_self();
151   buffer->__routine = routine;
152   buffer->__arg = arg;
153   buffer->__canceltype = THREAD_GETMEM(self, p_canceltype);
154   buffer->__prev = THREAD_GETMEM(self, p_cleanup);
155   if (buffer->__prev != NULL && FRAME_LEFT (buffer, buffer->__prev))
156     buffer->__prev = NULL;
157   THREAD_SETMEM(self, p_canceltype, PTHREAD_CANCEL_DEFERRED);
158   THREAD_SETMEM(self, p_cleanup, buffer);
159 }
160
161 void _pthread_cleanup_pop_restore(struct _pthread_cleanup_buffer * buffer,
162                                   int execute)
163 {
164   pthread_descr self = thread_self();
165   if (execute) buffer->__routine(buffer->__arg);
166   THREAD_SETMEM(self, p_cleanup, buffer->__prev);
167   THREAD_SETMEM(self, p_canceltype, buffer->__canceltype);
168   if (THREAD_GETMEM(self, p_canceled) &&
169       THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE &&
170       THREAD_GETMEM(self, p_canceltype) == PTHREAD_CANCEL_ASYNCHRONOUS)
171     __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
172 }
173
174 void __pthread_perform_cleanup(char *currentframe)
175 {
176   pthread_descr self = thread_self();
177   struct _pthread_cleanup_buffer *c = THREAD_GETMEM(self, p_cleanup);
178   struct _pthread_cleanup_buffer *last;
179
180   if (c != NULL)
181     while (FRAME_LEFT (currentframe, c))
182       {
183         last = c;
184         c = c->__prev;
185
186         if (c == NULL || FRAME_LEFT (last, c))
187           {
188             c = NULL;
189             break;
190           }
191       }
192
193   while (c != NULL)
194     {
195       c->__routine(c->__arg);
196
197       last = c;
198       c = c->__prev;
199
200       if (FRAME_LEFT (last, c))
201         break;
202     }
203
204   /* And the TSD which needs special help.  */
205 #if !(USE_TLS && HAVE___THREAD)
206   if (THREAD_GETMEM(self, p_libc_specific[_LIBC_TSD_KEY_RPC_VARS]) != NULL)
207     __rpc_thread_destroy ();
208 #else
209   if (__libc_tsd_get (RPC_VARS) != NULL)
210     __rpc_thread_destroy ();
211 #endif
212 }
213
214 #ifndef SHARED
215 /* We need a hook to force the cancelation wrappers and file locking
216    to be linked in when static libpthread is used.  */
217 extern const int __pthread_provide_wrappers;
218 static const int *const __pthread_require_wrappers =
219   &__pthread_provide_wrappers;
220 extern const int __pthread_provide_lockfile;
221 static const int *const __pthread_require_lockfile =
222   &__pthread_provide_lockfile;
223 #endif