thr_cancel.c revision 118510
1/* 2 * David Leonard <d@openbsd.org>, 1999. Public domain. 3 * $FreeBSD: head/lib/libkse/thread/thr_cancel.c 118510 2003-08-05 22:46:00Z deischen $ 4 */ 5#include <sys/errno.h> 6#include <pthread.h> 7#include "thr_private.h" 8 9__weak_reference(_pthread_cancel, pthread_cancel); 10__weak_reference(_pthread_setcancelstate, pthread_setcancelstate); 11__weak_reference(_pthread_setcanceltype, pthread_setcanceltype); 12__weak_reference(_pthread_testcancel, pthread_testcancel); 13 14static int checkcancel(struct pthread *curthread); 15static void testcancel(struct pthread *curthread); 16static void finish_cancellation(void *arg); 17 18int 19_pthread_cancel(pthread_t pthread) 20{ 21 struct pthread *curthread = _get_curthread(); 22 struct pthread *joinee = NULL; 23 struct kse_mailbox *kmbx = NULL; 24 int ret; 25 26 if ((ret = _thr_ref_add(curthread, pthread, /*include dead*/0)) == 0) { 27 /* 28 * Take the scheduling lock while we change the cancel flags. 29 */ 30 THR_SCHED_LOCK(curthread, pthread); 31 if (pthread->flags & THR_FLAGS_EXITING) { 32 THR_SCHED_UNLOCK(curthread, pthread); 33 _thr_ref_delete(curthread, pthread); 34 return (ESRCH); 35 } 36 if (((pthread->cancelflags & PTHREAD_CANCEL_DISABLE) != 0) || 37 (((pthread->cancelflags & THR_AT_CANCEL_POINT) == 0) && 38 ((pthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) == 0))) 39 /* Just mark it for cancellation: */ 40 pthread->cancelflags |= THR_CANCELLING; 41 else { 42 /* 43 * Check if we need to kick it back into the 44 * run queue: 45 */ 46 switch (pthread->state) { 47 case PS_RUNNING: 48 /* No need to resume: */ 49 pthread->cancelflags |= THR_CANCELLING; 50 break; 51 52 case PS_LOCKWAIT: 53 /* 54 * These can't be removed from the queue. 55 * Just mark it as cancelling and tell it 56 * to yield once it leaves the critical 57 * region. 58 */ 59 pthread->cancelflags |= THR_CANCELLING; 60 pthread->critical_yield = 1; 61 break; 62 63 case PS_SLEEP_WAIT: 64 case PS_SIGSUSPEND: 65 case PS_SIGWAIT: 66 /* Interrupt and resume: */ 67 pthread->interrupted = 1; 68 pthread->cancelflags |= THR_CANCELLING; 69 kmbx = _thr_setrunnable_unlocked(pthread); 70 break; 71 72 case PS_JOIN: 73 /* Disconnect the thread from the joinee: */ 74 joinee = pthread->join_status.thread; 75 pthread->join_status.thread = NULL; 76 pthread->cancelflags |= THR_CANCELLING; 77 kmbx = _thr_setrunnable_unlocked(pthread); 78 if ((joinee != NULL) && 79 (pthread->kseg == joinee->kseg)) { 80 /* Remove the joiner from the joinee. */ 81 joinee->joiner = NULL; 82 joinee = NULL; 83 } 84 break; 85 86 case PS_SUSPENDED: 87 case PS_MUTEX_WAIT: 88 case PS_COND_WAIT: 89 /* 90 * Threads in these states may be in queues. 91 * In order to preserve queue integrity, the 92 * cancelled thread must remove itself from the 93 * queue. Mark the thread as interrupted and 94 * needing cancellation, and set the state to 95 * running. When the thread resumes, it will 96 * remove itself from the queue and call the 97 * cancellation completion routine. 98 */ 99 pthread->interrupted = 1; 100 pthread->cancelflags |= THR_CANCEL_NEEDED; 101 kmbx = _thr_setrunnable_unlocked(pthread); 102 pthread->continuation = finish_cancellation; 103 break; 104 105 case PS_DEAD: 106 case PS_DEADLOCK: 107 case PS_STATE_MAX: 108 /* Ignore - only here to silence -Wall: */ 109 break; 110 } 111 if ((pthread->cancelflags & THR_AT_CANCEL_POINT) && 112 (pthread->blocked != 0 || 113 pthread->attr.flags & PTHREAD_SCOPE_SYSTEM)) 114 kse_thr_interrupt(&pthread->tcb->tcb_tmbx, 115 KSE_INTR_INTERRUPT, 0); 116 } 117 118 /* 119 * Release the thread's scheduling lock and remove the 120 * reference: 121 */ 122 THR_SCHED_UNLOCK(curthread, pthread); 123 _thr_ref_delete(curthread, pthread); 124 if (kmbx != NULL) 125 kse_wakeup(kmbx); 126 127 if ((joinee != NULL) && 128 (_thr_ref_add(curthread, joinee, /* include dead */1) == 0)) { 129 /* Remove the joiner from the joinee. */ 130 THR_SCHED_LOCK(curthread, joinee); 131 joinee->joiner = NULL; 132 THR_SCHED_UNLOCK(curthread, joinee); 133 _thr_ref_delete(curthread, joinee); 134 } 135 } 136 return (ret); 137} 138 139int 140_pthread_setcancelstate(int state, int *oldstate) 141{ 142 struct pthread *curthread = _get_curthread(); 143 int ostate; 144 int ret; 145 int need_exit = 0; 146 147 /* Take the scheduling lock while fiddling with the thread's state: */ 148 THR_SCHED_LOCK(curthread, curthread); 149 150 ostate = curthread->cancelflags & PTHREAD_CANCEL_DISABLE; 151 152 switch (state) { 153 case PTHREAD_CANCEL_ENABLE: 154 curthread->cancelflags &= ~PTHREAD_CANCEL_DISABLE; 155 if ((curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0) 156 need_exit = checkcancel(curthread); 157 ret = 0; 158 break; 159 case PTHREAD_CANCEL_DISABLE: 160 curthread->cancelflags |= PTHREAD_CANCEL_DISABLE; 161 ret = 0; 162 break; 163 default: 164 ret = EINVAL; 165 } 166 167 THR_SCHED_UNLOCK(curthread, curthread); 168 if (need_exit != 0) { 169 _thr_exit_cleanup(); 170 pthread_exit(PTHREAD_CANCELED); 171 PANIC("cancel"); 172 } 173 if (ret == 0 && oldstate != NULL) 174 *oldstate = ostate; 175 176 return (ret); 177} 178 179int 180_pthread_setcanceltype(int type, int *oldtype) 181{ 182 struct pthread *curthread = _get_curthread(); 183 int otype; 184 int ret; 185 int need_exit = 0; 186 187 /* Take the scheduling lock while fiddling with the state: */ 188 THR_SCHED_LOCK(curthread, curthread); 189 190 otype = curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS; 191 switch (type) { 192 case PTHREAD_CANCEL_ASYNCHRONOUS: 193 curthread->cancelflags |= PTHREAD_CANCEL_ASYNCHRONOUS; 194 need_exit = checkcancel(curthread); 195 ret = 0; 196 break; 197 case PTHREAD_CANCEL_DEFERRED: 198 curthread->cancelflags &= ~PTHREAD_CANCEL_ASYNCHRONOUS; 199 ret = 0; 200 break; 201 default: 202 ret = EINVAL; 203 } 204 205 THR_SCHED_UNLOCK(curthread, curthread); 206 if (need_exit != 0) { 207 _thr_exit_cleanup(); 208 pthread_exit(PTHREAD_CANCELED); 209 PANIC("cancel"); 210 } 211 if (ret == 0 && oldtype != NULL) 212 *oldtype = otype; 213 214 return (ret); 215} 216 217static int 218checkcancel(struct pthread *curthread) 219{ 220 if (((curthread->cancelflags & PTHREAD_CANCEL_DISABLE) == 0) && 221 ((curthread->cancelflags & THR_CANCELLING) != 0)) { 222 /* 223 * It is possible for this thread to be swapped out 224 * while performing cancellation; do not allow it 225 * to be cancelled again. 226 */ 227 curthread->cancelflags &= ~THR_CANCELLING; 228 return (1); 229 } 230 else 231 return (0); 232} 233 234static void 235testcancel(struct pthread *curthread) 236{ 237 /* Take the scheduling lock while fiddling with the state: */ 238 239 if (checkcancel(curthread) != 0) { 240 /* Unlock before exiting: */ 241 THR_SCHED_UNLOCK(curthread, curthread); 242 243 _thr_exit_cleanup(); 244 pthread_exit(PTHREAD_CANCELED); 245 PANIC("cancel"); 246 } 247} 248 249void 250_pthread_testcancel(void) 251{ 252 struct pthread *curthread = _get_curthread(); 253 254 THR_SCHED_LOCK(curthread, curthread); 255 testcancel(curthread); 256 THR_SCHED_UNLOCK(curthread, curthread); 257} 258 259void 260_thr_enter_cancellation_point(struct pthread *thread) 261{ 262 /* Look for a cancellation before we block: */ 263 THR_SCHED_LOCK(thread, thread); 264 testcancel(thread); 265 thread->cancelflags |= THR_AT_CANCEL_POINT; 266 THR_SCHED_UNLOCK(thread, thread); 267} 268 269void 270_thr_leave_cancellation_point(struct pthread *thread) 271{ 272 THR_SCHED_LOCK(thread, thread); 273 thread->cancelflags &= ~THR_AT_CANCEL_POINT; 274 /* Look for a cancellation after we unblock: */ 275 testcancel(thread); 276 THR_SCHED_UNLOCK(thread, thread); 277} 278 279static void 280finish_cancellation(void *arg) 281{ 282 struct pthread *curthread = _get_curthread(); 283 284 curthread->continuation = NULL; 285 curthread->interrupted = 0; 286 287 THR_SCHED_LOCK(curthread, curthread); 288 if ((curthread->cancelflags & THR_CANCEL_NEEDED) != 0) { 289 curthread->cancelflags &= ~THR_CANCEL_NEEDED; 290 THR_SCHED_UNLOCK(curthread, curthread); 291 _thr_exit_cleanup(); 292 pthread_exit(PTHREAD_CANCELED); 293 } 294 THR_SCHED_UNLOCK(curthread, curthread); 295} 296