thr_cancel.c revision 123312
138032Speter/* 264562Sgshapiro * David Leonard <d@openbsd.org>, 1999. Public domain. 364562Sgshapiro * $FreeBSD: head/lib/libkse/thread/thr_cancel.c 123312 2003-12-09 02:20:56Z davidxu $ 438032Speter */ 538032Speter#include <sys/errno.h> 638032Speter#include <pthread.h> 738032Speter#include "thr_private.h" 838032Speter 938032Speter__weak_reference(_pthread_cancel, pthread_cancel); 1038032Speter__weak_reference(_pthread_setcancelstate, pthread_setcancelstate); 1138032Speter__weak_reference(_pthread_setcanceltype, pthread_setcanceltype); 1238032Speter__weak_reference(_pthread_testcancel, pthread_testcancel); 1338032Speter 1438032Speterstatic inline int 1538032Spetercheckcancel(struct pthread *curthread) 1664562Sgshapiro{ 1764562Sgshapiro if (((curthread->cancelflags & PTHREAD_CANCEL_DISABLE) == 0) && 1838032Speter ((curthread->cancelflags & THR_CANCELLING) != 0)) { 1938032Speter /* 2038032Speter * It is possible for this thread to be swapped out 2164562Sgshapiro * while performing cancellation; do not allow it 2238032Speter * to be cancelled again. 2338032Speter */ 2466494Sgshapiro curthread->cancelflags &= ~THR_CANCELLING; 2564562Sgshapiro return (1); 2638032Speter } 2738032Speter else 2838032Speter return (0); 2964562Sgshapiro} 3038032Speter 3164562Sgshapirostatic inline void 3264562Sgshapirotestcancel(struct pthread *curthread) 3364562Sgshapiro{ 3464562Sgshapiro if (checkcancel(curthread) != 0) { 3564562Sgshapiro /* Unlock before exiting: */ 3664562Sgshapiro THR_THREAD_UNLOCK(curthread, curthread); 3764562Sgshapiro 3864562Sgshapiro _thr_exit_cleanup(); 3964562Sgshapiro pthread_exit(PTHREAD_CANCELED); 4038032Speter PANIC("cancel"); 4138032Speter } 4238032Speter} 4338032Speter 4438032Speterint 4538032Speter_pthread_cancel(pthread_t pthread) 4638032Speter{ 4738032Speter struct pthread *curthread = _get_curthread(); 4864562Sgshapiro struct pthread *joinee = NULL; 4938032Speter struct kse_mailbox *kmbx = NULL; 5038032Speter int ret; 5138032Speter 5238032Speter if ((ret = _thr_ref_add(curthread, pthread, /*include dead*/0)) == 0) { 5338032Speter /* 5438032Speter * Take the thread's lock while we change the cancel flags. 5538032Speter */ 5638032Speter THR_THREAD_LOCK(curthread, pthread); 5738032Speter THR_SCHED_LOCK(curthread, pthread); 5838032Speter if (pthread->flags & THR_FLAGS_EXITING) { 5938032Speter THR_SCHED_UNLOCK(curthread, pthread); 6038032Speter THR_THREAD_UNLOCK(curthread, pthread); 6138032Speter _thr_ref_delete(curthread, pthread); 6238032Speter return (ESRCH); 6338032Speter } 6438032Speter if (((pthread->cancelflags & PTHREAD_CANCEL_DISABLE) != 0) || 6538032Speter (((pthread->cancelflags & THR_AT_CANCEL_POINT) == 0) && 6638032Speter ((pthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) == 0))) 6738032Speter /* Just mark it for cancellation: */ 6838032Speter pthread->cancelflags |= THR_CANCELLING; 6938032Speter else { 7038032Speter /* 7138032Speter * Check if we need to kick it back into the 7238032Speter * run queue: 7338032Speter */ 7464562Sgshapiro switch (pthread->state) { 7538032Speter case PS_RUNNING: 7638032Speter /* No need to resume: */ 7738032Speter pthread->cancelflags |= THR_CANCELLING; 7838032Speter break; 7938032Speter 8064562Sgshapiro case PS_LOCKWAIT: 8138032Speter /* 8238032Speter * These can't be removed from the queue. 8338032Speter * Just mark it as cancelling and tell it 8464562Sgshapiro * to yield once it leaves the critical 8538032Speter * region. 8638032Speter */ 8738032Speter pthread->cancelflags |= THR_CANCELLING; 8838032Speter pthread->critical_yield = 1; 8938032Speter break; 9038032Speter 9164562Sgshapiro case PS_SLEEP_WAIT: 9238032Speter case PS_SIGSUSPEND: 9364562Sgshapiro case PS_SIGWAIT: 9438032Speter /* Interrupt and resume: */ 9564562Sgshapiro pthread->interrupted = 1; 9664562Sgshapiro pthread->cancelflags |= THR_CANCELLING; 9764562Sgshapiro kmbx = _thr_setrunnable_unlocked(pthread); 9864562Sgshapiro break; 9964562Sgshapiro 10064562Sgshapiro case PS_JOIN: 10164562Sgshapiro /* Disconnect the thread from the joinee: */ 10266494Sgshapiro joinee = pthread->join_status.thread; 10364562Sgshapiro pthread->join_status.thread = NULL; 10464562Sgshapiro pthread->cancelflags |= THR_CANCELLING; 10564562Sgshapiro kmbx = _thr_setrunnable_unlocked(pthread); 10664562Sgshapiro if ((joinee != NULL) && 10738032Speter (pthread->kseg == joinee->kseg)) { 10838032Speter /* Remove the joiner from the joinee. */ 10938032Speter joinee->joiner = NULL; 11038032Speter joinee = NULL; 11138032Speter } 11238032Speter break; 11338032Speter 11438032Speter case PS_SUSPENDED: 11538032Speter case PS_MUTEX_WAIT: 11638032Speter case PS_COND_WAIT: 11738032Speter /* 11838032Speter * Threads in these states may be in queues. 11938032Speter * In order to preserve queue integrity, the 12064562Sgshapiro * cancelled thread must remove itself from the 12138032Speter * queue. Mark the thread as interrupted and 12264562Sgshapiro * needing cancellation, and set the state to 12338032Speter * running. When the thread resumes, it will 12464562Sgshapiro * remove itself from the queue and call the 12538032Speter * cancellation completion routine. 12638032Speter */ 12738032Speter pthread->interrupted = 1; 12838032Speter pthread->cancelflags |= THR_CANCEL_NEEDED; 12938032Speter kmbx = _thr_setrunnable_unlocked(pthread); 13038032Speter pthread->continuation = 13164562Sgshapiro _thr_finish_cancellation; 13264562Sgshapiro break; 13338032Speter 13464562Sgshapiro case PS_DEAD: 13538032Speter case PS_DEADLOCK: 13638032Speter case PS_STATE_MAX: 13738032Speter /* Ignore - only here to silence -Wall: */ 13866494Sgshapiro break; 13966494Sgshapiro } 14066494Sgshapiro if ((pthread->cancelflags & THR_AT_CANCEL_POINT) && 14138032Speter (pthread->blocked != 0 || 14238032Speter pthread->attr.flags & PTHREAD_SCOPE_SYSTEM)) 14338032Speter kse_thr_interrupt(&pthread->tcb->tcb_tmbx, 14438032Speter KSE_INTR_INTERRUPT, 0); 14538032Speter } 14638032Speter 14738032Speter /* 14838032Speter * Release the thread's lock and remove the 14938032Speter * reference: 15038032Speter */ 15138032Speter THR_SCHED_UNLOCK(curthread, pthread); 15238032Speter THR_THREAD_UNLOCK(curthread, pthread); 15338032Speter _thr_ref_delete(curthread, pthread); 15438032Speter if (kmbx != NULL) 15538032Speter kse_wakeup(kmbx); 15638032Speter 15738032Speter if ((joinee != NULL) && 15838032Speter (_thr_ref_add(curthread, joinee, /* include dead */1) == 0)) { 15938032Speter /* Remove the joiner from the joinee. */ 16038032Speter THR_SCHED_LOCK(curthread, joinee); 16138032Speter joinee->joiner = NULL; 16238032Speter THR_SCHED_UNLOCK(curthread, joinee); 16338032Speter _thr_ref_delete(curthread, joinee); 16438032Speter } 16538032Speter } 16638032Speter return (ret); 16764562Sgshapiro} 16838032Speter 16938032Speterint 17038032Speter_pthread_setcancelstate(int state, int *oldstate) 17138032Speter{ 17238032Speter struct pthread *curthread = _get_curthread(); 17338032Speter int ostate; 17438032Speter int ret; 17538032Speter int need_exit = 0; 17638032Speter 17738032Speter /* Take the thread's lock while fiddling with the state: */ 17838032Speter THR_THREAD_LOCK(curthread, curthread); 17938032Speter 18038032Speter ostate = curthread->cancelflags & PTHREAD_CANCEL_DISABLE; 18138032Speter 18238032Speter switch (state) { 18338032Speter case PTHREAD_CANCEL_ENABLE: 18438032Speter curthread->cancelflags &= ~PTHREAD_CANCEL_DISABLE; 18538032Speter if ((curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0) 18638032Speter need_exit = checkcancel(curthread); 18738032Speter ret = 0; 18838032Speter break; 18938032Speter case PTHREAD_CANCEL_DISABLE: 19038032Speter curthread->cancelflags |= PTHREAD_CANCEL_DISABLE; 19164562Sgshapiro ret = 0; 19238032Speter break; 19364562Sgshapiro default: 19438032Speter ret = EINVAL; 19564562Sgshapiro } 19664562Sgshapiro 19738032Speter THR_THREAD_UNLOCK(curthread, curthread); 19838032Speter if (need_exit != 0) { 19938032Speter _thr_exit_cleanup(); 20038032Speter pthread_exit(PTHREAD_CANCELED); 20138032Speter PANIC("cancel"); 20238032Speter } 20338032Speter if (ret == 0 && oldstate != NULL) 20464562Sgshapiro *oldstate = ostate; 20538032Speter 20664562Sgshapiro return (ret); 20738032Speter} 20864562Sgshapiro 20938032Speterint 21038032Speter_pthread_setcanceltype(int type, int *oldtype) 21138032Speter{ 21238032Speter struct pthread *curthread = _get_curthread(); 21338032Speter int otype; 21438032Speter int ret; 21538032Speter int need_exit = 0; 21664562Sgshapiro 21738032Speter /* Take the thread's lock while fiddling with the state: */ 21838032Speter THR_THREAD_LOCK(curthread, curthread); 21964562Sgshapiro 22038032Speter otype = curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS; 22164562Sgshapiro switch (type) { 22264562Sgshapiro case PTHREAD_CANCEL_ASYNCHRONOUS: 22364562Sgshapiro curthread->cancelflags |= PTHREAD_CANCEL_ASYNCHRONOUS; 22464562Sgshapiro need_exit = checkcancel(curthread); 22564562Sgshapiro ret = 0; 22664562Sgshapiro break; 22764562Sgshapiro case PTHREAD_CANCEL_DEFERRED: 22864562Sgshapiro curthread->cancelflags &= ~PTHREAD_CANCEL_ASYNCHRONOUS; 22938032Speter ret = 0; 23038032Speter break; 23138032Speter default: 23238032Speter ret = EINVAL; 23338032Speter } 23438032Speter 23538032Speter THR_THREAD_UNLOCK(curthread, curthread); 23638032Speter if (need_exit != 0) { 23738032Speter _thr_exit_cleanup(); 23864562Sgshapiro pthread_exit(PTHREAD_CANCELED); 23938032Speter PANIC("cancel"); 24038032Speter } 24164562Sgshapiro if (ret == 0 && oldtype != NULL) 24264562Sgshapiro *oldtype = otype; 24338032Speter 24464562Sgshapiro return (ret); 24538032Speter} 24664562Sgshapiro 24764562Sgshapirovoid 24838032Speter_pthread_testcancel(void) 24938032Speter{ 25038032Speter struct pthread *curthread = _get_curthread(); 25138032Speter 25238032Speter THR_THREAD_LOCK(curthread, curthread); 25338032Speter testcancel(curthread); 25438032Speter THR_THREAD_UNLOCK(curthread, curthread); 25538032Speter} 25638032Speter 25738032Spetervoid 25838032Speter_thr_cancel_enter(struct pthread *thread) 25964562Sgshapiro{ 26038032Speter /* Look for a cancellation before we block: */ 26164562Sgshapiro THR_THREAD_LOCK(thread, thread); 26264562Sgshapiro testcancel(thread); 26338032Speter thread->cancelflags |= THR_AT_CANCEL_POINT; 26464562Sgshapiro THR_THREAD_UNLOCK(thread, thread); 26564562Sgshapiro} 26638032Speter 26764562Sgshapirovoid 26864562Sgshapiro_thr_cancel_leave(struct pthread *thread, int check) 26938032Speter{ 27038032Speter THR_THREAD_LOCK(thread, thread); 27138032Speter thread->cancelflags &= ~THR_AT_CANCEL_POINT; 27238032Speter /* Look for a cancellation after we unblock: */ 27338032Speter if (check) 27438032Speter testcancel(thread); 27538032Speter THR_THREAD_UNLOCK(thread, thread); 27638032Speter} 27738032Speter 27838032Spetervoid 27938032Speter_thr_finish_cancellation(void *arg) 28038032Speter{ 28138032Speter struct pthread *curthread = _get_curthread(); 28238032Speter 28338032Speter curthread->continuation = NULL; 28438032Speter curthread->interrupted = 0; 28564562Sgshapiro 28664562Sgshapiro THR_THREAD_LOCK(curthread, curthread); 28764562Sgshapiro if ((curthread->cancelflags & THR_CANCEL_NEEDED) != 0) { 28864562Sgshapiro curthread->cancelflags &= ~THR_CANCEL_NEEDED; 28964562Sgshapiro THR_THREAD_UNLOCK(curthread, curthread); 29064562Sgshapiro _thr_exit_cleanup(); 29164562Sgshapiro pthread_exit(PTHREAD_CANCELED); 29264562Sgshapiro } 29364562Sgshapiro THR_THREAD_UNLOCK(curthread, curthread); 29464562Sgshapiro} 29564562Sgshapiro