thr_cancel.c revision 117300
1/* 2 * David Leonard <d@openbsd.org>, 1999. Public domain. 3 * $FreeBSD: head/lib/libkse/thread/thr_cancel.c 117300 2003-07-07 04:28:23Z 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 (pthread->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 curthread->cancelflags &= ~PTHREAD_CANCEL_DISABLE; 150 if ((curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0) 151 need_exit = checkcancel(curthread); 152 ret = 0; 153 break; 154 case PTHREAD_CANCEL_DISABLE: 155 curthread->cancelflags |= PTHREAD_CANCEL_DISABLE; 156 ret = 0; 157 break; 158 default: 159 ret = EINVAL; 160 } 161 162 THR_SCHED_UNLOCK(curthread, curthread); 163 if (need_exit != 0) { 164 _thr_exit_cleanup(); 165 pthread_exit(PTHREAD_CANCELED); 166 PANIC("cancel"); 167 } 168 if (ret == 0 && oldstate != NULL) 169 *oldstate = ostate; 170 171 return (ret); 172} 173 174int 175_pthread_setcanceltype(int type, int *oldtype) 176{ 177 struct pthread *curthread = _get_curthread(); 178 int otype; 179 int ret; 180 int need_exit = 0; 181 182 /* Take the scheduling lock while fiddling with the state: */ 183 THR_SCHED_LOCK(curthread, curthread); 184 185 otype = curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS; 186 switch (type) { 187 case PTHREAD_CANCEL_ASYNCHRONOUS: 188 curthread->cancelflags |= PTHREAD_CANCEL_ASYNCHRONOUS; 189 need_exit = checkcancel(curthread); 190 ret = 0; 191 break; 192 case PTHREAD_CANCEL_DEFERRED: 193 curthread->cancelflags &= ~PTHREAD_CANCEL_ASYNCHRONOUS; 194 ret = 0; 195 break; 196 default: 197 ret = EINVAL; 198 } 199 200 THR_SCHED_UNLOCK(curthread, curthread); 201 if (need_exit != 0) { 202 _thr_exit_cleanup(); 203 pthread_exit(PTHREAD_CANCELED); 204 PANIC("cancel"); 205 } 206 if (ret == 0 && oldtype != NULL) 207 *oldtype = otype; 208 209 return (ret); 210} 211 212static int 213checkcancel(struct pthread *curthread) 214{ 215 if (((curthread->cancelflags & PTHREAD_CANCEL_DISABLE) == 0) && 216 ((curthread->cancelflags & THR_CANCELLING) != 0)) { 217 /* 218 * It is possible for this thread to be swapped out 219 * while performing cancellation; do not allow it 220 * to be cancelled again. 221 */ 222 curthread->cancelflags &= ~THR_CANCELLING; 223 return (1); 224 } 225 else 226 return (0); 227} 228 229static void 230testcancel(struct pthread *curthread) 231{ 232 /* Take the scheduling lock while fiddling with the state: */ 233 234 if (checkcancel(curthread) != 0) { 235 /* Unlock before exiting: */ 236 THR_SCHED_UNLOCK(curthread, curthread); 237 238 _thr_exit_cleanup(); 239 pthread_exit(PTHREAD_CANCELED); 240 PANIC("cancel"); 241 } 242} 243 244void 245_pthread_testcancel(void) 246{ 247 struct pthread *curthread = _get_curthread(); 248 249 THR_SCHED_LOCK(curthread, curthread); 250 testcancel(curthread); 251 THR_SCHED_UNLOCK(curthread, curthread); 252} 253 254void 255_thr_enter_cancellation_point(struct pthread *thread) 256{ 257 /* Look for a cancellation before we block: */ 258 THR_SCHED_LOCK(thread, thread); 259 testcancel(thread); 260 thread->cancelflags |= THR_AT_CANCEL_POINT; 261 THR_SCHED_UNLOCK(thread, thread); 262} 263 264void 265_thr_leave_cancellation_point(struct pthread *thread) 266{ 267 THR_SCHED_LOCK(thread, thread); 268 thread->cancelflags &= ~THR_AT_CANCEL_POINT; 269 /* Look for a cancellation after we unblock: */ 270 testcancel(thread); 271 THR_SCHED_UNLOCK(thread, thread); 272} 273 274static void 275finish_cancellation(void *arg) 276{ 277 struct pthread *curthread = _get_curthread(); 278 279 curthread->continuation = NULL; 280 curthread->interrupted = 0; 281 282 THR_SCHED_LOCK(curthread, curthread); 283 if ((curthread->cancelflags & THR_CANCEL_NEEDED) != 0) { 284 curthread->cancelflags &= ~THR_CANCEL_NEEDED; 285 THR_SCHED_UNLOCK(curthread, curthread); 286 _thr_exit_cleanup(); 287 pthread_exit(PTHREAD_CANCELED); 288 } 289 THR_SCHED_UNLOCK(curthread, curthread); 290} 291