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