]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - lib/libc_r/uthread/uthread_cancel.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / lib / libc_r / uthread / uthread_cancel.c
1 /*
2  * David Leonard <d@openbsd.org>, 1999. Public domain.
3  * $FreeBSD$
4  */
5 #include <sys/errno.h>
6 #include <pthread.h>
7 #include "pthread_private.h"
8
9 static void     finish_cancellation(void *arg);
10
11 __weak_reference(_pthread_cancel, pthread_cancel);
12 __weak_reference(_pthread_setcancelstate, pthread_setcancelstate);
13 __weak_reference(_pthread_setcanceltype, pthread_setcanceltype);
14 __weak_reference(_pthread_testcancel, pthread_testcancel);
15
16 int
17 _pthread_cancel(pthread_t pthread)
18 {
19         int ret;
20
21         if ((ret = _find_thread(pthread)) != 0) {
22                 /* NOTHING */
23         } else if (pthread->state == PS_DEAD || pthread->state == PS_DEADLOCK
24             || (pthread->flags & PTHREAD_EXITING) != 0) {
25                 ret = 0;
26         } else {
27                 /* Protect the scheduling queues: */
28                 _thread_kern_sig_defer();
29
30                 if (((pthread->cancelflags & PTHREAD_CANCEL_DISABLE) != 0) ||
31                     (((pthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) == 0) &&
32                     ((pthread->cancelflags & PTHREAD_AT_CANCEL_POINT) == 0)))
33                         /* Just mark it for cancellation: */
34                         pthread->cancelflags |= PTHREAD_CANCELLING;
35                 else {
36                         /*
37                          * Check if we need to kick it back into the
38                          * run queue:
39                          */
40                         switch (pthread->state) {
41                         case PS_RUNNING:
42                                 /* No need to resume: */
43                                 pthread->cancelflags |= PTHREAD_CANCELLING;
44                                 break;
45
46                         case PS_SPINBLOCK:
47                         case PS_FDR_WAIT:
48                         case PS_FDW_WAIT:
49                         case PS_POLL_WAIT:
50                         case PS_SELECT_WAIT:
51                                 /* Remove these threads from the work queue: */
52                                 if ((pthread->flags & PTHREAD_FLAGS_IN_WORKQ)
53                                     != 0)
54                                         PTHREAD_WORKQ_REMOVE(pthread);
55                                 /* Fall through: */
56                         case PS_SIGTHREAD:
57                         case PS_SLEEP_WAIT:
58                         case PS_WAIT_WAIT:
59                         case PS_SIGSUSPEND:
60                         case PS_SIGWAIT:
61                                 /* Interrupt and resume: */
62                                 pthread->interrupted = 1;
63                                 pthread->cancelflags |= PTHREAD_CANCELLING;
64                                 PTHREAD_NEW_STATE(pthread,PS_RUNNING);
65                                 break;
66
67                         case PS_JOIN:
68                                 /*
69                                  * Disconnect the thread from the joinee:
70                                  */
71                                 if (pthread->join_status.thread != NULL) {
72                                         pthread->join_status.thread->joiner
73                                             = NULL;
74                                         pthread->join_status.thread = NULL;
75                                 }
76                                 pthread->cancelflags |= PTHREAD_CANCELLING;
77                                 PTHREAD_NEW_STATE(pthread, PS_RUNNING);
78                                 break;
79
80                         case PS_SUSPENDED:
81                         case PS_MUTEX_WAIT:
82                         case PS_COND_WAIT:
83                         case PS_FDLR_WAIT:
84                         case PS_FDLW_WAIT:
85                         case PS_FILE_WAIT:
86                                 /*
87                                  * Threads in these states may be in queues.
88                                  * In order to preserve queue integrity, the
89                                  * cancelled thread must remove itself from the
90                                  * queue.  Mark the thread as interrupted and
91                                  * needing cancellation, and set the state to
92                                  * running.  When the thread resumes, it will
93                                  * remove itself from the queue and call the
94                                  * cancellation completion routine.
95                                  */
96                                 pthread->interrupted = 1;
97                                 pthread->cancelflags |= PTHREAD_CANCEL_NEEDED;
98                                 PTHREAD_NEW_STATE(pthread, PS_RUNNING);
99                                 pthread->continuation = finish_cancellation;
100                                 break;
101
102                         case PS_DEAD:
103                         case PS_DEADLOCK:
104                         case PS_STATE_MAX:
105                                 /* Ignore - only here to silence -Wall: */
106                                 break;
107                         }
108                 }
109
110                 /* Unprotect the scheduling queues: */
111                 _thread_kern_sig_undefer();
112
113                 ret = 0;
114         }
115         return (ret);
116 }
117
118 int
119 _pthread_setcancelstate(int state, int *oldstate)
120 {
121         struct pthread  *curthread = _get_curthread();
122         int ostate;
123         int ret;
124
125         ostate = curthread->cancelflags & PTHREAD_CANCEL_DISABLE;
126
127         switch (state) {
128         case PTHREAD_CANCEL_ENABLE:
129                 if (oldstate != NULL)
130                         *oldstate = ostate;
131                 curthread->cancelflags &= ~PTHREAD_CANCEL_DISABLE;
132                 if ((curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0)
133                         pthread_testcancel();
134                 ret = 0;
135                 break;
136         case PTHREAD_CANCEL_DISABLE:
137                 if (oldstate != NULL)
138                         *oldstate = ostate;
139                 curthread->cancelflags |= PTHREAD_CANCEL_DISABLE;
140                 ret = 0;
141                 break;
142         default:
143                 ret = EINVAL;
144         }
145
146         return (ret);
147 }
148
149 int
150 _pthread_setcanceltype(int type, int *oldtype)
151 {
152         struct pthread  *curthread = _get_curthread();
153         int otype;
154         int ret;
155
156         otype = curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS;
157         switch (type) {
158         case PTHREAD_CANCEL_ASYNCHRONOUS:
159                 if (oldtype != NULL)
160                         *oldtype = otype;
161                 curthread->cancelflags |= PTHREAD_CANCEL_ASYNCHRONOUS;
162                 pthread_testcancel();
163                 ret = 0;
164                 break;
165         case PTHREAD_CANCEL_DEFERRED:
166                 if (oldtype != NULL)
167                         *oldtype = otype;
168                 curthread->cancelflags &= ~PTHREAD_CANCEL_ASYNCHRONOUS;
169                 ret = 0;
170                 break;
171         default:
172                 ret = EINVAL;
173         }
174
175         return (ret);
176 }
177
178 void
179 _pthread_testcancel(void)
180 {
181         struct pthread  *curthread = _get_curthread();
182
183         if (((curthread->cancelflags & PTHREAD_CANCEL_DISABLE) == 0) &&
184             ((curthread->cancelflags & PTHREAD_CANCELLING) != 0) &&
185             ((curthread->flags & PTHREAD_EXITING) == 0)) {
186                 /*
187                  * It is possible for this thread to be swapped out
188                  * while performing cancellation; do not allow it
189                  * to be cancelled again.
190                  */
191                 curthread->cancelflags &= ~PTHREAD_CANCELLING;
192                 _thread_exit_cleanup();
193                 pthread_exit(PTHREAD_CANCELED);
194                 PANIC("cancel");
195         }
196 }
197
198 void
199 _thread_enter_cancellation_point(void)
200 {
201         struct pthread  *curthread = _get_curthread();
202
203         /* Look for a cancellation before we block: */
204         pthread_testcancel();
205         curthread->cancelflags |= PTHREAD_AT_CANCEL_POINT;
206 }
207
208 void
209 _thread_leave_cancellation_point(void)
210 {
211         struct pthread  *curthread = _get_curthread();
212
213         curthread->cancelflags &= ~PTHREAD_AT_CANCEL_POINT;
214         /* Look for a cancellation after we unblock: */
215         pthread_testcancel();
216 }
217
218 static void
219 finish_cancellation(void *arg)
220 {
221         struct pthread  *curthread = _get_curthread();
222
223         curthread->continuation = NULL;
224         curthread->interrupted = 0;
225
226         if ((curthread->cancelflags & PTHREAD_CANCEL_NEEDED) != 0) {
227                 curthread->cancelflags &= ~PTHREAD_CANCEL_NEEDED;
228                 _thread_exit_cleanup();
229                 pthread_exit(PTHREAD_CANCELED);
230         }
231 }