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