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