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