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_BARRIER_WAIT: 97 case PS_MUTEX_WAIT: 98 case PS_COND_WAIT: 99 /* 100 * Threads in these states may be in queues. 101 * In order to preserve queue integrity, the 102 * cancelled thread must remove itself from the 103 * queue. When the thread resumes, it will 104 * remove itself from the queue and call the 105 * cancellation routine. 106 */ 107 pthread->cancelflags |= PTHREAD_CANCELLING; 108 PTHREAD_NEW_STATE(pthread, PS_RUNNING); 109 break; 110 111 case PS_DEAD: 112 case PS_DEADLOCK: 113 case PS_STATE_MAX: 114 /* Ignore - only here to silence -Wall: */ 115 break; 116 } 117 } 118 119 /* Unprotect the scheduling queues: */ 120 _thread_critical_exit(pthread); 121 122 ret = 0; 123 out: 124 return (ret); 125 } 126 127 int 128 _pthread_setcancelstate(int state, int *oldstate) 129 { 130 int ostate, ret; 131 132 ret = 0; 133 134 _thread_critical_enter(curthread); 135 136 ostate = curthread->cancelflags & PTHREAD_CANCEL_DISABLE; 137 138 switch (state) { 139 case PTHREAD_CANCEL_ENABLE: 140 if (oldstate != NULL) 141 *oldstate = ostate; 142 curthread->cancelflags &= ~PTHREAD_CANCEL_DISABLE; 143 if ((curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) == 0) 144 break; 145 testcancel(); 146 break; 147 case PTHREAD_CANCEL_DISABLE: 148 if (oldstate != NULL) 149 *oldstate = ostate; 150 curthread->cancelflags |= PTHREAD_CANCEL_DISABLE; 151 break; 152 default: 153 ret = EINVAL; 154 } 155 156 _thread_critical_exit(curthread); 157 return (ret); 158 } 159 160 int 161 _pthread_setcanceltype(int type, int *oldtype) 162 { 163 int otype; 164 165 _thread_critical_enter(curthread); 166 otype = curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS; 167 switch (type) { 168 case PTHREAD_CANCEL_ASYNCHRONOUS: 169 if (oldtype != NULL) 170 *oldtype = otype; 171 curthread->cancelflags |= PTHREAD_CANCEL_ASYNCHRONOUS; 172 testcancel(); 173 break; 174 case PTHREAD_CANCEL_DEFERRED: 175 if (oldtype != NULL) 176 *oldtype = otype; 177 curthread->cancelflags &= ~PTHREAD_CANCEL_ASYNCHRONOUS; 178 break; 179 default: 180 return (EINVAL); 181 } 182 183 _thread_critical_exit(curthread); 184 return (0); 185 } 186 187 void 188 _pthread_testcancel(void) 189 { 190 _thread_critical_enter(curthread); 191 testcancel(); 192 _thread_critical_exit(curthread); 193 } 194 195 static void 196 testcancel() 197 { 198 /* 199 * This pthread should already be locked by the caller. 200 */ 201 202 if (((curthread->cancelflags & PTHREAD_CANCEL_DISABLE) == 0) && 203 ((curthread->cancelflags & PTHREAD_CANCELLING) != 0) && 204 ((curthread->flags & PTHREAD_EXITING) == 0)) { 205 /* 206 * It is possible for this thread to be swapped out 207 * while performing cancellation; do not allow it 208 * to be cancelled again. 209 */ 210 curthread->cancelflags &= ~PTHREAD_CANCELLING; 211 _thread_critical_exit(curthread); 212 _thread_exit_cleanup(); 213 pthread_exit(PTHREAD_CANCELED); 214 PANIC("cancel"); 215 } 216 } 217 218 void 219 _thread_enter_cancellation_point(void) 220 { 221 _thread_critical_enter(curthread); 222 testcancel(); 223 curthread->cancelflags |= PTHREAD_AT_CANCEL_POINT; 224 _thread_critical_exit(curthread); 225 } 226 227 void 228 _thread_leave_cancellation_point(void) 229 { 230 _thread_critical_enter(curthread); 231 curthread->cancelflags &= ~PTHREAD_AT_CANCEL_POINT; 232 testcancel(); 233 _thread_critical_exit(curthread); 234 235 } 236