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