thr_cancel.c revision 91762
153812Salfred/* 253812Salfred * David Leonard <d@openbsd.org>, 1999. Public domain. 353812Salfred * $FreeBSD: head/lib/libkse/thread/thr_cancel.c 91762 2002-03-06 19:28:41Z deischen $ 453812Salfred */ 553812Salfred#include <sys/errno.h> 653812Salfred#include <pthread.h> 753812Salfred#include "pthread_private.h" 853812Salfred 956277Sjasonestatic void finish_cancellation(void *arg); 1056277Sjasone 1175369Sdeischen__weak_reference(_pthread_cancel, pthread_cancel); 1275369Sdeischen__weak_reference(_pthread_setcancelstate, pthread_setcancelstate); 1375369Sdeischen__weak_reference(_pthread_setcanceltype, pthread_setcanceltype); 1475369Sdeischen__weak_reference(_pthread_testcancel, pthread_testcancel); 1571581Sdeischen 1653812Salfredint 1771581Sdeischen_pthread_cancel(pthread_t pthread) 1853812Salfred{ 1953812Salfred int ret; 2053812Salfred 2153812Salfred if ((ret = _find_thread(pthread)) != 0) { 2253812Salfred /* NOTHING */ 2353812Salfred } else if (pthread->state == PS_DEAD || pthread->state == PS_DEADLOCK) { 2453812Salfred ret = 0; 2553812Salfred } else { 2653812Salfred /* Protect the scheduling queues: */ 2753812Salfred _thread_kern_sig_defer(); 2853812Salfred 2954708Sdeischen if (((pthread->cancelflags & PTHREAD_CANCEL_DISABLE) != 0) || 3054708Sdeischen (((pthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) == 0) && 3154708Sdeischen ((pthread->cancelflags & PTHREAD_AT_CANCEL_POINT) == 0))) 3254708Sdeischen /* Just mark it for cancellation: */ 3354708Sdeischen pthread->cancelflags |= PTHREAD_CANCELLING; 3454708Sdeischen else { 3554708Sdeischen /* 3654708Sdeischen * Check if we need to kick it back into the 3754708Sdeischen * run queue: 3854708Sdeischen */ 3953812Salfred switch (pthread->state) { 4053812Salfred case PS_RUNNING: 4153812Salfred /* No need to resume: */ 4253812Salfred pthread->cancelflags |= PTHREAD_CANCELLING; 4353812Salfred break; 4453812Salfred 4553812Salfred case PS_SPINBLOCK: 4653812Salfred case PS_FDR_WAIT: 4753812Salfred case PS_FDW_WAIT: 4853812Salfred case PS_POLL_WAIT: 4953812Salfred case PS_SELECT_WAIT: 5054708Sdeischen /* Remove these threads from the work queue: */ 5153812Salfred if ((pthread->flags & PTHREAD_FLAGS_IN_WORKQ) 5253812Salfred != 0) 5353812Salfred PTHREAD_WORKQ_REMOVE(pthread); 5453812Salfred /* Fall through: */ 5553812Salfred case PS_SIGTHREAD: 5653812Salfred case PS_SLEEP_WAIT: 5753812Salfred case PS_WAIT_WAIT: 5853812Salfred case PS_SIGSUSPEND: 5953812Salfred case PS_SIGWAIT: 6053812Salfred /* Interrupt and resume: */ 6153812Salfred pthread->interrupted = 1; 6253812Salfred pthread->cancelflags |= PTHREAD_CANCELLING; 6353812Salfred PTHREAD_NEW_STATE(pthread,PS_RUNNING); 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 } 7581750Sjasone pthread->cancelflags |= PTHREAD_CANCELLING; 7681750Sjasone PTHREAD_NEW_STATE(pthread, PS_RUNNING); 7781750Sjasone break; 7881750Sjasone 7961681Sjasone case PS_SUSPENDED: 8061681Sjasone if (pthread->suspended == SUSP_NO || 8161681Sjasone pthread->suspended == SUSP_YES || 8281750Sjasone pthread->suspended == SUSP_JOIN || 8361681Sjasone pthread->suspended == SUSP_NOWAIT) { 8461681Sjasone /* 8561681Sjasone * This thread isn't in any scheduling 8661681Sjasone * queues; just change it's state: 8761681Sjasone */ 8861681Sjasone pthread->cancelflags |= 8961681Sjasone PTHREAD_CANCELLING; 9061681Sjasone PTHREAD_SET_STATE(pthread, PS_RUNNING); 9161681Sjasone break; 9261681Sjasone } 9361681Sjasone /* FALLTHROUGH */ 9453812Salfred case PS_MUTEX_WAIT: 9553812Salfred case PS_COND_WAIT: 9653812Salfred case PS_FDLR_WAIT: 9753812Salfred case PS_FDLW_WAIT: 9853812Salfred case PS_FILE_WAIT: 9953812Salfred /* 10053812Salfred * Threads in these states may be in queues. 10153812Salfred * In order to preserve queue integrity, the 10253812Salfred * cancelled thread must remove itself from the 10353812Salfred * queue. Mark the thread as interrupted and 10453812Salfred * needing cancellation, and set the state to 10553812Salfred * running. When the thread resumes, it will 10656277Sjasone * remove itself from the queue and call the 10756277Sjasone * cancellation completion routine. 10853812Salfred */ 10953812Salfred pthread->interrupted = 1; 11053812Salfred pthread->cancelflags |= PTHREAD_CANCEL_NEEDED; 11153812Salfred PTHREAD_NEW_STATE(pthread,PS_RUNNING); 11256277Sjasone pthread->continuation = finish_cancellation; 11353812Salfred break; 11453812Salfred 11553812Salfred case PS_DEAD: 11653812Salfred case PS_DEADLOCK: 11753812Salfred case PS_STATE_MAX: 11853812Salfred /* Ignore - only here to silence -Wall: */ 11953812Salfred break; 12054708Sdeischen } 12153812Salfred } 12254708Sdeischen 12353812Salfred /* Unprotect the scheduling queues: */ 12453812Salfred _thread_kern_sig_undefer(); 12553812Salfred 12653812Salfred ret = 0; 12753812Salfred } 12853812Salfred return (ret); 12953812Salfred} 13053812Salfred 13153812Salfredint 13271581Sdeischen_pthread_setcancelstate(int state, int *oldstate) 13353812Salfred{ 13471581Sdeischen struct pthread *curthread = _get_curthread(); 13553812Salfred int ostate; 13653812Salfred int ret; 13753812Salfred 13871581Sdeischen ostate = curthread->cancelflags & PTHREAD_CANCEL_DISABLE; 13953812Salfred 14053812Salfred switch (state) { 14153812Salfred case PTHREAD_CANCEL_ENABLE: 14253812Salfred if (oldstate != NULL) 14353812Salfred *oldstate = ostate; 14471581Sdeischen curthread->cancelflags &= ~PTHREAD_CANCEL_DISABLE; 14571581Sdeischen if ((curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0) 14653812Salfred pthread_testcancel(); 14753812Salfred ret = 0; 14853812Salfred break; 14953812Salfred case PTHREAD_CANCEL_DISABLE: 15053812Salfred if (oldstate != NULL) 15153812Salfred *oldstate = ostate; 15271581Sdeischen curthread->cancelflags |= PTHREAD_CANCEL_DISABLE; 15353812Salfred ret = 0; 15453812Salfred break; 15553812Salfred default: 15653812Salfred ret = EINVAL; 15753812Salfred } 15853812Salfred 15953812Salfred return (ret); 16053812Salfred} 16153812Salfred 16253812Salfredint 16371581Sdeischen_pthread_setcanceltype(int type, int *oldtype) 16453812Salfred{ 16571581Sdeischen struct pthread *curthread = _get_curthread(); 16653812Salfred int otype; 16753812Salfred int ret; 16853812Salfred 16971581Sdeischen otype = curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS; 17053812Salfred switch (type) { 17153812Salfred case PTHREAD_CANCEL_ASYNCHRONOUS: 17253812Salfred if (oldtype != NULL) 17353812Salfred *oldtype = otype; 17471581Sdeischen curthread->cancelflags |= PTHREAD_CANCEL_ASYNCHRONOUS; 17553812Salfred pthread_testcancel(); 17653812Salfred ret = 0; 17753812Salfred break; 17853812Salfred case PTHREAD_CANCEL_DEFERRED: 17953812Salfred if (oldtype != NULL) 18053812Salfred *oldtype = otype; 18171581Sdeischen curthread->cancelflags &= ~PTHREAD_CANCEL_ASYNCHRONOUS; 18253812Salfred ret = 0; 18353812Salfred break; 18453812Salfred default: 18553812Salfred ret = EINVAL; 18653812Salfred } 18753812Salfred 18853812Salfred return (ret); 18953812Salfred} 19053812Salfred 19153812Salfredvoid 19271581Sdeischen_pthread_testcancel(void) 19353812Salfred{ 19471581Sdeischen struct pthread *curthread = _get_curthread(); 19571581Sdeischen 19671581Sdeischen if (((curthread->cancelflags & PTHREAD_CANCEL_DISABLE) == 0) && 19771581Sdeischen ((curthread->cancelflags & PTHREAD_CANCELLING) != 0)) { 19853812Salfred /* 19953812Salfred * It is possible for this thread to be swapped out 20053812Salfred * while performing cancellation; do not allow it 20153812Salfred * to be cancelled again. 20253812Salfred */ 20371581Sdeischen curthread->cancelflags &= ~PTHREAD_CANCELLING; 20453812Salfred _thread_exit_cleanup(); 20553812Salfred pthread_exit(PTHREAD_CANCELED); 20653812Salfred PANIC("cancel"); 20753812Salfred } 20853812Salfred} 20953812Salfred 21053812Salfredvoid 21153812Salfred_thread_enter_cancellation_point(void) 21253812Salfred{ 21371581Sdeischen struct pthread *curthread = _get_curthread(); 21471581Sdeischen 21553812Salfred /* Look for a cancellation before we block: */ 21653812Salfred pthread_testcancel(); 21771581Sdeischen curthread->cancelflags |= PTHREAD_AT_CANCEL_POINT; 21853812Salfred} 21953812Salfred 22053812Salfredvoid 22153812Salfred_thread_leave_cancellation_point(void) 22253812Salfred{ 22371581Sdeischen struct pthread *curthread = _get_curthread(); 22471581Sdeischen 22571581Sdeischen curthread->cancelflags &= ~PTHREAD_AT_CANCEL_POINT; 22653812Salfred /* Look for a cancellation after we unblock: */ 22753812Salfred pthread_testcancel(); 22853812Salfred} 22956277Sjasone 23056277Sjasonestatic void 23156277Sjasonefinish_cancellation(void *arg) 23256277Sjasone{ 23371581Sdeischen struct pthread *curthread = _get_curthread(); 23456277Sjasone 23571581Sdeischen curthread->continuation = NULL; 23671581Sdeischen curthread->interrupted = 0; 23771581Sdeischen 23871581Sdeischen if ((curthread->cancelflags & PTHREAD_CANCEL_NEEDED) != 0) { 23971581Sdeischen curthread->cancelflags &= ~PTHREAD_CANCEL_NEEDED; 24056277Sjasone _thread_exit_cleanup(); 24156277Sjasone pthread_exit(PTHREAD_CANCELED); 24256277Sjasone } 24356277Sjasone} 244