thr_cancel.c revision 115278
153812Salfred/* 253812Salfred * David Leonard <d@openbsd.org>, 1999. Public domain. 353812Salfred * $FreeBSD: head/lib/libkse/thread/thr_cancel.c 115278 2003-05-24 02:29:25Z 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(); 22115278Sdeischen struct pthread *joinee = NULL; 2353812Salfred int ret; 2453812Salfred 25113658Sdeischen if ((ret = _thr_ref_add(curthread, pthread, /*include dead*/0)) == 0) { 26113658Sdeischen /* 27113658Sdeischen * Take the scheduling lock while we change the cancel flags. 28113658Sdeischen */ 29113658Sdeischen THR_SCHED_LOCK(curthread, pthread); 3053812Salfred 3154708Sdeischen if (((pthread->cancelflags & PTHREAD_CANCEL_DISABLE) != 0) || 32113658Sdeischen (((pthread->cancelflags & THR_AT_CANCEL_POINT) == 0) && 33113658Sdeischen ((pthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) == 0))) 3454708Sdeischen /* Just mark it for cancellation: */ 35113658Sdeischen pthread->cancelflags |= THR_CANCELLING; 3654708Sdeischen else { 3754708Sdeischen /* 3854708Sdeischen * Check if we need to kick it back into the 3954708Sdeischen * run queue: 4054708Sdeischen */ 4153812Salfred switch (pthread->state) { 4253812Salfred case PS_RUNNING: 4353812Salfred /* No need to resume: */ 44113658Sdeischen pthread->cancelflags |= THR_CANCELLING; 4553812Salfred break; 4653812Salfred 47113658Sdeischen case PS_LOCKWAIT: 48113658Sdeischen /* 49113658Sdeischen * These can't be removed from the queue. 50113658Sdeischen * Just mark it as cancelling and tell it 51113658Sdeischen * to yield once it leaves the critical 52113658Sdeischen * region. 53113658Sdeischen */ 54113658Sdeischen pthread->cancelflags |= THR_CANCELLING; 55113658Sdeischen pthread->critical_yield = 1; 56113658Sdeischen break; 57113658Sdeischen 5853812Salfred case PS_SLEEP_WAIT: 59111035Smini case PS_SIGSUSPEND: 60111035Smini case PS_SIGWAIT: 6153812Salfred /* Interrupt and resume: */ 6253812Salfred pthread->interrupted = 1; 63113658Sdeischen pthread->cancelflags |= THR_CANCELLING; 64113658Sdeischen _thr_setrunnable_unlocked(pthread); 6553812Salfred break; 6653812Salfred 6776909Sjasone case PS_JOIN: 68115278Sdeischen /* Disconnect the thread from the joinee: */ 69115278Sdeischen joinee = pthread->join_status.thread; 70115278Sdeischen pthread->join_status.thread = NULL; 71113658Sdeischen pthread->cancelflags |= THR_CANCELLING; 72115278Sdeischen _thr_setrunnable_unlocked(pthread); 73115278Sdeischen if ((joinee != NULL) && 74115278Sdeischen (curthread->kseg == joinee->kseg)) { 75115278Sdeischen /* Remove the joiner from the joinee. */ 76115278Sdeischen joinee->joiner = NULL; 77115278Sdeischen joinee = NULL; 78115278Sdeischen } 7981750Sjasone break; 8081750Sjasone 8161681Sjasone case PS_SUSPENDED: 8253812Salfred case PS_MUTEX_WAIT: 8353812Salfred case PS_COND_WAIT: 8453812Salfred /* 8553812Salfred * Threads in these states may be in queues. 8653812Salfred * In order to preserve queue integrity, the 8753812Salfred * cancelled thread must remove itself from the 8853812Salfred * queue. Mark the thread as interrupted and 8953812Salfred * needing cancellation, and set the state to 9053812Salfred * running. When the thread resumes, it will 9156277Sjasone * remove itself from the queue and call the 9256277Sjasone * cancellation completion routine. 9353812Salfred */ 9453812Salfred pthread->interrupted = 1; 95113658Sdeischen pthread->cancelflags |= THR_CANCEL_NEEDED; 96113658Sdeischen _thr_setrunnable_unlocked(pthread); 9756277Sjasone pthread->continuation = finish_cancellation; 9853812Salfred break; 9953812Salfred 10053812Salfred case PS_DEAD: 10153812Salfred case PS_DEADLOCK: 10253812Salfred case PS_STATE_MAX: 10353812Salfred /* Ignore - only here to silence -Wall: */ 10453812Salfred break; 10554708Sdeischen } 106113658Sdeischen if ((pthread->blocked != 0) && 107113658Sdeischen ((pthread->cancelflags & THR_AT_CANCEL_POINT) != 0)) 108113658Sdeischen kse_thr_interrupt(&pthread->tmbx); 10953812Salfred } 11054708Sdeischen 111113658Sdeischen /* 112113658Sdeischen * Release the thread's scheduling lock and remove the 113113658Sdeischen * reference: 114113658Sdeischen */ 115113658Sdeischen THR_SCHED_UNLOCK(curthread, pthread); 116113658Sdeischen _thr_ref_delete(curthread, pthread); 117115278Sdeischen 118115278Sdeischen if ((joinee != NULL) && 119115278Sdeischen (_thr_ref_add(curthread, joinee, /* include dead */1) == 0)) { 120115278Sdeischen /* Remove the joiner from the joinee. */ 121115278Sdeischen THR_SCHED_LOCK(curthread, joinee); 122115278Sdeischen joinee->joiner = NULL; 123115278Sdeischen THR_SCHED_UNLOCK(curthread, joinee); 124115278Sdeischen _thr_ref_delete(curthread, joinee); 125115278Sdeischen } 12653812Salfred } 12753812Salfred return (ret); 12853812Salfred} 12953812Salfred 13053812Salfredint 13171581Sdeischen_pthread_setcancelstate(int state, int *oldstate) 13253812Salfred{ 13371581Sdeischen struct pthread *curthread = _get_curthread(); 13453812Salfred int ostate; 13553812Salfred int ret; 136113658Sdeischen int need_exit = 0; 13753812Salfred 138113658Sdeischen /* Take the scheduling lock while fiddling with the thread's state: */ 139113658Sdeischen THR_SCHED_LOCK(curthread, curthread); 140113658Sdeischen 14171581Sdeischen ostate = curthread->cancelflags & PTHREAD_CANCEL_DISABLE; 14253812Salfred 14353812Salfred switch (state) { 14453812Salfred case PTHREAD_CANCEL_ENABLE: 14553812Salfred if (oldstate != NULL) 14653812Salfred *oldstate = ostate; 14771581Sdeischen curthread->cancelflags &= ~PTHREAD_CANCEL_DISABLE; 14871581Sdeischen if ((curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0) 149113658Sdeischen need_exit = checkcancel(curthread); 15053812Salfred ret = 0; 15153812Salfred break; 15253812Salfred case PTHREAD_CANCEL_DISABLE: 15353812Salfred if (oldstate != NULL) 15453812Salfred *oldstate = ostate; 15571581Sdeischen curthread->cancelflags |= PTHREAD_CANCEL_DISABLE; 15653812Salfred ret = 0; 15753812Salfred break; 15853812Salfred default: 15953812Salfred ret = EINVAL; 16053812Salfred } 16153812Salfred 162113658Sdeischen THR_SCHED_UNLOCK(curthread, curthread); 163113658Sdeischen if (need_exit != 0) { 164113658Sdeischen _thr_exit_cleanup(); 165113658Sdeischen pthread_exit(PTHREAD_CANCELED); 166113658Sdeischen PANIC("cancel"); 167113658Sdeischen } 16853812Salfred return (ret); 16953812Salfred} 17053812Salfred 17153812Salfredint 17271581Sdeischen_pthread_setcanceltype(int type, int *oldtype) 17353812Salfred{ 17471581Sdeischen struct pthread *curthread = _get_curthread(); 17553812Salfred int otype; 17653812Salfred int ret; 177113658Sdeischen int need_exit = 0; 17853812Salfred 179113658Sdeischen /* Take the scheduling lock while fiddling with the state: */ 180113658Sdeischen THR_SCHED_LOCK(curthread, curthread); 181113658Sdeischen 18271581Sdeischen otype = curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS; 18353812Salfred switch (type) { 18453812Salfred case PTHREAD_CANCEL_ASYNCHRONOUS: 18553812Salfred if (oldtype != NULL) 18653812Salfred *oldtype = otype; 18771581Sdeischen curthread->cancelflags |= PTHREAD_CANCEL_ASYNCHRONOUS; 188113658Sdeischen need_exit = checkcancel(curthread); 18953812Salfred ret = 0; 19053812Salfred break; 19153812Salfred case PTHREAD_CANCEL_DEFERRED: 19253812Salfred if (oldtype != NULL) 19353812Salfred *oldtype = otype; 19471581Sdeischen curthread->cancelflags &= ~PTHREAD_CANCEL_ASYNCHRONOUS; 19553812Salfred ret = 0; 19653812Salfred break; 19753812Salfred default: 19853812Salfred ret = EINVAL; 19953812Salfred } 20053812Salfred 201113658Sdeischen THR_SCHED_UNLOCK(curthread, curthread); 202113658Sdeischen if (need_exit != 0) { 203113658Sdeischen _thr_exit_cleanup(); 204113658Sdeischen pthread_exit(PTHREAD_CANCELED); 205113658Sdeischen PANIC("cancel"); 206113658Sdeischen } 20753812Salfred return (ret); 20853812Salfred} 20953812Salfred 210113658Sdeischenstatic int 211113658Sdeischencheckcancel(struct pthread *curthread) 21253812Salfred{ 21371581Sdeischen if (((curthread->cancelflags & PTHREAD_CANCEL_DISABLE) == 0) && 214113658Sdeischen ((curthread->cancelflags & THR_CANCELLING) != 0)) { 21553812Salfred /* 21653812Salfred * It is possible for this thread to be swapped out 21753812Salfred * while performing cancellation; do not allow it 21853812Salfred * to be cancelled again. 21953812Salfred */ 220113658Sdeischen curthread->cancelflags &= ~THR_CANCELLING; 221113658Sdeischen return (1); 222113658Sdeischen } 223113658Sdeischen else 224113658Sdeischen return (0); 225113658Sdeischen} 226113658Sdeischen 227113658Sdeischenstatic void 228113658Sdeischentestcancel(struct pthread *curthread) 229113658Sdeischen{ 230113658Sdeischen /* Take the scheduling lock while fiddling with the state: */ 231113658Sdeischen 232113658Sdeischen if (checkcancel(curthread) != 0) { 233113658Sdeischen /* Unlock before exiting: */ 234113658Sdeischen THR_SCHED_UNLOCK(curthread, curthread); 235113658Sdeischen 236113658Sdeischen _thr_exit_cleanup(); 23753812Salfred pthread_exit(PTHREAD_CANCELED); 23853812Salfred PANIC("cancel"); 23953812Salfred } 24053812Salfred} 24153812Salfred 24253812Salfredvoid 243113658Sdeischen_pthread_testcancel(void) 24453812Salfred{ 24571581Sdeischen struct pthread *curthread = _get_curthread(); 24671581Sdeischen 247114187Sdeischen THR_SCHED_LOCK(curthread, curthread); 248113658Sdeischen testcancel(curthread); 249114187Sdeischen THR_SCHED_UNLOCK(curthread, curthread); 250113658Sdeischen} 251113658Sdeischen 252113658Sdeischenvoid 253113658Sdeischen_thr_enter_cancellation_point(struct pthread *thread) 254113658Sdeischen{ 25553812Salfred /* Look for a cancellation before we block: */ 256114187Sdeischen THR_SCHED_LOCK(thread, thread); 257113658Sdeischen testcancel(thread); 258113658Sdeischen thread->cancelflags |= THR_AT_CANCEL_POINT; 259114187Sdeischen THR_SCHED_UNLOCK(thread, thread); 26053812Salfred} 26153812Salfred 26253812Salfredvoid 263113658Sdeischen_thr_leave_cancellation_point(struct pthread *thread) 26453812Salfred{ 265114187Sdeischen THR_SCHED_LOCK(thread, thread); 266113658Sdeischen thread->cancelflags &= ~THR_AT_CANCEL_POINT; 26753812Salfred /* Look for a cancellation after we unblock: */ 268113658Sdeischen testcancel(thread); 269114187Sdeischen THR_SCHED_UNLOCK(thread, thread); 27053812Salfred} 27156277Sjasone 27256277Sjasonestatic void 27356277Sjasonefinish_cancellation(void *arg) 27456277Sjasone{ 27571581Sdeischen struct pthread *curthread = _get_curthread(); 27656277Sjasone 27771581Sdeischen curthread->continuation = NULL; 27871581Sdeischen curthread->interrupted = 0; 27971581Sdeischen 280114187Sdeischen THR_SCHED_LOCK(curthread, curthread); 281113658Sdeischen if ((curthread->cancelflags & THR_CANCEL_NEEDED) != 0) { 282113658Sdeischen curthread->cancelflags &= ~THR_CANCEL_NEEDED; 283114187Sdeischen THR_SCHED_UNLOCK(curthread, curthread); 284113658Sdeischen _thr_exit_cleanup(); 28556277Sjasone pthread_exit(PTHREAD_CANCELED); 28656277Sjasone } 287114187Sdeischen THR_SCHED_UNLOCK(curthread, curthread); 28856277Sjasone} 289