thr_cancel.c revision 111035
1/* 2 * David Leonard <d@openbsd.org>, 1999. Public domain. 3 * $FreeBSD: head/lib/libkse/thread/thr_cancel.c 111035 2003-02-17 10:05:18Z mini $ 4 */ 5#include <sys/errno.h> 6#include <pthread.h> 7#include "thr_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 || (pthread->flags & PTHREAD_EXITING) != 0) { 25 ret = 0; 26 } else { 27 /* Protect the scheduling queues: */ 28 _thread_kern_sig_defer(); 29 30 if (((pthread->cancelflags & PTHREAD_CANCEL_DISABLE) != 0) || 31 (((pthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) == 0) && 32 ((pthread->cancelflags & PTHREAD_AT_CANCEL_POINT) == 0))) 33 /* Just mark it for cancellation: */ 34 pthread->cancelflags |= PTHREAD_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 |= PTHREAD_CANCELLING; 44 break; 45 46 case PS_SPINBLOCK: 47 /* Remove these threads from the work queue: */ 48 if ((pthread->flags & PTHREAD_FLAGS_IN_WORKQ) 49 != 0) 50 PTHREAD_WORKQ_REMOVE(pthread); 51 /* Fall through: */ 52 case PS_SLEEP_WAIT: 53 case PS_WAIT_WAIT: 54 case PS_SIGSUSPEND: 55 case PS_SIGWAIT: 56 /* Interrupt and resume: */ 57 pthread->interrupted = 1; 58 pthread->cancelflags |= PTHREAD_CANCELLING; 59 PTHREAD_NEW_STATE(pthread,PS_RUNNING); 60 break; 61 62 case PS_JOIN: 63 /* 64 * Disconnect the thread from the joinee: 65 */ 66 if (pthread->join_status.thread != NULL) { 67 pthread->join_status.thread->joiner 68 = NULL; 69 pthread->join_status.thread = NULL; 70 } 71 pthread->cancelflags |= PTHREAD_CANCELLING; 72 PTHREAD_NEW_STATE(pthread, PS_RUNNING); 73 break; 74 75 case PS_SUSPENDED: 76 case PS_MUTEX_WAIT: 77 case PS_COND_WAIT: 78 /* 79 * Threads in these states may be in queues. 80 * In order to preserve queue integrity, the 81 * cancelled thread must remove itself from the 82 * queue. Mark the thread as interrupted and 83 * needing cancellation, and set the state to 84 * running. When the thread resumes, it will 85 * remove itself from the queue and call the 86 * cancellation completion routine. 87 */ 88 pthread->interrupted = 1; 89 pthread->cancelflags |= PTHREAD_CANCEL_NEEDED; 90 PTHREAD_NEW_STATE(pthread, PS_RUNNING); 91 pthread->continuation = finish_cancellation; 92 break; 93 94 case PS_DEAD: 95 case PS_DEADLOCK: 96 case PS_STATE_MAX: 97 /* Ignore - only here to silence -Wall: */ 98 break; 99 } 100 } 101 102 /* Unprotect the scheduling queues: */ 103 _thread_kern_sig_undefer(); 104 105 ret = 0; 106 } 107 return (ret); 108} 109 110int 111_pthread_setcancelstate(int state, int *oldstate) 112{ 113 struct pthread *curthread = _get_curthread(); 114 int ostate; 115 int ret; 116 117 ostate = curthread->cancelflags & PTHREAD_CANCEL_DISABLE; 118 119 switch (state) { 120 case PTHREAD_CANCEL_ENABLE: 121 if (oldstate != NULL) 122 *oldstate = ostate; 123 curthread->cancelflags &= ~PTHREAD_CANCEL_DISABLE; 124 if ((curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0) 125 pthread_testcancel(); 126 ret = 0; 127 break; 128 case PTHREAD_CANCEL_DISABLE: 129 if (oldstate != NULL) 130 *oldstate = ostate; 131 curthread->cancelflags |= PTHREAD_CANCEL_DISABLE; 132 ret = 0; 133 break; 134 default: 135 ret = EINVAL; 136 } 137 138 return (ret); 139} 140 141int 142_pthread_setcanceltype(int type, int *oldtype) 143{ 144 struct pthread *curthread = _get_curthread(); 145 int otype; 146 int ret; 147 148 otype = curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS; 149 switch (type) { 150 case PTHREAD_CANCEL_ASYNCHRONOUS: 151 if (oldtype != NULL) 152 *oldtype = otype; 153 curthread->cancelflags |= PTHREAD_CANCEL_ASYNCHRONOUS; 154 pthread_testcancel(); 155 ret = 0; 156 break; 157 case PTHREAD_CANCEL_DEFERRED: 158 if (oldtype != NULL) 159 *oldtype = otype; 160 curthread->cancelflags &= ~PTHREAD_CANCEL_ASYNCHRONOUS; 161 ret = 0; 162 break; 163 default: 164 ret = EINVAL; 165 } 166 167 return (ret); 168} 169 170void 171_pthread_testcancel(void) 172{ 173 struct pthread *curthread = _get_curthread(); 174 175 if (((curthread->cancelflags & PTHREAD_CANCEL_DISABLE) == 0) && 176 ((curthread->cancelflags & PTHREAD_CANCELLING) != 0) && 177 ((curthread->flags & PTHREAD_EXITING) == 0)) { 178 /* 179 * It is possible for this thread to be swapped out 180 * while performing cancellation; do not allow it 181 * to be cancelled again. 182 */ 183 curthread->cancelflags &= ~PTHREAD_CANCELLING; 184 _thread_exit_cleanup(); 185 pthread_exit(PTHREAD_CANCELED); 186 PANIC("cancel"); 187 } 188} 189 190void 191_thread_enter_cancellation_point(void) 192{ 193 struct pthread *curthread = _get_curthread(); 194 195 /* Look for a cancellation before we block: */ 196 pthread_testcancel(); 197 curthread->cancelflags |= PTHREAD_AT_CANCEL_POINT; 198} 199 200void 201_thread_leave_cancellation_point(void) 202{ 203 struct pthread *curthread = _get_curthread(); 204 205 curthread->cancelflags &= ~PTHREAD_AT_CANCEL_POINT; 206 /* Look for a cancellation after we unblock: */ 207 pthread_testcancel(); 208} 209 210static void 211finish_cancellation(void *arg) 212{ 213 struct pthread *curthread = _get_curthread(); 214 215 curthread->continuation = NULL; 216 curthread->interrupted = 0; 217 218 if ((curthread->cancelflags & PTHREAD_CANCEL_NEEDED) != 0) { 219 curthread->cancelflags &= ~PTHREAD_CANCEL_NEEDED; 220 _thread_exit_cleanup(); 221 pthread_exit(PTHREAD_CANCELED); 222 } 223} 224