Cancellation and join
_pthread_update_cancel_state source
[
OS_ALWAYS_INLINE static inline int _pthread_update_cancel_state(pthread_t thread, int mask, int state) { uint16_t oldstate, newstate; os_atomic_rmw_loop(&thread->cancel_state, oldstate, newstate, relaxed, { newstate = oldstate; newstate &= ~mask; newstate |= state; }); return oldstate; }
_pthread_setcancelstate_exit source
When a thread exits set the cancellation state to DISABLE and DEFERRED
void _pthread_setcancelstate_exit(pthread_t thread, void *value_ptr) { _pthread_update_cancel_state(thread, _PTHREAD_CANCEL_STATE_MASK | _PTHREAD_CANCEL_TYPE_MASK, PTHREAD_CANCEL_DISABLE | PTHREAD_CANCEL_DEFERRED | _PTHREAD_CANCEL_EXITING); }
pthread_cancel source · pthread_cancel reference
Cancel a thread
PTHREAD_NOEXPORT_VARIANT int pthread_cancel(pthread_t thread) { if (!_pthread_is_valid(thread, NULL)) { return(ESRCH); } /* if the thread is a workqueue thread, then return error */ if (thread->wqthread != 0) { return(ENOTSUP); } int state = os_atomic_or(&thread->cancel_state, _PTHREAD_CANCEL_PENDING, relaxed); if (state & PTHREAD_CANCEL_ENABLE) { mach_port_t kport = _pthread_tsd_slot(thread, MACH_THREAD_SELF); if (kport) __pthread_markcancel(kport); } return (0); }
pthread_setcancelstate source · pthread_setcancelstate reference
Query/update the cancelability 'state' of a thread
PTHREAD_NOEXPORT_VARIANT int pthread_setcancelstate(int state, int *oldstateptr) { pthread_t self = pthread_self(); _pthread_validate_signature(self); switch (state) { case PTHREAD_CANCEL_ENABLE: __pthread_canceled(1); break; case PTHREAD_CANCEL_DISABLE: __pthread_canceled(2); break; default: return EINVAL; } int oldstate = _pthread_update_cancel_state(self, _PTHREAD_CANCEL_STATE_MASK, state); if (oldstateptr) { *oldstateptr = oldstate & _PTHREAD_CANCEL_STATE_MASK; } return 0; }
pthread_setcanceltype source · pthread_setcanceltype reference
Query/update the cancelability 'type' of a thread
PTHREAD_NOEXPORT_VARIANT int pthread_setcanceltype(int type, int *oldtype) { pthread_t self = pthread_self(); _pthread_validate_signature(self); if ((type != PTHREAD_CANCEL_DEFERRED) && (type != PTHREAD_CANCEL_ASYNCHRONOUS)) return EINVAL; int oldstate = _pthread_update_cancel_state(self, _PTHREAD_CANCEL_TYPE_MASK, type); if (oldtype) { *oldtype = oldstate & _PTHREAD_CANCEL_TYPE_MASK; } return (0); }
_pthread_is_canceled source
OS_ALWAYS_INLINE static inline bool _pthread_is_canceled(pthread_t thread) { const int flags = (PTHREAD_CANCEL_ENABLE|_PTHREAD_CANCEL_PENDING); int state = os_atomic_load(&thread->cancel_state, seq_cst); return (state & flags) == flags; }
_pthread_get_exit_value source
OS_ALWAYS_INLINE static inline void * _pthread_get_exit_value(pthread_t thread) { if (os_unlikely(_pthread_is_canceled(thread))) { return PTHREAD_CANCELED; } return thread->tl_exit_value; }
pthread_testcancel source · pthread_testcancel reference
void pthread_testcancel(void) { pthread_t self = pthread_self(); if (os_unlikely(_pthread_is_canceled(self))) { _pthread_validate_signature(self); // 4597450: begin self->canceled = true; // 4597450: end pthread_exit(PTHREAD_CANCELED); } }
_pthread_markcancel_if_canceled source
void _pthread_markcancel_if_canceled(pthread_t thread, mach_port_t kport) { if (os_unlikely(_pthread_is_canceled(thread))) { __pthread_markcancel(kport); } }
_pthread_exit_if_canceled source
void _pthread_exit_if_canceled(int error) { if ((error & 0xff) == EINTR && __pthread_canceled(0) == 0) { pthread_t self = pthread_self(); _pthread_validate_signature(self); self->cancel_error = error; self->canceled = true; pthread_exit(PTHREAD_CANCELED); } }
pthread_sigmask source · pthread_sigmask reference
int pthread_sigmask(int how, const sigset_t * set, sigset_t * oset) { int err = 0; if (__pthread_sigmask(how, set, oset) == -1) { err = errno; } return(err); }
_pthread_joiner_prepost_wake source
called with _pthread_list_lock held
semaphore_t _pthread_joiner_prepost_wake(pthread_t thread) { pthread_join_context_t ctx = thread->tl_join_ctx; semaphore_t sema = MACH_PORT_NULL; if (thread->tl_joinable) { sema = ctx->custom_stack_sema; thread->tl_joinable = false; } else { ctx->detached = true; thread->tl_join_ctx = NULL; } if (ctx->value_ptr) *ctx->value_ptr = _pthread_get_exit_value(thread); return sema; }
_pthread_joiner_abort_wait source
static inline bool _pthread_joiner_abort_wait(pthread_t thread, pthread_join_context_t ctx) { bool aborted = false; _pthread_lock_lock(&_pthread_list_lock); if (!ctx->detached && thread->tl_exit_gate != MACH_PORT_DEAD) { /* * _pthread_joiner_prepost_wake() didn't happen * allow another thread to join */ PTHREAD_DEBUG_ASSERT(thread->tl_join_ctx == ctx); thread->tl_join_ctx = NULL; thread->tl_exit_gate = MACH_PORT_NULL; aborted = true; } _pthread_lock_unlock(&_pthread_list_lock); return aborted; }
_pthread_joiner_wait source
static int _pthread_joiner_wait(pthread_t thread, pthread_join_context_t ctx, pthread_conformance_t conforming) { uint32_t *exit_gate = &thread->tl_exit_gate; int ulock_op = UL_UNFAIR_LOCK | ULF_NO_ERRNO; if (conforming == PTHREAD_CONFORM_UNIX03_CANCELABLE) { ulock_op |= ULF_WAIT_CANCEL_POINT; } for (;;) { uint32_t cur = os_atomic_load(exit_gate, acquire); if (cur == MACH_PORT_DEAD) { break; } if (os_unlikely(cur != ctx->kport)) { PTHREAD_CLIENT_CRASH(cur, "pthread_join() state corruption"); } int ret = __ulock_wait(ulock_op, exit_gate, ctx->kport, 0); switch (-ret) { case 0: case EFAULT: break; case EINTR: /* * POSIX says: * * As specified, either the pthread_join() call is canceled, or it * succeeds, but not both. The difference is obvious to the … more in source
_pthread_join source
OS_NOINLINE int _pthread_join(pthread_t thread, void **value_ptr, pthread_conformance_t conforming) { pthread_t self = pthread_self(); pthread_join_context_s ctx = { .waiter = self, .value_ptr = value_ptr, .kport = MACH_PORT_NULL, .custom_stack_sema = MACH_PORT_NULL, }; int res = 0; kern_return_t kr; if (!_pthread_validate_thread_and_list_lock(thread)) { return ESRCH; } _pthread_validate_signature(self); if (!thread->tl_joinable || (thread->tl_join_ctx != NULL)) { res = EINVAL; } else if (thread == self || (self->tl_join_ctx && self->tl_join_ctx->waiter == thread)) { res = EDEADLK; } else if (thread->tl_exit_gate == MACH_PORT_DEAD) { TAILQ_REMOVE(&__pthread_head, thread, tl_plist); PTHREAD_DEBUG_ASSERT(thread->tl_joiner_cleans_up); thread->tl_joinable = false; if (value_ptr) *value_ptr = _pthread_get_exit_value(thread); … more in source
_pthread_conformance source
!BUILDING_VARIANT ]
static inline pthread_conformance_t _pthread_conformance(void) { #ifdef VARIANT_CANCELABLE return PTHREAD_CONFORM_UNIX03_CANCELABLE; #else /* !VARIANT_CANCELABLE */ return PTHREAD_CONFORM_UNIX03_NOCANCEL; #endif }
_pthread_testcancel_if_cancelable_variant source
static inline void _pthread_testcancel_if_cancelable_variant(void) { #ifdef VARIANT_CANCELABLE pthread_testcancel(); #endif }
pthread_join source · pthread_join reference
int pthread_join(pthread_t thread, void **value_ptr) { _pthread_testcancel_if_cancelable_variant(); return _pthread_join(thread, value_ptr, _pthread_conformance()); }
pthread_cond_wait source · pthread_cond_wait reference
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) { return _pthread_cond_wait(cond, mutex, NULL, 0, _pthread_conformance()); }
pthread_cond_timedwait source · pthread_cond_timedwait reference
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime) { return _pthread_cond_wait(cond, mutex, abstime, 0, _pthread_conformance()); }
sigwait source · sigwait reference
int sigwait(const sigset_t * set, int * sig) { int err = 0; _pthread_testcancel_if_cancelable_variant(); if (__sigwait(set, sig) == -1) { err = errno; _pthread_testcancel_if_cancelable_variant(); /* * EINTR that isn't a result of pthread_cancel() * is translated to 0. */ if (err == EINTR) { err = 0; } } return(err); }