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