thr_cancel.c revision 88015
1/* 2 * David Leonard <d@openbsd.org>, 1999. Public domain. 3 * $FreeBSD: head/lib/libkse/thread/thr_cancel.c 88015 2001-12-16 13:26:44Z 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: 69 */ 70 if (pthread->join_status.thread != NULL) { 71 pthread->join_status.thread->joiner 72 = NULL; 73 } 74 pthread->cancelflags |= PTHREAD_CANCELLING; 75 PTHREAD_NEW_STATE(pthread, PS_RUNNING); 76 break; 77 78 case PS_SUSPENDED: 79 if (pthread->suspended == SUSP_NO || 80 pthread->suspended == SUSP_YES || 81 pthread->suspended == SUSP_JOIN || 82 pthread->suspended == SUSP_NOWAIT) { 83 /* 84 * This thread isn't in any scheduling 85 * queues; just change it's state: 86 */ 87 pthread->cancelflags |= 88 PTHREAD_CANCELLING; 89 PTHREAD_SET_STATE(pthread, PS_RUNNING); 90 break; 91 } 92 /* FALLTHROUGH */ 93 case PS_MUTEX_WAIT: 94 case PS_COND_WAIT: 95 case PS_FDLR_WAIT: 96 case PS_FDLW_WAIT: 97 case PS_FILE_WAIT: 98 /* 99 * Threads in these states may be in queues. 100 * In order to preserve queue integrity, the 101 * cancelled thread must remove itself from the 102 * queue. Mark the thread as interrupted and 103 * needing cancellation, and set the state to 104 * running. When the thread resumes, it will 105 * remove itself from the queue and call the 106 * cancellation completion routine. 107 */ 108 pthread->interrupted = 1; 109 pthread->cancelflags |= PTHREAD_CANCEL_NEEDED; 110 PTHREAD_NEW_STATE(pthread,PS_RUNNING); 111 pthread->continuation = finish_cancellation; 112 break; 113 114 case PS_DEAD: 115 case PS_DEADLOCK: 116 case PS_STATE_MAX: 117 /* Ignore - only here to silence -Wall: */ 118 break; 119 } 120 } 121 122 /* Unprotect the scheduling queues: */ 123 _thread_kern_sig_undefer(); 124 125 ret = 0; 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 137 ostate = curthread->cancelflags & PTHREAD_CANCEL_DISABLE; 138 139 switch (state) { 140 case PTHREAD_CANCEL_ENABLE: 141 if (oldstate != NULL) 142 *oldstate = ostate; 143 curthread->cancelflags &= ~PTHREAD_CANCEL_DISABLE; 144 if ((curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0) 145 pthread_testcancel(); 146 ret = 0; 147 break; 148 case PTHREAD_CANCEL_DISABLE: 149 if (oldstate != NULL) 150 *oldstate = ostate; 151 curthread->cancelflags |= PTHREAD_CANCEL_DISABLE; 152 ret = 0; 153 break; 154 default: 155 ret = EINVAL; 156 } 157 158 return (ret); 159} 160 161int 162_pthread_setcanceltype(int type, int *oldtype) 163{ 164 struct pthread *curthread = _get_curthread(); 165 int otype; 166 int ret; 167 168 otype = curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS; 169 switch (type) { 170 case PTHREAD_CANCEL_ASYNCHRONOUS: 171 if (oldtype != NULL) 172 *oldtype = otype; 173 curthread->cancelflags |= PTHREAD_CANCEL_ASYNCHRONOUS; 174 pthread_testcancel(); 175 ret = 0; 176 break; 177 case PTHREAD_CANCEL_DEFERRED: 178 if (oldtype != NULL) 179 *oldtype = otype; 180 curthread->cancelflags &= ~PTHREAD_CANCEL_ASYNCHRONOUS; 181 ret = 0; 182 break; 183 default: 184 ret = EINVAL; 185 } 186 187 return (ret); 188} 189 190void 191_pthread_testcancel(void) 192{ 193 struct pthread *curthread = _get_curthread(); 194 195 if (((curthread->cancelflags & PTHREAD_CANCEL_DISABLE) == 0) && 196 ((curthread->cancelflags & PTHREAD_CANCELLING) != 0)) { 197 /* 198 * It is possible for this thread to be swapped out 199 * while performing cancellation; do not allow it 200 * to be cancelled again. 201 */ 202 curthread->cancelflags &= ~PTHREAD_CANCELLING; 203 _thread_exit_cleanup(); 204 pthread_exit(PTHREAD_CANCELED); 205 PANIC("cancel"); 206 } 207} 208 209void 210_thread_enter_cancellation_point(void) 211{ 212 struct pthread *curthread = _get_curthread(); 213 214 /* Look for a cancellation before we block: */ 215 pthread_testcancel(); 216 curthread->cancelflags |= PTHREAD_AT_CANCEL_POINT; 217} 218 219void 220_thread_leave_cancellation_point(void) 221{ 222 struct pthread *curthread = _get_curthread(); 223 224 curthread->cancelflags &= ~PTHREAD_AT_CANCEL_POINT; 225 /* Look for a cancellation after we unblock: */ 226 pthread_testcancel(); 227} 228 229static void 230finish_cancellation(void *arg) 231{ 232 struct pthread *curthread = _get_curthread(); 233 234 curthread->continuation = NULL; 235 curthread->interrupted = 0; 236 237 if ((curthread->cancelflags & PTHREAD_CANCEL_NEEDED) != 0) { 238 curthread->cancelflags &= ~PTHREAD_CANCEL_NEEDED; 239 _thread_exit_cleanup(); 240 pthread_exit(PTHREAD_CANCELED); 241 } 242} 243