Cancellation and join

libpthread/src/pthread_cancelable.c · 466 lines · browse source
_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_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);
}