1 /* 2 * David Leonard <d@openbsd.org>, 1999. Public domain. 3 * $FreeBSD$ 4 */ 5 #include <sys/errno.h> 6 #include <pthread.h> 7 #include <stdlib.h> 8 #include "thr_private.h" 9 10 /* 11 * Static prototypes 12 */ 13 static void testcancel(void); 14 15 __weak_reference(_pthread_cancel, pthread_cancel); 16 __weak_reference(_pthread_setcancelstate, pthread_setcancelstate); 17 __weak_reference(_pthread_setcanceltype, pthread_setcanceltype); 18 __weak_reference(_pthread_testcancel, pthread_testcancel); 19 20 int 21 _pthread_cancel(pthread_t pthread) 22 { 23 int ret; 24 pthread_t joined; 25 26 /* 27 * When canceling a thread that has joined another thread, this 28 * routine breaks the normal lock order of locking first the 29 * joined and then the joiner. Therefore, it is necessary that 30 * if it can't obtain the second lock, that it release the first 31 * one and restart from the top. 32 */ 33 retry: 34 if ((ret = _find_thread(pthread)) != 0) 35 /* The thread is not on the list of active threads */ 36 goto out; 37 38 _thread_critical_enter(pthread); 39 40 if (pthread->state == PS_DEAD || pthread->state == PS_DEADLOCK 41 || (pthread->flags & PTHREAD_EXITING) != 0) { 42 /* 43 * The thread is in the process of (or has already) exited 44 * or is deadlocked. 45 */ 46 _thread_critical_exit(pthread); 47 ret = 0; 48 goto out; 49 } 50 51 /* 52 * The thread is on the active thread list and is not in the process 53 * of exiting. 54 */ 55 56 if (((pthread->cancelflags & PTHREAD_CANCEL_DISABLE) != 0) || 57 (((pthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) == 0) && 58 ((pthread->cancelflags & PTHREAD_AT_CANCEL_POINT) == 0))) 59 /* Just mark it for cancellation: */ 60 pthread->cancelflags |= PTHREAD_CANCELLING; 61 else { 62 /* 63 * Check if we need to kick it back into the 64 * run queue: 65 */ 66 switch (pthread->state) { 67 case PS_RUNNING: 68 /* No need to resume: */ 69 pthread->cancelflags |= PTHREAD_CANCELLING; 70 break; 71 72 case PS_SLEEP_WAIT: 73 case PS_WAIT_WAIT: 74 pthread->cancelflags |= PTHREAD_CANCELLING; 75 PTHREAD_NEW_STATE(pthread, PS_RUNNING); 76 break; 77 78 case PS_JOIN: 79 /* 80 * Disconnect the thread from the joinee: 81 */ 82 if ((joined = pthread->join_status.thread) != NULL) { 83 UMTX_TRYLOCK(&joined->lock, ret); 84 if (ret == EBUSY) { 85 _thread_critical_exit(pthread); 86 goto retry; 87 } 88 pthread->join_status.thread->joiner = NULL; 89 UMTX_UNLOCK(&joined->lock); 90 joined = pthread->join_status.thread = NULL; 91 } 92 pthread->cancelflags |= PTHREAD_CANCELLING; 93 PTHREAD_NEW_STATE(pthread, PS_RUNNING); 94 break; 95 96 case PS_MUTEX_WAIT: 97 case PS_COND_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. When the thread resumes, it will 103 * remove itself from the queue and call the 104 * cancellation routine. 105 */ 106 pthread->cancelflags |= PTHREAD_CANCELLING; 107 PTHREAD_NEW_STATE(pthread, PS_RUNNING); 108 break; 109 110 case PS_DEAD: 111 case PS_DEADLOCK: 112 case PS_STATE_MAX: 113 /* Ignore - only here to silence -Wall: */ 114 break; 115 } 116 } 117 118 /* Unprotect the scheduling queues: */ 119 _thread_critical_exit(pthread); 120 121 ret = 0; 122 out: 123 return (ret); 124 } 125 126 int 127 _pthread_setcancelstate(int state, int *oldstate) 128 { 129 int ostate, ret; 130 131 ret = 0; 132 133 _thread_critical_enter(curthread); 134 135 ostate = curthread->cancelflags & PTHREAD_CANCEL_DISABLE; 136 137 switch (state) { 138 case PTHREAD_CANCEL_ENABLE: 139 if (oldstate != NULL) 140 *oldstate = ostate; 141 curthread->cancelflags &= ~PTHREAD_CANCEL_DISABLE; 142 if ((curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) == 0) 143 break; 144 testcancel(); 145 break; 146 case PTHREAD_CANCEL_DISABLE: 147 if (oldstate != NULL) 148 *oldstate = ostate; 149 curthread->cancelflags |= PTHREAD_CANCEL_DISABLE; 150 break; 151 default: 152 ret = EINVAL; 153 } 154 155 _thread_critical_exit(curthread); 156 return (ret); 157 } 158 159 int 160 _pthread_setcanceltype(int type, int *oldtype) 161 { 162 int otype; 163 164 _thread_critical_enter(curthread); 165 otype = curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS; 166 switch (type) { 167 case PTHREAD_CANCEL_ASYNCHRONOUS: 168 if (oldtype != NULL) 169 *oldtype = otype; 170 curthread->cancelflags |= PTHREAD_CANCEL_ASYNCHRONOUS; 171 testcancel(); 172 break; 173 case PTHREAD_CANCEL_DEFERRED: 174 if (oldtype != NULL) 175 *oldtype = otype; 176 curthread->cancelflags &= ~PTHREAD_CANCEL_ASYNCHRONOUS; 177 break; 178 default: 179 return (EINVAL); 180 } 181 182 _thread_critical_exit(curthread); 183 return (0); 184 } 185 186 void 187 _pthread_testcancel(void) 188 { 189 _thread_critical_enter(curthread); 190 testcancel(); 191 _thread_critical_exit(curthread); 192 } 193 194 static void 195 testcancel() 196 { 197 /* 198 * This pthread should already be locked by the caller. 199 */ 200 201 if (((curthread->cancelflags & PTHREAD_CANCEL_DISABLE) == 0) && 202 ((curthread->cancelflags & PTHREAD_CANCELLING) != 0) && 203 ((curthread->flags & PTHREAD_EXITING) == 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 &= ~PTHREAD_CANCELLING; 210 _thread_critical_exit(curthread); 211 _thread_exit_cleanup(); 212 pthread_exit(PTHREAD_CANCELED); 213 PANIC("cancel"); 214 } 215 } 216 217 void 218 _thread_enter_cancellation_point(void) 219 { 220 _thread_critical_enter(curthread); 221 testcancel(); 222 curthread->cancelflags |= PTHREAD_AT_CANCEL_POINT; 223 _thread_critical_exit(curthread); 224 } 225 226 void 227 _thread_leave_cancellation_point(void) 228 { 229 _thread_critical_enter(curthread); 230 curthread->cancelflags &= ~PTHREAD_AT_CANCEL_POINT; 231 testcancel(); 232 _thread_critical_exit(curthread); 233 234 } 235