thr_cancel.c revision 114187
153812Salfred/* 253812Salfred * David Leonard <d@openbsd.org>, 1999. Public domain. 353812Salfred * $FreeBSD: head/lib/libkse/thread/thr_cancel.c 114187 2003-04-28 23:56:12Z deischen $ 453812Salfred */ 553812Salfred#include <sys/errno.h> 653812Salfred#include <pthread.h> 7103388Smini#include "thr_private.h" 853812Salfred 975369Sdeischen__weak_reference(_pthread_cancel, pthread_cancel); 1075369Sdeischen__weak_reference(_pthread_setcancelstate, pthread_setcancelstate); 1175369Sdeischen__weak_reference(_pthread_setcanceltype, pthread_setcanceltype); 1275369Sdeischen__weak_reference(_pthread_testcancel, pthread_testcancel); 1371581Sdeischen 14113658Sdeischenstatic int checkcancel(struct pthread *curthread); 15113658Sdeischenstatic void testcancel(struct pthread *curthread); 16113658Sdeischenstatic void finish_cancellation(void *arg); 17113658Sdeischen 1853812Salfredint 1971581Sdeischen_pthread_cancel(pthread_t pthread) 2053812Salfred{ 21113658Sdeischen struct pthread *curthread = _get_curthread(); 2253812Salfred int ret; 2353812Salfred 24113658Sdeischen if ((ret = _thr_ref_add(curthread, pthread, /*include dead*/0)) == 0) { 25113658Sdeischen /* 26113658Sdeischen * Take the scheduling lock while we change the cancel flags. 27113658Sdeischen */ 28113658Sdeischen THR_SCHED_LOCK(curthread, pthread); 2953812Salfred 3054708Sdeischen if (((pthread->cancelflags & PTHREAD_CANCEL_DISABLE) != 0) || 31113658Sdeischen (((pthread->cancelflags & THR_AT_CANCEL_POINT) == 0) && 32113658Sdeischen ((pthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) == 0))) 3354708Sdeischen /* Just mark it for cancellation: */ 34113658Sdeischen pthread->cancelflags |= THR_CANCELLING; 3554708Sdeischen else { 3654708Sdeischen /* 3754708Sdeischen * Check if we need to kick it back into the 3854708Sdeischen * run queue: 3954708Sdeischen */ 4053812Salfred switch (pthread->state) { 4153812Salfred case PS_RUNNING: 4253812Salfred /* No need to resume: */ 43113658Sdeischen pthread->cancelflags |= THR_CANCELLING; 4453812Salfred break; 4553812Salfred 46113658Sdeischen case PS_LOCKWAIT: 47113658Sdeischen /* 48113658Sdeischen * These can't be removed from the queue. 49113658Sdeischen * Just mark it as cancelling and tell it 50113658Sdeischen * to yield once it leaves the critical 51113658Sdeischen * region. 52113658Sdeischen */ 53113658Sdeischen pthread->cancelflags |= THR_CANCELLING; 54113658Sdeischen pthread->critical_yield = 1; 55113658Sdeischen break; 56113658Sdeischen 5753812Salfred case PS_SLEEP_WAIT: 58111035Smini case PS_SIGSUSPEND: 59111035Smini case PS_SIGWAIT: 6053812Salfred /* Interrupt and resume: */ 6153812Salfred pthread->interrupted = 1; 62113658Sdeischen pthread->cancelflags |= THR_CANCELLING; 63113658Sdeischen _thr_setrunnable_unlocked(pthread); 6453812Salfred break; 6553812Salfred 6676909Sjasone case PS_JOIN: 6781750Sjasone /* 6888015Sdeischen * Disconnect the thread from the joinee: 6981750Sjasone */ 7087988Sdeischen if (pthread->join_status.thread != NULL) { 7187988Sdeischen pthread->join_status.thread->joiner 7287988Sdeischen = NULL; 7391762Sdeischen pthread->join_status.thread = NULL; 7481750Sjasone } 75113658Sdeischen pthread->cancelflags |= THR_CANCELLING; 76113658Sdeischen _thr_setrunnable_unlocked(pthread); 7781750Sjasone break; 7881750Sjasone 7961681Sjasone case PS_SUSPENDED: 8053812Salfred case PS_MUTEX_WAIT: 8153812Salfred case PS_COND_WAIT: 8253812Salfred /* 8353812Salfred * Threads in these states may be in queues. 8453812Salfred * In order to preserve queue integrity, the 8553812Salfred * cancelled thread must remove itself from the 8653812Salfred * queue. Mark the thread as interrupted and 8753812Salfred * needing cancellation, and set the state to 8853812Salfred * running. When the thread resumes, it will 8956277Sjasone * remove itself from the queue and call the 9056277Sjasone * cancellation completion routine. 9153812Salfred */ 9253812Salfred pthread->interrupted = 1; 93113658Sdeischen pthread->cancelflags |= THR_CANCEL_NEEDED; 94113658Sdeischen _thr_setrunnable_unlocked(pthread); 9556277Sjasone pthread->continuation = finish_cancellation; 9653812Salfred break; 9753812Salfred 9853812Salfred case PS_DEAD: 9953812Salfred case PS_DEADLOCK: 10053812Salfred case PS_STATE_MAX: 10153812Salfred /* Ignore - only here to silence -Wall: */ 10253812Salfred break; 10354708Sdeischen } 104113658Sdeischen if ((pthread->blocked != 0) && 105113658Sdeischen ((pthread->cancelflags & THR_AT_CANCEL_POINT) != 0)) 106113658Sdeischen kse_thr_interrupt(&pthread->tmbx); 10753812Salfred } 10854708Sdeischen 109113658Sdeischen /* 110113658Sdeischen * Release the thread's scheduling lock and remove the 111113658Sdeischen * reference: 112113658Sdeischen */ 113113658Sdeischen THR_SCHED_UNLOCK(curthread, pthread); 114113658Sdeischen _thr_ref_delete(curthread, pthread); 11553812Salfred } 11653812Salfred return (ret); 11753812Salfred} 11853812Salfred 11953812Salfredint 12071581Sdeischen_pthread_setcancelstate(int state, int *oldstate) 12153812Salfred{ 12271581Sdeischen struct pthread *curthread = _get_curthread(); 12353812Salfred int ostate; 12453812Salfred int ret; 125113658Sdeischen int need_exit = 0; 12653812Salfred 127113658Sdeischen /* Take the scheduling lock while fiddling with the thread's state: */ 128113658Sdeischen THR_SCHED_LOCK(curthread, curthread); 129113658Sdeischen 13071581Sdeischen ostate = curthread->cancelflags & PTHREAD_CANCEL_DISABLE; 13153812Salfred 13253812Salfred switch (state) { 13353812Salfred case PTHREAD_CANCEL_ENABLE: 13453812Salfred if (oldstate != NULL) 13553812Salfred *oldstate = ostate; 13671581Sdeischen curthread->cancelflags &= ~PTHREAD_CANCEL_DISABLE; 13771581Sdeischen if ((curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0) 138113658Sdeischen need_exit = checkcancel(curthread); 13953812Salfred ret = 0; 14053812Salfred break; 14153812Salfred case PTHREAD_CANCEL_DISABLE: 14253812Salfred if (oldstate != NULL) 14353812Salfred *oldstate = ostate; 14471581Sdeischen curthread->cancelflags |= PTHREAD_CANCEL_DISABLE; 14553812Salfred ret = 0; 14653812Salfred break; 14753812Salfred default: 14853812Salfred ret = EINVAL; 14953812Salfred } 15053812Salfred 151113658Sdeischen THR_SCHED_UNLOCK(curthread, curthread); 152113658Sdeischen if (need_exit != 0) { 153113658Sdeischen _thr_exit_cleanup(); 154113658Sdeischen pthread_exit(PTHREAD_CANCELED); 155113658Sdeischen PANIC("cancel"); 156113658Sdeischen } 15753812Salfred return (ret); 15853812Salfred} 15953812Salfred 16053812Salfredint 16171581Sdeischen_pthread_setcanceltype(int type, int *oldtype) 16253812Salfred{ 16371581Sdeischen struct pthread *curthread = _get_curthread(); 16453812Salfred int otype; 16553812Salfred int ret; 166113658Sdeischen int need_exit = 0; 16753812Salfred 168113658Sdeischen /* Take the scheduling lock while fiddling with the state: */ 169113658Sdeischen THR_SCHED_LOCK(curthread, curthread); 170113658Sdeischen 17171581Sdeischen otype = curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS; 17253812Salfred switch (type) { 17353812Salfred case PTHREAD_CANCEL_ASYNCHRONOUS: 17453812Salfred if (oldtype != NULL) 17553812Salfred *oldtype = otype; 17671581Sdeischen curthread->cancelflags |= PTHREAD_CANCEL_ASYNCHRONOUS; 177113658Sdeischen need_exit = checkcancel(curthread); 17853812Salfred ret = 0; 17953812Salfred break; 18053812Salfred case PTHREAD_CANCEL_DEFERRED: 18153812Salfred if (oldtype != NULL) 18253812Salfred *oldtype = otype; 18371581Sdeischen curthread->cancelflags &= ~PTHREAD_CANCEL_ASYNCHRONOUS; 18453812Salfred ret = 0; 18553812Salfred break; 18653812Salfred default: 18753812Salfred ret = EINVAL; 18853812Salfred } 18953812Salfred 190113658Sdeischen THR_SCHED_UNLOCK(curthread, curthread); 191113658Sdeischen if (need_exit != 0) { 192113658Sdeischen _thr_exit_cleanup(); 193113658Sdeischen pthread_exit(PTHREAD_CANCELED); 194113658Sdeischen PANIC("cancel"); 195113658Sdeischen } 19653812Salfred return (ret); 19753812Salfred} 19853812Salfred 199113658Sdeischenstatic int 200113658Sdeischencheckcancel(struct pthread *curthread) 20153812Salfred{ 20271581Sdeischen if (((curthread->cancelflags & PTHREAD_CANCEL_DISABLE) == 0) && 203113658Sdeischen ((curthread->cancelflags & THR_CANCELLING) != 0)) { 20453812Salfred /* 20553812Salfred * It is possible for this thread to be swapped out 20653812Salfred * while performing cancellation; do not allow it 20753812Salfred * to be cancelled again. 20853812Salfred */ 209113658Sdeischen curthread->cancelflags &= ~THR_CANCELLING; 210113658Sdeischen return (1); 211113658Sdeischen } 212113658Sdeischen else 213113658Sdeischen return (0); 214113658Sdeischen} 215113658Sdeischen 216113658Sdeischenstatic void 217113658Sdeischentestcancel(struct pthread *curthread) 218113658Sdeischen{ 219113658Sdeischen /* Take the scheduling lock while fiddling with the state: */ 220113658Sdeischen 221113658Sdeischen if (checkcancel(curthread) != 0) { 222113658Sdeischen /* Unlock before exiting: */ 223113658Sdeischen THR_SCHED_UNLOCK(curthread, curthread); 224113658Sdeischen 225113658Sdeischen _thr_exit_cleanup(); 22653812Salfred pthread_exit(PTHREAD_CANCELED); 22753812Salfred PANIC("cancel"); 22853812Salfred } 22953812Salfred} 23053812Salfred 23153812Salfredvoid 232113658Sdeischen_pthread_testcancel(void) 23353812Salfred{ 23471581Sdeischen struct pthread *curthread = _get_curthread(); 23571581Sdeischen 236114187Sdeischen THR_SCHED_LOCK(curthread, curthread); 237113658Sdeischen testcancel(curthread); 238114187Sdeischen THR_SCHED_UNLOCK(curthread, curthread); 239113658Sdeischen} 240113658Sdeischen 241113658Sdeischenvoid 242113658Sdeischen_thr_enter_cancellation_point(struct pthread *thread) 243113658Sdeischen{ 24453812Salfred /* Look for a cancellation before we block: */ 245114187Sdeischen THR_SCHED_LOCK(thread, thread); 246113658Sdeischen testcancel(thread); 247113658Sdeischen thread->cancelflags |= THR_AT_CANCEL_POINT; 248114187Sdeischen THR_SCHED_UNLOCK(thread, thread); 24953812Salfred} 25053812Salfred 25153812Salfredvoid 252113658Sdeischen_thr_leave_cancellation_point(struct pthread *thread) 25353812Salfred{ 254114187Sdeischen THR_SCHED_LOCK(thread, thread); 255113658Sdeischen thread->cancelflags &= ~THR_AT_CANCEL_POINT; 25653812Salfred /* Look for a cancellation after we unblock: */ 257113658Sdeischen testcancel(thread); 258114187Sdeischen THR_SCHED_UNLOCK(thread, thread); 25953812Salfred} 26056277Sjasone 26156277Sjasonestatic void 26256277Sjasonefinish_cancellation(void *arg) 26356277Sjasone{ 26471581Sdeischen struct pthread *curthread = _get_curthread(); 26556277Sjasone 26671581Sdeischen curthread->continuation = NULL; 26771581Sdeischen curthread->interrupted = 0; 26871581Sdeischen 269114187Sdeischen THR_SCHED_LOCK(curthread, curthread); 270113658Sdeischen if ((curthread->cancelflags & THR_CANCEL_NEEDED) != 0) { 271113658Sdeischen curthread->cancelflags &= ~THR_CANCEL_NEEDED; 272114187Sdeischen THR_SCHED_UNLOCK(curthread, curthread); 273113658Sdeischen _thr_exit_cleanup(); 27456277Sjasone pthread_exit(PTHREAD_CANCELED); 27556277Sjasone } 276114187Sdeischen THR_SCHED_UNLOCK(curthread, curthread); 27756277Sjasone} 278