about summary refs log tree commit diff
path: root/linuxthreads/cancel.c
diff options
context:
space:
mode:
authorUlrich Drepper <drepper@redhat.com>2004-12-22 20:10:10 +0000
committerUlrich Drepper <drepper@redhat.com>2004-12-22 20:10:10 +0000
commita334319f6530564d22e775935d9c91663623a1b4 (patch)
treeb5877475619e4c938e98757d518bb1e9cbead751 /linuxthreads/cancel.c
parent0ecb606cb6cf65de1d9fc8a919bceb4be476c602 (diff)
downloadglibc-a334319f6530564d22e775935d9c91663623a1b4.tar.gz
glibc-a334319f6530564d22e775935d9c91663623a1b4.tar.xz
glibc-a334319f6530564d22e775935d9c91663623a1b4.zip
(CFLAGS-tst-align.c): Add -mpreferred-stack-boundary=4.
Diffstat (limited to 'linuxthreads/cancel.c')
-rw-r--r--linuxthreads/cancel.c234
1 files changed, 234 insertions, 0 deletions
diff --git a/linuxthreads/cancel.c b/linuxthreads/cancel.c
new file mode 100644
index 0000000000..d8053ca899
--- /dev/null
+++ b/linuxthreads/cancel.c
@@ -0,0 +1,234 @@
+/* Linuxthreads - a simple clone()-based implementation of Posix        */
+/* threads for Linux.                                                   */
+/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr)              */
+/*                                                                      */
+/* This program is free software; you can redistribute it and/or        */
+/* modify it under the terms of the GNU Library General Public License  */
+/* as published by the Free Software Foundation; either version 2       */
+/* of the License, or (at your option) any later version.               */
+/*                                                                      */
+/* This program is distributed in the hope that it will be useful,      */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of       */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        */
+/* GNU Library General Public License for more details.                 */
+
+/* Thread cancellation */
+
+#include <errno.h>
+#include <libc-internal.h>
+#include "pthread.h"
+#include "internals.h"
+#include "spinlock.h"
+#include "restart.h"
+
+#ifdef _STACK_GROWS_DOWN
+# define FRAME_LEFT(frame, other) ((char *) frame >= (char *) other)
+#elif _STACK_GROWS_UP
+# define FRAME_LEFT(frame, other) ((char *) frame <= (char *) other)
+#else
+# error "Define either _STACK_GROWS_DOWN or _STACK_GROWS_UP"
+#endif
+
+
+int __pthread_setcancelstate(int state, int * oldstate)
+{
+  pthread_descr self = thread_self();
+  if (state < PTHREAD_CANCEL_ENABLE || state > PTHREAD_CANCEL_DISABLE)
+    return EINVAL;
+  if (oldstate != NULL) *oldstate = THREAD_GETMEM(self, p_cancelstate);
+  THREAD_SETMEM(self, p_cancelstate, state);
+  if (THREAD_GETMEM(self, p_canceled) &&
+      THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE &&
+      THREAD_GETMEM(self, p_canceltype) == PTHREAD_CANCEL_ASYNCHRONOUS)
+    __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
+  return 0;
+}
+strong_alias (__pthread_setcancelstate, pthread_setcancelstate);
+
+int __pthread_setcanceltype(int type, int * oldtype)
+{
+  pthread_descr self = thread_self();
+  if (type < PTHREAD_CANCEL_DEFERRED || type > PTHREAD_CANCEL_ASYNCHRONOUS)
+    return EINVAL;
+  if (oldtype != NULL) *oldtype = THREAD_GETMEM(self, p_canceltype);
+  THREAD_SETMEM(self, p_canceltype, type);
+  if (THREAD_GETMEM(self, p_canceled) &&
+      THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE &&
+      THREAD_GETMEM(self, p_canceltype) == PTHREAD_CANCEL_ASYNCHRONOUS)
+    __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
+  return 0;
+}
+strong_alias (__pthread_setcanceltype, pthread_setcanceltype);
+
+
+/* The next two functions are similar to pthread_setcanceltype() but
+   more specialized for the use in the cancelable functions like write().
+   They do not need to check parameters etc.  */
+int
+attribute_hidden
+__pthread_enable_asynccancel (void)
+{
+  pthread_descr self = thread_self();
+  int oldtype = THREAD_GETMEM(self, p_canceltype);
+  THREAD_SETMEM(self, p_canceltype, PTHREAD_CANCEL_ASYNCHRONOUS);
+  if (__builtin_expect (THREAD_GETMEM(self, p_canceled), 0) &&
+      THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE)
+    __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
+  return oldtype;
+}
+
+void
+internal_function attribute_hidden
+__pthread_disable_asynccancel (int oldtype)
+{
+  pthread_descr self = thread_self();
+  THREAD_SETMEM(self, p_canceltype, oldtype);
+}
+
+
+int pthread_cancel(pthread_t thread)
+{
+  pthread_handle handle = thread_handle(thread);
+  int pid;
+  int dorestart = 0;
+  pthread_descr th;
+  pthread_extricate_if *pextricate;
+  int already_canceled;
+
+  __pthread_lock(&handle->h_lock, NULL);
+  if (invalid_handle(handle, thread)) {
+    __pthread_unlock(&handle->h_lock);
+    return ESRCH;
+  }
+
+  th = handle->h_descr;
+
+  already_canceled = th->p_canceled;
+  th->p_canceled = 1;
+
+  if (th->p_cancelstate == PTHREAD_CANCEL_DISABLE || already_canceled) {
+    __pthread_unlock(&handle->h_lock);
+    return 0;
+  }
+
+  pextricate = th->p_extricate;
+  pid = th->p_pid;
+
+  /* If the thread has registered an extrication interface, then
+     invoke the interface. If it returns 1, then we succeeded in
+     dequeuing the thread from whatever waiting object it was enqueued
+     with. In that case, it is our responsibility to wake it up.
+     And also to set the p_woken_by_cancel flag so the woken thread
+     can tell that it was woken by cancellation. */
+
+  if (pextricate != NULL) {
+    dorestart = pextricate->pu_extricate_func(pextricate->pu_object, th);
+    th->p_woken_by_cancel = dorestart;
+  }
+
+  __pthread_unlock(&handle->h_lock);
+
+  /* If the thread has suspended or is about to, then we unblock it by
+     issuing a restart, instead of a cancel signal. Otherwise we send
+     the cancel signal to unblock the thread from a cancellation point,
+     or to initiate asynchronous cancellation. The restart is needed so
+     we have proper accounting of restarts; suspend decrements the thread's
+     resume count, and restart() increments it.  This also means that suspend's
+     handling of the cancel signal is obsolete. */
+
+  if (dorestart)
+    restart(th);
+  else
+    kill(pid, __pthread_sig_cancel);
+
+  return 0;
+}
+
+void pthread_testcancel(void)
+{
+  pthread_descr self = thread_self();
+  if (THREAD_GETMEM(self, p_canceled)
+      && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE)
+    __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
+}
+
+void _pthread_cleanup_push(struct _pthread_cleanup_buffer * buffer,
+			   void (*routine)(void *), void * arg)
+{
+  pthread_descr self = thread_self();
+  buffer->__routine = routine;
+  buffer->__arg = arg;
+  buffer->__prev = THREAD_GETMEM(self, p_cleanup);
+  if (buffer->__prev != NULL && FRAME_LEFT (buffer, buffer->__prev))
+    buffer->__prev = NULL;
+  THREAD_SETMEM(self, p_cleanup, buffer);
+}
+
+void _pthread_cleanup_pop(struct _pthread_cleanup_buffer * buffer,
+			  int execute)
+{
+  pthread_descr self = thread_self();
+  if (execute) buffer->__routine(buffer->__arg);
+  THREAD_SETMEM(self, p_cleanup, buffer->__prev);
+}
+
+void _pthread_cleanup_push_defer(struct _pthread_cleanup_buffer * buffer,
+				 void (*routine)(void *), void * arg)
+{
+  pthread_descr self = thread_self();
+  buffer->__routine = routine;
+  buffer->__arg = arg;
+  buffer->__canceltype = THREAD_GETMEM(self, p_canceltype);
+  buffer->__prev = THREAD_GETMEM(self, p_cleanup);
+  if (buffer->__prev != NULL && FRAME_LEFT (buffer, buffer->__prev))
+    buffer->__prev = NULL;
+  THREAD_SETMEM(self, p_canceltype, PTHREAD_CANCEL_DEFERRED);
+  THREAD_SETMEM(self, p_cleanup, buffer);
+}
+
+void _pthread_cleanup_pop_restore(struct _pthread_cleanup_buffer * buffer,
+				  int execute)
+{
+  pthread_descr self = thread_self();
+  if (execute) buffer->__routine(buffer->__arg);
+  THREAD_SETMEM(self, p_cleanup, buffer->__prev);
+  THREAD_SETMEM(self, p_canceltype, buffer->__canceltype);
+  if (THREAD_GETMEM(self, p_canceled) &&
+      THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE &&
+      THREAD_GETMEM(self, p_canceltype) == PTHREAD_CANCEL_ASYNCHRONOUS)
+    __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
+}
+
+void __pthread_perform_cleanup(char *currentframe)
+{
+  pthread_descr self = thread_self();
+  struct _pthread_cleanup_buffer *c = THREAD_GETMEM(self, p_cleanup);
+  struct _pthread_cleanup_buffer *last;
+
+  if (c != NULL)
+    while (FRAME_LEFT (currentframe, c))
+      {
+	last = c;
+	c = c->__prev;
+
+	if (c == NULL || FRAME_LEFT (last, c))
+	  {
+	    c = NULL;
+	    break;
+	  }
+      }
+
+  while (c != NULL)
+    {
+      c->__routine(c->__arg);
+
+      last = c;
+      c = c->__prev;
+
+      if (FRAME_LEFT (last, c))
+	break;
+    }
+
+  /* And the TSD which needs special help.  */
+  __libc_thread_freeres ();
+}