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