1/* 2 * David Leonard <d@openbsd.org>, 1999. Public domain.
| 1/* 2 * David Leonard <d@openbsd.org>, 1999. Public domain.
|
3 * $FreeBSD: head/lib/libthr/thread/thr_cancel.c 126000 2004-02-19 13:51:52Z mtm $
| 3 * $FreeBSD: head/lib/libthr/thread/thr_cancel.c 129484 2004-05-20 12:06:16Z mtm $
|
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 */ 13static 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
| 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 */ 13static 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/* 21 * Posix requires this function to be async-cancel-safe, so it 22 * may not aquire any type of resource or call any functions 23 * that might do so. 24 */
|
20int 21_pthread_cancel(pthread_t pthread) 22{
| 25int 26_pthread_cancel(pthread_t pthread) 27{
|
23 int ret; 24 pthread_t joined;
| 28 /* Don't continue if cancellation has already been set. */ 29 if (atomic_cmpset_int(&pthread->cancellation, (int)CS_NULL, 30 (int)CS_PENDING) != 1) 31 return (0);
|
25 26 /*
| 32 33 /*
|
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.
| 34 * Only wakeup threads that are in cancellation points or 35 * have set async cancel. 36 * XXX - access to pthread->flags is not safe. We should just 37 * unconditionally wake the thread and make sure that 38 * the the library correctly handles spurious wakeups.
|
32 */
| 39 */
|
33retry: 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; 123out: 124 return (ret);
| 40 if ((pthread->cancellationpoint || pthread->cancelmode == M_ASYNC) && 41 (pthread->flags & PTHREAD_FLAGS_NOT_RUNNING) != 0) 42 PTHREAD_WAKE(pthread); 43 return (0);
|
125} 126
| 44} 45
|
| 46/* 47 * Posix requires this function to be async-cancel-safe, so it 48 * may not aquire any type of resource or call any functions 49 * that might do so. 50 */
|
127int 128_pthread_setcancelstate(int state, int *oldstate) 129{
| 51int 52_pthread_setcancelstate(int state, int *oldstate) 53{
|
130 int ostate, ret;
| 54 int ostate;
|
131
| 55
|
132 ret = 0; 133 134 _thread_critical_enter(curthread); 135 136 ostate = curthread->cancelflags & PTHREAD_CANCEL_DISABLE; 137
| 56 ostate = (curthread->cancelmode == M_OFF) ? PTHREAD_CANCEL_DISABLE : 57 PTHREAD_CANCEL_ENABLE;
|
138 switch (state) { 139 case PTHREAD_CANCEL_ENABLE:
| 58 switch (state) { 59 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();
| 60 curthread->cancelmode = curthread->cancelstate;
|
146 break; 147 case PTHREAD_CANCEL_DISABLE:
| 61 break; 62 case PTHREAD_CANCEL_DISABLE:
|
148 if (oldstate != NULL) 149 *oldstate = ostate; 150 curthread->cancelflags |= PTHREAD_CANCEL_DISABLE;
| 63 if (curthread->cancelmode != M_OFF) { 64 curthread->cancelstate = curthread->cancelmode; 65 curthread->cancelmode = M_OFF; 66 }
|
151 break; 152 default:
| 67 break; 68 default:
|
153 ret = EINVAL;
| 69 return (EINVAL);
|
154 }
| 70 }
|
155 156 _thread_critical_exit(curthread); 157 return (ret);
| 71 if (oldstate != NULL) 72 *oldstate = ostate; 73 return (0);
|
158} 159
| 74} 75
|
| 76/* 77 * Posix requires this function to be async-cancel-safe, so it 78 * may not aquire any type of resource or call any functions that 79 * might do so. 80 */
|
160int 161_pthread_setcanceltype(int type, int *oldtype) 162{
| 81int 82_pthread_setcanceltype(int type, int *oldtype) 83{
|
163 int otype;
| 84 enum cancel_mode omode;
|
164
| 85
|
165 _thread_critical_enter(curthread); 166 otype = curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS;
| 86 omode = curthread->cancelstate;
|
167 switch (type) { 168 case PTHREAD_CANCEL_ASYNCHRONOUS:
| 87 switch (type) { 88 case PTHREAD_CANCEL_ASYNCHRONOUS:
|
169 if (oldtype != NULL) 170 *oldtype = otype; 171 curthread->cancelflags |= PTHREAD_CANCEL_ASYNCHRONOUS; 172 testcancel();
| 89 if (curthread->cancelmode != M_OFF) 90 curthread->cancelmode = M_ASYNC; 91 curthread->cancelstate = M_ASYNC;
|
173 break; 174 case PTHREAD_CANCEL_DEFERRED:
| 92 break; 93 case PTHREAD_CANCEL_DEFERRED:
|
175 if (oldtype != NULL) 176 *oldtype = otype; 177 curthread->cancelflags &= ~PTHREAD_CANCEL_ASYNCHRONOUS;
| 94 if (curthread->cancelmode != M_OFF) 95 curthread->cancelmode = M_DEFERRED; 96 curthread->cancelstate = M_DEFERRED;
|
178 break; 179 default: 180 return (EINVAL); 181 }
| 97 break; 98 default: 99 return (EINVAL); 100 }
|
182 183 _thread_critical_exit(curthread);
| 101 if (oldtype != NULL) { 102 if (omode == M_DEFERRED) 103 *oldtype = PTHREAD_CANCEL_DEFERRED; 104 else if (omode == M_ASYNC) 105 *oldtype = PTHREAD_CANCEL_ASYNCHRONOUS; 106 }
|
184 return (0); 185} 186 187void 188_pthread_testcancel(void) 189{
| 107 return (0); 108} 109 110void 111_pthread_testcancel(void) 112{
|
190 _thread_critical_enter(curthread);
| |
191 testcancel();
| 113 testcancel();
|
192 _thread_critical_exit(curthread);
| |
193} 194 195static void 196testcancel() 197{
| 114} 115 116static void 117testcancel() 118{
|
198 /* 199 * This pthread should already be locked by the caller. 200 */
| 119 if (curthread->cancelmode != M_OFF) {
|
201
| 120
|
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");
| 121 /* Cleanup a canceled thread only once. */ 122 if (atomic_cmpset_int(&curthread->cancellation, 123 (int)CS_PENDING, (int)CS_SET) == 1) { 124 _thread_exit_cleanup(); 125 pthread_exit(PTHREAD_CANCELED); 126 PANIC("cancel"); 127 }
|
215 } 216} 217 218void 219_thread_enter_cancellation_point(void) 220{
| 128 } 129} 130 131void 132_thread_enter_cancellation_point(void) 133{
|
221 _thread_critical_enter(curthread);
| |
222 testcancel();
| 134 testcancel();
|
223 curthread->cancelflags |= PTHREAD_AT_CANCEL_POINT; 224 _thread_critical_exit(curthread);
| 135 curthread->cancellationpoint = 1;
|
225} 226 227void 228_thread_leave_cancellation_point(void) 229{
| 136} 137 138void 139_thread_leave_cancellation_point(void) 140{
|
230 _thread_critical_enter(curthread); 231 curthread->cancelflags &= ~PTHREAD_AT_CANCEL_POINT;
| 141 curthread->cancellationpoint = 0;
|
232 testcancel();
| 142 testcancel();
|
233 _thread_critical_exit(curthread); 234
| |
235}
| 143}
|