FreeBSD-5.3/lib/libthr/thread/thr_cancel.c

Compare this file to the similar file:
Show the results in this format:

/*
 * David Leonard <d@openbsd.org>, 1999. Public domain.
 * $FreeBSD: src/lib/libthr/thread/thr_cancel.c,v 1.10 2004/05/20 12:06:16 mtm Exp $
 */
#include <sys/errno.h>
#include <pthread.h>
#include <stdlib.h>
#include "thr_private.h"

/*
 * Static prototypes
 */
static void	testcancel(void);

__weak_reference(_pthread_cancel, pthread_cancel);
__weak_reference(_pthread_setcancelstate, pthread_setcancelstate);
__weak_reference(_pthread_setcanceltype, pthread_setcanceltype);
__weak_reference(_pthread_testcancel, pthread_testcancel);

/*
 * Posix requires this function to be async-cancel-safe, so it
 * may not aquire any type of resource or call any functions
 * that might do so.
 */
int
_pthread_cancel(pthread_t pthread)
{
	/* Don't continue if cancellation has already been set. */
	if (atomic_cmpset_int(&pthread->cancellation, (int)CS_NULL,
	    (int)CS_PENDING) != 1)
		return (0);

	/*
	 * Only wakeup threads that are in cancellation points or
	 * have set async cancel.
	 * XXX - access to pthread->flags is not safe. We should just
	 *	 unconditionally wake the thread and make sure that
	 *	 the the library correctly handles spurious wakeups.
	 */
	if ((pthread->cancellationpoint || pthread->cancelmode == M_ASYNC) &&
	    (pthread->flags & PTHREAD_FLAGS_NOT_RUNNING) != 0)
		PTHREAD_WAKE(pthread);
	return (0);
}

/*
 * Posix requires this function to be async-cancel-safe, so it
 * may not aquire any type of resource or call any functions
 * that might do so.
 */
int
_pthread_setcancelstate(int state, int *oldstate)
{
	int ostate;

	ostate = (curthread->cancelmode == M_OFF) ? PTHREAD_CANCEL_DISABLE :
	    PTHREAD_CANCEL_ENABLE;
	switch (state) {
	case PTHREAD_CANCEL_ENABLE:
		curthread->cancelmode = curthread->cancelstate;
		break;
	case PTHREAD_CANCEL_DISABLE:
		if (curthread->cancelmode != M_OFF) {
			curthread->cancelstate = curthread->cancelmode;
			curthread->cancelmode = M_OFF;
		}
		break;
	default:
		return (EINVAL);
	}
	if (oldstate != NULL)
		*oldstate = ostate;
	return (0);
}

/*
 * Posix requires this function to be async-cancel-safe, so it
 * may not aquire any type of resource or call any functions that
 * might do so.
 */
int
_pthread_setcanceltype(int type, int *oldtype)
{
	enum cancel_mode omode;

	omode = curthread->cancelstate;
	switch (type) {
	case PTHREAD_CANCEL_ASYNCHRONOUS:
		if (curthread->cancelmode != M_OFF)
			curthread->cancelmode = M_ASYNC;
		curthread->cancelstate = M_ASYNC;
		break;
	case PTHREAD_CANCEL_DEFERRED:
		if (curthread->cancelmode != M_OFF)
			curthread->cancelmode = M_DEFERRED;
		curthread->cancelstate = M_DEFERRED;
		break;
	default:
		return (EINVAL);
	}
	if (oldtype != NULL) {
		if (omode == M_DEFERRED)
			*oldtype = PTHREAD_CANCEL_DEFERRED;
		else if (omode == M_ASYNC)
			*oldtype = PTHREAD_CANCEL_ASYNCHRONOUS;
	}
	return (0);
}

void
_pthread_testcancel(void)
{
	testcancel();
}

static void
testcancel()
{
	if (curthread->cancelmode != M_OFF) {

		/* Cleanup a canceled thread only once. */
		if (atomic_cmpset_int(&curthread->cancellation,
		    (int)CS_PENDING, (int)CS_SET) == 1) {
			_thread_exit_cleanup();
			pthread_exit(PTHREAD_CANCELED);
			PANIC("cancel");
		}
	}
}

void
_thread_enter_cancellation_point(void)
{
	testcancel();
	curthread->cancellationpoint = 1;
}

void
_thread_leave_cancellation_point(void)
{
	curthread->cancellationpoint = 0;
	testcancel();
}