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