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