summaryrefslogtreecommitdiffstats
path: root/lib/libc_r/uthread/uthread_cancel.c
diff options
context:
space:
mode:
authoralfred <alfred@FreeBSD.org>1999-11-28 05:38:13 +0000
committeralfred <alfred@FreeBSD.org>1999-11-28 05:38:13 +0000
commite7efcb5302ff3b4faef7cf619f51a4b4a509f09a (patch)
tree295e248e2503f8c817b16353f285d30439bf4bf4 /lib/libc_r/uthread/uthread_cancel.c
parentdff67f0b84733be29e807fc281e817bc8639fd70 (diff)
downloadFreeBSD-src-e7efcb5302ff3b4faef7cf619f51a4b4a509f09a.zip
FreeBSD-src-e7efcb5302ff3b4faef7cf619f51a4b4a509f09a.tar.gz
add pthread_cancel, obtained from OpenBSD.
eischen (Daniel Eischen) added wrappers to protect against cancled threads orphaning internal resources. the cancelability code is still a bit fuzzy but works for test programs of my own, OpenBSD's and some examples from ORA's books. add readdir_r to both libc and libc_r add some 'const' attributes to function parameters Reviewed by: eischen, jasone
Diffstat (limited to 'lib/libc_r/uthread/uthread_cancel.c')
-rw-r--r--lib/libc_r/uthread/uthread_cancel.c179
1 files changed, 179 insertions, 0 deletions
diff --git a/lib/libc_r/uthread/uthread_cancel.c b/lib/libc_r/uthread/uthread_cancel.c
new file mode 100644
index 0000000..bad5533
--- /dev/null
+++ b/lib/libc_r/uthread/uthread_cancel.c
@@ -0,0 +1,179 @@
+/*
+ * David Leonard <d@openbsd.org>, 1999. Public domain.
+ * $FreeBSD$
+ */
+
+#include <sys/errno.h>
+#include <pthread.h>
+#include "pthread_private.h"
+
+int
+pthread_cancel(pthread_t pthread)
+{
+ int ret;
+
+ if ((ret = _find_thread(pthread)) != 0) {
+ /* NOTHING */
+ } else if (pthread->state == PS_DEAD || pthread->state == PS_DEADLOCK) {
+ ret = 0;
+ } else {
+ /* Protect the scheduling queues: */
+ _thread_kern_sig_defer();
+
+ /* Check if we need to kick it back into the run queue: */
+ if ((pthread->cancelflags & PTHREAD_CANCEL_DISABLE) == 0)
+ switch (pthread->state) {
+ case PS_RUNNING:
+ /* No need to resume: */
+ pthread->cancelflags |= PTHREAD_CANCELLING;
+ break;
+
+ case PS_SPINBLOCK:
+ case PS_FDR_WAIT:
+ case PS_FDW_WAIT:
+ case PS_POLL_WAIT:
+ case PS_SELECT_WAIT:
+ /* Remove these threads from the work queue: */
+ if ((pthread->flags & PTHREAD_FLAGS_IN_WORKQ)
+ != 0)
+ PTHREAD_WORKQ_REMOVE(pthread);
+ /* Fall through: */
+ case PS_SIGTHREAD:
+ case PS_SLEEP_WAIT:
+ case PS_WAIT_WAIT:
+ case PS_SIGSUSPEND:
+ case PS_SIGWAIT:
+ case PS_SUSPENDED:
+ /* Interrupt and resume: */
+ pthread->interrupted = 1;
+ pthread->cancelflags |= PTHREAD_CANCELLING;
+ PTHREAD_NEW_STATE(pthread,PS_RUNNING);
+ break;
+
+ case PS_MUTEX_WAIT:
+ case PS_COND_WAIT:
+ case PS_FDLR_WAIT:
+ case PS_FDLW_WAIT:
+ case PS_FILE_WAIT:
+ case PS_JOIN:
+ /*
+ * Threads in these states may be in queues.
+ * In order to preserve queue integrity, the
+ * cancelled thread must remove itself from the
+ * queue. Mark the thread as interrupted and
+ * needing cancellation, and set the state to
+ * running. When the thread resumes, it will
+ * exit after removing itself from the queue.
+ */
+ pthread->interrupted = 1;
+ pthread->cancelflags |= PTHREAD_CANCEL_NEEDED;
+ 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_kern_sig_undefer();
+
+ ret = 0;
+ }
+ return (ret);
+}
+
+int
+pthread_setcancelstate(int state, int *oldstate)
+{
+ int ostate;
+ int ret;
+
+ ostate = _thread_run->cancelflags & PTHREAD_CANCEL_DISABLE;
+
+ switch (state) {
+ case PTHREAD_CANCEL_ENABLE:
+ if (oldstate != NULL)
+ *oldstate = ostate;
+ _thread_run->cancelflags &= PTHREAD_CANCEL_ENABLE;
+ if ((_thread_run->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0)
+ pthread_testcancel();
+ ret = 0;
+ break;
+ case PTHREAD_CANCEL_DISABLE:
+ if (oldstate != NULL)
+ *oldstate = ostate;
+ _thread_run->cancelflags |= PTHREAD_CANCEL_DISABLE;
+ ret = 0;
+ break;
+ default:
+ ret = EINVAL;
+ }
+
+ return (ret);
+}
+
+int
+pthread_setcanceltype(int type, int *oldtype)
+{
+ int otype;
+ int ret;
+
+ otype = _thread_run->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS;
+ switch (type) {
+ case PTHREAD_CANCEL_ASYNCHRONOUS:
+ if (oldtype != NULL)
+ *oldtype = otype;
+ _thread_run->cancelflags |= PTHREAD_CANCEL_ASYNCHRONOUS;
+ pthread_testcancel();
+ ret = 0;
+ break;
+ case PTHREAD_CANCEL_DEFERRED:
+ if (oldtype != NULL)
+ *oldtype = otype;
+ _thread_run->cancelflags &= ~PTHREAD_CANCEL_ASYNCHRONOUS;
+ ret = 0;
+ break;
+ default:
+ ret = EINVAL;
+ }
+
+ return (ret);
+}
+
+void
+pthread_testcancel(void)
+{
+
+ if (((_thread_run->cancelflags & PTHREAD_CANCEL_DISABLE) == 0) &&
+ ((_thread_run->cancelflags & PTHREAD_CANCELLING) != 0)) {
+ /*
+ * It is possible for this thread to be swapped out
+ * while performing cancellation; do not allow it
+ * to be cancelled again.
+ */
+ _thread_run->cancelflags &= ~PTHREAD_CANCELLING;
+ _thread_exit_cleanup();
+ pthread_exit(PTHREAD_CANCELED);
+ PANIC("cancel");
+ }
+}
+
+void
+_thread_enter_cancellation_point(void)
+{
+
+ /* Look for a cancellation before we block: */
+ pthread_testcancel();
+ _thread_run->cancelflags |= PTHREAD_AT_CANCEL_POINT;
+}
+
+void
+_thread_leave_cancellation_point(void)
+{
+
+ _thread_run->cancelflags &= ~PTHREAD_AT_CANCEL_POINT;
+ /* Look for a cancellation after we unblock: */
+ pthread_testcancel();
+}
OpenPOWER on IntegriCloud