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