summaryrefslogtreecommitdiffstats
path: root/lib/libthr/thread/thr_cancel.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libthr/thread/thr_cancel.c')
-rw-r--r--lib/libthr/thread/thr_cancel.c233
1 files changed, 233 insertions, 0 deletions
diff --git a/lib/libthr/thread/thr_cancel.c b/lib/libthr/thread/thr_cancel.c
new file mode 100644
index 0000000..7e1bace
--- /dev/null
+++ b/lib/libthr/thread/thr_cancel.c
@@ -0,0 +1,233 @@
+/*
+ * David Leonard <d@openbsd.org>, 1999. Public domain.
+ * $FreeBSD$
+ */
+#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);
+
+int
+_pthread_cancel(pthread_t pthread)
+{
+ int ret;
+ pthread_t joined;
+
+ /*
+ * When canceling a thread that has joined another thread, this
+ * routine breaks the normal lock order of locking first the
+ * joined and then the joiner. Therefore, it is necessary that
+ * if it can't obtain the second lock, that it release the first
+ * one and restart from the top.
+ */
+retry:
+ if ((ret = _find_thread(pthread)) != 0)
+ /* The thread is not on the list of active threads */
+ goto out;
+
+ _thread_critical_enter(pthread);
+
+ if (pthread->state == PS_DEAD || pthread->state == PS_DEADLOCK
+ || (pthread->flags & PTHREAD_EXITING) != 0) {
+ /*
+ * The thread is in the process of (or has already) exited
+ * or is deadlocked.
+ */
+ _thread_critical_exit(pthread);
+ ret = 0;
+ goto out;
+ }
+
+ /*
+ * The thread is on the active thread list and is not in the process
+ * of exiting.
+ */
+
+ if (((pthread->cancelflags & PTHREAD_CANCEL_DISABLE) != 0) ||
+ (((pthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) == 0) &&
+ ((pthread->cancelflags & PTHREAD_AT_CANCEL_POINT) == 0)))
+ /* Just mark it for cancellation: */
+ pthread->cancelflags |= PTHREAD_CANCELLING;
+ else {
+ /*
+ * Check if we need to kick it back into the
+ * run queue:
+ */
+ switch (pthread->state) {
+ case PS_RUNNING:
+ /* No need to resume: */
+ pthread->cancelflags |= PTHREAD_CANCELLING;
+ break;
+
+ case PS_SLEEP_WAIT:
+ case PS_WAIT_WAIT:
+ pthread->cancelflags |= PTHREAD_CANCELLING;
+ PTHREAD_NEW_STATE(pthread, PS_RUNNING);
+ break;
+
+ case PS_JOIN:
+ /*
+ * Disconnect the thread from the joinee:
+ */
+ if ((joined = pthread->join_status.thread) != NULL) {
+ if (_spintrylock(&joined->lock) == EBUSY) {
+ _thread_critical_exit(pthread);
+ goto retry;
+ }
+ pthread->join_status.thread->joiner = NULL;
+ _spinunlock(&joined->lock);
+ joined = pthread->join_status.thread = NULL;
+ }
+ pthread->cancelflags |= PTHREAD_CANCELLING;
+ PTHREAD_NEW_STATE(pthread, PS_RUNNING);
+ break;
+
+ case PS_MUTEX_WAIT:
+ case PS_COND_WAIT:
+ /*
+ * Threads in these states may be in queues.
+ * In order to preserve queue integrity, the
+ * cancelled thread must remove itself from the
+ * queue. When the thread resumes, it will
+ * remove itself from the queue and call the
+ * cancellation routine.
+ */
+ pthread->cancelflags |= PTHREAD_CANCELLING;
+ PTHREAD_NEW_STATE(pthread, PS_RUNNING);
+ break;
+
+ case PS_DEAD:
+ case PS_DEADLOCK:
+ case PS_STATE_MAX:
+ /* Ignore - only here to silence -Wall: */
+ break;
+ }
+ }
+
+ /* Unprotect the scheduling queues: */
+ _thread_critical_exit(pthread);
+
+ ret = 0;
+out:
+ return (ret);
+}
+
+int
+_pthread_setcancelstate(int state, int *oldstate)
+{
+ int ostate, ret;
+
+ ret = 0;
+
+ _thread_critical_enter(curthread);
+
+ ostate = curthread->cancelflags & PTHREAD_CANCEL_DISABLE;
+
+ switch (state) {
+ case PTHREAD_CANCEL_ENABLE:
+ if (oldstate != NULL)
+ *oldstate = ostate;
+ curthread->cancelflags &= ~PTHREAD_CANCEL_DISABLE;
+ if ((curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) == 0)
+ break;
+ testcancel();
+ break;
+ case PTHREAD_CANCEL_DISABLE:
+ if (oldstate != NULL)
+ *oldstate = ostate;
+ curthread->cancelflags |= PTHREAD_CANCEL_DISABLE;
+ break;
+ default:
+ ret = EINVAL;
+ }
+
+ _thread_critical_exit(curthread);
+ return (ret);
+}
+
+int
+_pthread_setcanceltype(int type, int *oldtype)
+{
+ int otype;
+
+ _thread_critical_enter(curthread);
+ otype = curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS;
+ switch (type) {
+ case PTHREAD_CANCEL_ASYNCHRONOUS:
+ if (oldtype != NULL)
+ *oldtype = otype;
+ curthread->cancelflags |= PTHREAD_CANCEL_ASYNCHRONOUS;
+ testcancel();
+ break;
+ case PTHREAD_CANCEL_DEFERRED:
+ if (oldtype != NULL)
+ *oldtype = otype;
+ curthread->cancelflags &= ~PTHREAD_CANCEL_ASYNCHRONOUS;
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ _thread_critical_exit(curthread);
+ return (0);
+}
+
+void
+_pthread_testcancel(void)
+{
+ _thread_critical_enter(curthread);
+ testcancel();
+ _thread_critical_exit(curthread);
+}
+
+static void
+testcancel()
+{
+ /*
+ * This pthread should already be locked by the caller.
+ */
+
+ if (((curthread->cancelflags & PTHREAD_CANCEL_DISABLE) == 0) &&
+ ((curthread->cancelflags & PTHREAD_CANCELLING) != 0) &&
+ ((curthread->flags & PTHREAD_EXITING) == 0)) {
+ /*
+ * It is possible for this thread to be swapped out
+ * while performing cancellation; do not allow it
+ * to be cancelled again.
+ */
+ curthread->cancelflags &= ~PTHREAD_CANCELLING;
+ _thread_critical_exit(curthread);
+ _thread_exit_cleanup();
+ pthread_exit(PTHREAD_CANCELED);
+ PANIC("cancel");
+ }
+}
+
+void
+_thread_enter_cancellation_point(void)
+{
+ _thread_critical_enter(curthread);
+ testcancel();
+ curthread->cancelflags |= PTHREAD_AT_CANCEL_POINT;
+ _thread_critical_exit(curthread);
+}
+
+void
+_thread_leave_cancellation_point(void)
+{
+ _thread_critical_enter(curthread);
+ curthread->cancelflags &= ~PTHREAD_AT_CANCEL_POINT;
+ testcancel();
+ _thread_critical_exit(curthread);
+
+}
OpenPOWER on IntegriCloud