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