thr_cancel.c revision 87988
1/* 2 * David Leonard <d@openbsd.org>, 1999. Public domain. 3 * $FreeBSD: head/lib/libkse/thread/thr_cancel.c 87988 2001-12-15 15:52:24Z deischen $ 4 */ 5#include <sys/errno.h> 6#include <pthread.h> 7#include "pthread_private.h" 8 9static void finish_cancellation(void *arg); 10 11__weak_reference(_pthread_cancel, pthread_cancel); 12__weak_reference(_pthread_setcancelstate, pthread_setcancelstate); 13__weak_reference(_pthread_setcanceltype, pthread_setcanceltype); 14__weak_reference(_pthread_testcancel, pthread_testcancel); 15 16int 17_pthread_cancel(pthread_t pthread) 18{ 19 int ret; 20 21 if ((ret = _find_thread(pthread)) != 0) { 22 /* NOTHING */ 23 } else if (pthread->state == PS_DEAD || pthread->state == PS_DEADLOCK) { 24 ret = 0; 25 } else { 26 /* Protect the scheduling queues: */ 27 _thread_kern_sig_defer(); 28 29 if (((pthread->cancelflags & PTHREAD_CANCEL_DISABLE) != 0) || 30 (((pthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) == 0) && 31 ((pthread->cancelflags & PTHREAD_AT_CANCEL_POINT) == 0))) 32 /* Just mark it for cancellation: */ 33 pthread->cancelflags |= PTHREAD_CANCELLING; 34 else { 35 /* 36 * Check if we need to kick it back into the 37 * run queue: 38 */ 39 switch (pthread->state) { 40 case PS_RUNNING: 41 /* No need to resume: */ 42 pthread->cancelflags |= PTHREAD_CANCELLING; 43 break; 44 45 case PS_SPINBLOCK: 46 case PS_FDR_WAIT: 47 case PS_FDW_WAIT: 48 case PS_POLL_WAIT: 49 case PS_SELECT_WAIT: 50 /* Remove these threads from the work queue: */ 51 if ((pthread->flags & PTHREAD_FLAGS_IN_WORKQ) 52 != 0) 53 PTHREAD_WORKQ_REMOVE(pthread); 54 /* Fall through: */ 55 case PS_SIGTHREAD: 56 case PS_SLEEP_WAIT: 57 case PS_WAIT_WAIT: 58 case PS_SIGSUSPEND: 59 case PS_SIGWAIT: 60 /* Interrupt and resume: */ 61 pthread->interrupted = 1; 62 pthread->cancelflags |= PTHREAD_CANCELLING; 63 PTHREAD_NEW_STATE(pthread,PS_RUNNING); 64 break; 65 66 case PS_JOIN: 67 /* 68 * Disconnect the thread from the joinee and 69 * detach: 70 */ 71 if (pthread->join_status.thread != NULL) { 72 pthread->join_status.thread->joiner 73 = NULL; 74 pthread_detach((pthread_t) 75 pthread->join_status.thread); 76 } 77 pthread->cancelflags |= PTHREAD_CANCELLING; 78 PTHREAD_NEW_STATE(pthread, PS_RUNNING); 79 break; 80 81 case PS_SUSPENDED: 82 if (pthread->suspended == SUSP_NO || 83 pthread->suspended == SUSP_YES || 84 pthread->suspended == SUSP_JOIN || 85 pthread->suspended == SUSP_NOWAIT) { 86 /* 87 * This thread isn't in any scheduling 88 * queues; just change it's state: 89 */ 90 pthread->cancelflags |= 91 PTHREAD_CANCELLING; 92 PTHREAD_SET_STATE(pthread, PS_RUNNING); 93 break; 94 } 95 /* FALLTHROUGH */ 96 case PS_MUTEX_WAIT: 97 case PS_COND_WAIT: 98 case PS_FDLR_WAIT: 99 case PS_FDLW_WAIT: 100 case PS_FILE_WAIT: 101 /* 102 * Threads in these states may be in queues. 103 * In order to preserve queue integrity, the 104 * cancelled thread must remove itself from the 105 * queue. Mark the thread as interrupted and 106 * needing cancellation, and set the state to 107 * running. When the thread resumes, it will 108 * remove itself from the queue and call the 109 * cancellation completion routine. 110 */ 111 pthread->interrupted = 1; 112 pthread->cancelflags |= PTHREAD_CANCEL_NEEDED; 113 PTHREAD_NEW_STATE(pthread,PS_RUNNING); 114 pthread->continuation = finish_cancellation; 115 break; 116 117 case PS_DEAD: 118 case PS_DEADLOCK: 119 case PS_STATE_MAX: 120 /* Ignore - only here to silence -Wall: */ 121 break; 122 } 123 } 124 125 /* Unprotect the scheduling queues: */ 126 _thread_kern_sig_undefer(); 127 128 ret = 0; 129 } 130 return (ret); 131} 132 133int 134_pthread_setcancelstate(int state, int *oldstate) 135{ 136 struct pthread *curthread = _get_curthread(); 137 int ostate; 138 int ret; 139 140 ostate = curthread->cancelflags & PTHREAD_CANCEL_DISABLE; 141 142 switch (state) { 143 case PTHREAD_CANCEL_ENABLE: 144 if (oldstate != NULL) 145 *oldstate = ostate; 146 curthread->cancelflags &= ~PTHREAD_CANCEL_DISABLE; 147 if ((curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0) 148 pthread_testcancel(); 149 ret = 0; 150 break; 151 case PTHREAD_CANCEL_DISABLE: 152 if (oldstate != NULL) 153 *oldstate = ostate; 154 curthread->cancelflags |= PTHREAD_CANCEL_DISABLE; 155 ret = 0; 156 break; 157 default: 158 ret = EINVAL; 159 } 160 161 return (ret); 162} 163 164int 165_pthread_setcanceltype(int type, int *oldtype) 166{ 167 struct pthread *curthread = _get_curthread(); 168 int otype; 169 int ret; 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 pthread_testcancel(); 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 return (ret); 191} 192 193void 194_pthread_testcancel(void) 195{ 196 struct pthread *curthread = _get_curthread(); 197 198 if (((curthread->cancelflags & PTHREAD_CANCEL_DISABLE) == 0) && 199 ((curthread->cancelflags & PTHREAD_CANCELLING) != 0)) { 200 /* 201 * It is possible for this thread to be swapped out 202 * while performing cancellation; do not allow it 203 * to be cancelled again. 204 */ 205 curthread->cancelflags &= ~PTHREAD_CANCELLING; 206 _thread_exit_cleanup(); 207 pthread_exit(PTHREAD_CANCELED); 208 PANIC("cancel"); 209 } 210} 211 212void 213_thread_enter_cancellation_point(void) 214{ 215 struct pthread *curthread = _get_curthread(); 216 217 /* Look for a cancellation before we block: */ 218 pthread_testcancel(); 219 curthread->cancelflags |= PTHREAD_AT_CANCEL_POINT; 220} 221 222void 223_thread_leave_cancellation_point(void) 224{ 225 struct pthread *curthread = _get_curthread(); 226 227 curthread->cancelflags &= ~PTHREAD_AT_CANCEL_POINT; 228 /* Look for a cancellation after we unblock: */ 229 pthread_testcancel(); 230} 231 232static void 233finish_cancellation(void *arg) 234{ 235 struct pthread *curthread = _get_curthread(); 236 237 curthread->continuation = NULL; 238 curthread->interrupted = 0; 239 240 if ((curthread->cancelflags & PTHREAD_CANCEL_NEEDED) != 0) { 241 curthread->cancelflags &= ~PTHREAD_CANCEL_NEEDED; 242 _thread_exit_cleanup(); 243 pthread_exit(PTHREAD_CANCELED); 244 } 245} 246