153812Salfred/* 253812Salfred * David Leonard <d@openbsd.org>, 1999. Public domain. 353812Salfred * $FreeBSD$ 453812Salfred */ 5174112Sdeischen#include "namespace.h" 653812Salfred#include <sys/errno.h> 753812Salfred#include <pthread.h> 8174112Sdeischen#include "un-namespace.h" 9103388Smini#include "thr_private.h" 1053812Salfred 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 16123312Sdavidxustatic inline int 17123312Sdavidxucheckcancel(struct pthread *curthread) 18123312Sdavidxu{ 19139023Sdeischen if ((curthread->cancelflags & THR_CANCELLING) != 0) { 20123312Sdavidxu /* 21123312Sdavidxu * It is possible for this thread to be swapped out 22123312Sdavidxu * while performing cancellation; do not allow it 23123312Sdavidxu * to be cancelled again. 24123312Sdavidxu */ 25139023Sdeischen if ((curthread->flags & THR_FLAGS_EXITING) != 0) { 26139023Sdeischen /* 27139023Sdeischen * This may happen once, but after this, it 28139023Sdeischen * shouldn't happen again. 29139023Sdeischen */ 30139023Sdeischen curthread->cancelflags &= ~THR_CANCELLING; 31139023Sdeischen return (0); 32139023Sdeischen } 33139023Sdeischen if ((curthread->cancelflags & PTHREAD_CANCEL_DISABLE) == 0) { 34139023Sdeischen curthread->cancelflags &= ~THR_CANCELLING; 35139023Sdeischen return (1); 36139023Sdeischen } 37123312Sdavidxu } 38139023Sdeischen return (0); 39123312Sdavidxu} 40113658Sdeischen 41123312Sdavidxustatic inline void 42123312Sdavidxutestcancel(struct pthread *curthread) 43123312Sdavidxu{ 44123312Sdavidxu if (checkcancel(curthread) != 0) { 45123312Sdavidxu /* Unlock before exiting: */ 46123312Sdavidxu THR_THREAD_UNLOCK(curthread, curthread); 47123312Sdavidxu 48123312Sdavidxu _thr_exit_cleanup(); 49174112Sdeischen _pthread_exit(PTHREAD_CANCELED); 50123312Sdavidxu PANIC("cancel"); 51123312Sdavidxu } 52123312Sdavidxu} 53123312Sdavidxu 5453812Salfredint 5571581Sdeischen_pthread_cancel(pthread_t pthread) 5653812Salfred{ 57113658Sdeischen struct pthread *curthread = _get_curthread(); 58115278Sdeischen struct pthread *joinee = NULL; 59117907Sdeischen struct kse_mailbox *kmbx = NULL; 6053812Salfred int ret; 6153812Salfred 62113658Sdeischen if ((ret = _thr_ref_add(curthread, pthread, /*include dead*/0)) == 0) { 63113658Sdeischen /* 64120897Sdavidxu * Take the thread's lock while we change the cancel flags. 65113658Sdeischen */ 66120895Sdavidxu THR_THREAD_LOCK(curthread, pthread); 67113658Sdeischen THR_SCHED_LOCK(curthread, pthread); 68116972Sdavidxu if (pthread->flags & THR_FLAGS_EXITING) { 69116972Sdavidxu THR_SCHED_UNLOCK(curthread, pthread); 70120895Sdavidxu THR_THREAD_UNLOCK(curthread, pthread); 71116972Sdavidxu _thr_ref_delete(curthread, pthread); 72116972Sdavidxu return (ESRCH); 73116972Sdavidxu } 7454708Sdeischen if (((pthread->cancelflags & PTHREAD_CANCEL_DISABLE) != 0) || 75113658Sdeischen (((pthread->cancelflags & THR_AT_CANCEL_POINT) == 0) && 76113658Sdeischen ((pthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) == 0))) 7754708Sdeischen /* Just mark it for cancellation: */ 78113658Sdeischen pthread->cancelflags |= THR_CANCELLING; 7954708Sdeischen else { 8054708Sdeischen /* 8154708Sdeischen * Check if we need to kick it back into the 8254708Sdeischen * run queue: 8354708Sdeischen */ 8453812Salfred switch (pthread->state) { 8553812Salfred case PS_RUNNING: 8653812Salfred /* No need to resume: */ 87113658Sdeischen pthread->cancelflags |= THR_CANCELLING; 8853812Salfred break; 8953812Salfred 90113658Sdeischen case PS_LOCKWAIT: 91113658Sdeischen /* 92113658Sdeischen * These can't be removed from the queue. 93113658Sdeischen * Just mark it as cancelling and tell it 94113658Sdeischen * to yield once it leaves the critical 95113658Sdeischen * region. 96113658Sdeischen */ 97113658Sdeischen pthread->cancelflags |= THR_CANCELLING; 98113658Sdeischen pthread->critical_yield = 1; 99113658Sdeischen break; 100113658Sdeischen 10153812Salfred case PS_SLEEP_WAIT: 102111035Smini case PS_SIGSUSPEND: 103111035Smini case PS_SIGWAIT: 10453812Salfred /* Interrupt and resume: */ 10553812Salfred pthread->interrupted = 1; 106113658Sdeischen pthread->cancelflags |= THR_CANCELLING; 107117907Sdeischen kmbx = _thr_setrunnable_unlocked(pthread); 10853812Salfred break; 10953812Salfred 11076909Sjasone case PS_JOIN: 111115278Sdeischen /* Disconnect the thread from the joinee: */ 112115278Sdeischen joinee = pthread->join_status.thread; 113115278Sdeischen pthread->join_status.thread = NULL; 114113658Sdeischen pthread->cancelflags |= THR_CANCELLING; 115117907Sdeischen kmbx = _thr_setrunnable_unlocked(pthread); 116115278Sdeischen if ((joinee != NULL) && 117117162Sdavidxu (pthread->kseg == joinee->kseg)) { 118115278Sdeischen /* Remove the joiner from the joinee. */ 119115278Sdeischen joinee->joiner = NULL; 120115278Sdeischen joinee = NULL; 121115278Sdeischen } 12281750Sjasone break; 12381750Sjasone 12461681Sjasone case PS_SUSPENDED: 12553812Salfred case PS_MUTEX_WAIT: 12653812Salfred case PS_COND_WAIT: 12753812Salfred /* 12853812Salfred * Threads in these states may be in queues. 12953812Salfred * In order to preserve queue integrity, the 13053812Salfred * cancelled thread must remove itself from the 13153812Salfred * queue. Mark the thread as interrupted and 13253812Salfred * needing cancellation, and set the state to 13353812Salfred * running. When the thread resumes, it will 13456277Sjasone * remove itself from the queue and call the 13556277Sjasone * cancellation completion routine. 13653812Salfred */ 13753812Salfred pthread->interrupted = 1; 138113658Sdeischen pthread->cancelflags |= THR_CANCEL_NEEDED; 139117907Sdeischen kmbx = _thr_setrunnable_unlocked(pthread); 140120895Sdavidxu pthread->continuation = 141120895Sdavidxu _thr_finish_cancellation; 14253812Salfred break; 14353812Salfred 14453812Salfred case PS_DEAD: 14553812Salfred case PS_DEADLOCK: 14653812Salfred case PS_STATE_MAX: 14753812Salfred /* Ignore - only here to silence -Wall: */ 14853812Salfred break; 14954708Sdeischen } 150117706Sdavidxu if ((pthread->cancelflags & THR_AT_CANCEL_POINT) && 151117706Sdavidxu (pthread->blocked != 0 || 152117706Sdavidxu pthread->attr.flags & PTHREAD_SCOPE_SYSTEM)) 153118510Sdeischen kse_thr_interrupt(&pthread->tcb->tcb_tmbx, 154117706Sdavidxu KSE_INTR_INTERRUPT, 0); 15553812Salfred } 15654708Sdeischen 157113658Sdeischen /* 158120897Sdavidxu * Release the thread's lock and remove the 159113658Sdeischen * reference: 160113658Sdeischen */ 161113658Sdeischen THR_SCHED_UNLOCK(curthread, pthread); 162120895Sdavidxu THR_THREAD_UNLOCK(curthread, pthread); 163113658Sdeischen _thr_ref_delete(curthread, pthread); 164117907Sdeischen if (kmbx != NULL) 165117907Sdeischen kse_wakeup(kmbx); 166115278Sdeischen 167115278Sdeischen if ((joinee != NULL) && 168115278Sdeischen (_thr_ref_add(curthread, joinee, /* include dead */1) == 0)) { 169115278Sdeischen /* Remove the joiner from the joinee. */ 170115278Sdeischen THR_SCHED_LOCK(curthread, joinee); 171115278Sdeischen joinee->joiner = NULL; 172115278Sdeischen THR_SCHED_UNLOCK(curthread, joinee); 173115278Sdeischen _thr_ref_delete(curthread, joinee); 174115278Sdeischen } 17553812Salfred } 17653812Salfred return (ret); 17753812Salfred} 17853812Salfred 17953812Salfredint 18071581Sdeischen_pthread_setcancelstate(int state, int *oldstate) 18153812Salfred{ 18271581Sdeischen struct pthread *curthread = _get_curthread(); 18353812Salfred int ostate; 18453812Salfred int ret; 185113658Sdeischen int need_exit = 0; 18653812Salfred 187120897Sdavidxu /* Take the thread's lock while fiddling with the state: */ 188120895Sdavidxu THR_THREAD_LOCK(curthread, curthread); 189113658Sdeischen 19071581Sdeischen ostate = curthread->cancelflags & PTHREAD_CANCEL_DISABLE; 19153812Salfred 19253812Salfred switch (state) { 19353812Salfred case PTHREAD_CANCEL_ENABLE: 19471581Sdeischen curthread->cancelflags &= ~PTHREAD_CANCEL_DISABLE; 19571581Sdeischen if ((curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0) 196113658Sdeischen need_exit = checkcancel(curthread); 19753812Salfred ret = 0; 19853812Salfred break; 19953812Salfred case PTHREAD_CANCEL_DISABLE: 20071581Sdeischen curthread->cancelflags |= PTHREAD_CANCEL_DISABLE; 20153812Salfred ret = 0; 20253812Salfred break; 20353812Salfred default: 20453812Salfred ret = EINVAL; 20553812Salfred } 20653812Salfred 207120895Sdavidxu THR_THREAD_UNLOCK(curthread, curthread); 208113658Sdeischen if (need_exit != 0) { 209113658Sdeischen _thr_exit_cleanup(); 210174112Sdeischen _pthread_exit(PTHREAD_CANCELED); 211113658Sdeischen PANIC("cancel"); 212113658Sdeischen } 213117300Sdavidxu if (ret == 0 && oldstate != NULL) 214117300Sdavidxu *oldstate = ostate; 215117300Sdavidxu 21653812Salfred return (ret); 21753812Salfred} 21853812Salfred 21953812Salfredint 22071581Sdeischen_pthread_setcanceltype(int type, int *oldtype) 22153812Salfred{ 22271581Sdeischen struct pthread *curthread = _get_curthread(); 22353812Salfred int otype; 22453812Salfred int ret; 225113658Sdeischen int need_exit = 0; 22653812Salfred 227120897Sdavidxu /* Take the thread's lock while fiddling with the state: */ 228120895Sdavidxu THR_THREAD_LOCK(curthread, curthread); 229113658Sdeischen 23071581Sdeischen otype = curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS; 23153812Salfred switch (type) { 23253812Salfred case PTHREAD_CANCEL_ASYNCHRONOUS: 23371581Sdeischen curthread->cancelflags |= PTHREAD_CANCEL_ASYNCHRONOUS; 234113658Sdeischen need_exit = checkcancel(curthread); 23553812Salfred ret = 0; 23653812Salfred break; 23753812Salfred case PTHREAD_CANCEL_DEFERRED: 23871581Sdeischen curthread->cancelflags &= ~PTHREAD_CANCEL_ASYNCHRONOUS; 23953812Salfred ret = 0; 24053812Salfred break; 24153812Salfred default: 24253812Salfred ret = EINVAL; 24353812Salfred } 24453812Salfred 245120895Sdavidxu THR_THREAD_UNLOCK(curthread, curthread); 246113658Sdeischen if (need_exit != 0) { 247113658Sdeischen _thr_exit_cleanup(); 248174112Sdeischen _pthread_exit(PTHREAD_CANCELED); 249113658Sdeischen PANIC("cancel"); 250113658Sdeischen } 251117300Sdavidxu if (ret == 0 && oldtype != NULL) 252117300Sdavidxu *oldtype = otype; 253117300Sdavidxu 25453812Salfred return (ret); 25553812Salfred} 25653812Salfred 25753812Salfredvoid 258113658Sdeischen_pthread_testcancel(void) 25953812Salfred{ 26071581Sdeischen struct pthread *curthread = _get_curthread(); 26171581Sdeischen 262120895Sdavidxu THR_THREAD_LOCK(curthread, curthread); 263113658Sdeischen testcancel(curthread); 264120895Sdavidxu THR_THREAD_UNLOCK(curthread, curthread); 265113658Sdeischen} 266113658Sdeischen 267113658Sdeischenvoid 268123312Sdavidxu_thr_cancel_enter(struct pthread *thread) 269113658Sdeischen{ 27053812Salfred /* Look for a cancellation before we block: */ 271120895Sdavidxu THR_THREAD_LOCK(thread, thread); 272113658Sdeischen testcancel(thread); 273113658Sdeischen thread->cancelflags |= THR_AT_CANCEL_POINT; 274120895Sdavidxu THR_THREAD_UNLOCK(thread, thread); 27553812Salfred} 27653812Salfred 27753812Salfredvoid 278123312Sdavidxu_thr_cancel_leave(struct pthread *thread, int check) 27953812Salfred{ 280120895Sdavidxu THR_THREAD_LOCK(thread, thread); 281113658Sdeischen thread->cancelflags &= ~THR_AT_CANCEL_POINT; 28253812Salfred /* Look for a cancellation after we unblock: */ 283123312Sdavidxu if (check) 284123312Sdavidxu testcancel(thread); 285120895Sdavidxu THR_THREAD_UNLOCK(thread, thread); 28653812Salfred} 28756277Sjasone 288120895Sdavidxuvoid 289174112Sdeischen_thr_finish_cancellation(void *arg __unused) 29056277Sjasone{ 29171581Sdeischen struct pthread *curthread = _get_curthread(); 29256277Sjasone 29371581Sdeischen curthread->continuation = NULL; 29471581Sdeischen curthread->interrupted = 0; 29571581Sdeischen 296120895Sdavidxu THR_THREAD_LOCK(curthread, curthread); 297113658Sdeischen if ((curthread->cancelflags & THR_CANCEL_NEEDED) != 0) { 298113658Sdeischen curthread->cancelflags &= ~THR_CANCEL_NEEDED; 299120895Sdavidxu THR_THREAD_UNLOCK(curthread, curthread); 300113658Sdeischen _thr_exit_cleanup(); 301174112Sdeischen _pthread_exit(PTHREAD_CANCELED); 30256277Sjasone } 303120895Sdavidxu THR_THREAD_UNLOCK(curthread, curthread); 30456277Sjasone} 305