about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--nptl/cleanup.c32
-rw-r--r--nptl/cleanup_compat.c55
-rw-r--r--nptl/cleanup_defer.c52
-rw-r--r--nptl/cleanup_defer_compat.c98
-rw-r--r--nptl/descr.h50
-rw-r--r--nptl/forward.c10
-rw-r--r--nptl/init.c3
-rw-r--r--nptl/pthreadP.h66
-rw-r--r--nptl/tst-cleanup0.expect3
9 files changed, 282 insertions, 87 deletions
diff --git a/nptl/cleanup.c b/nptl/cleanup.c
index a25b397f9c..2029fe2141 100644
--- a/nptl/cleanup.c
+++ b/nptl/cleanup.c
@@ -22,34 +22,26 @@
 
 
 void
-_pthread_cleanup_push (buffer, routine, arg)
-     struct _pthread_cleanup_buffer *buffer;
-     void (*routine) (void *);
-     void *arg;
+__cleanup_fct_attribute
+__pthread_register_cancel (__pthread_unwind_buf_t *buf)
 {
+  struct pthread_unwind_buf *ibuf = (struct pthread_unwind_buf *) buf;
   struct pthread *self = THREAD_SELF;
 
-  buffer->__routine = routine;
-  buffer->__arg = arg;
-  buffer->__prev = THREAD_GETMEM (self, cleanup);
+  /* Store old info.  */
+  ibuf->priv.data.prev = THREAD_GETMEM (self, cleanup_jmp_buf);
+  ibuf->priv.data.cleanup = THREAD_GETMEM (self, cleanup);
 
-  THREAD_SETMEM (self, cleanup, buffer);
+  /* Store the new cleanup handler info.  */
+  THREAD_SETMEM (self, cleanup_jmp_buf, buf);
 }
-strong_alias (_pthread_cleanup_push, __pthread_cleanup_push)
 
 
 void
-_pthread_cleanup_pop (buffer, execute)
-     struct _pthread_cleanup_buffer *buffer;
-     int execute;
+__cleanup_fct_attribute
+__pthread_unregister_cancel (__pthread_unwind_buf_t *buf)
 {
-  struct pthread *self __attribute ((unused)) = THREAD_SELF;
+  struct pthread_unwind_buf *ibuf = (struct pthread_unwind_buf *) buf;
 
-  THREAD_SETMEM (self, cleanup, buffer->__prev);
-
-  /* If necessary call the cleanup routine after we removed the
-     current cleanup block from the list.  */
-  if (execute)
-    buffer->__routine (buffer->__arg);
+  THREAD_SETMEM (THREAD_SELF, cleanup_jmp_buf, ibuf->priv.data.prev);
 }
-strong_alias (_pthread_cleanup_pop, __pthread_cleanup_pop)
diff --git a/nptl/cleanup_compat.c b/nptl/cleanup_compat.c
new file mode 100644
index 0000000000..a25b397f9c
--- /dev/null
+++ b/nptl/cleanup_compat.c
@@ -0,0 +1,55 @@
+/* Copyright (C) 2002, 2003 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <stdlib.h>
+#include "pthreadP.h"
+
+
+void
+_pthread_cleanup_push (buffer, routine, arg)
+     struct _pthread_cleanup_buffer *buffer;
+     void (*routine) (void *);
+     void *arg;
+{
+  struct pthread *self = THREAD_SELF;
+
+  buffer->__routine = routine;
+  buffer->__arg = arg;
+  buffer->__prev = THREAD_GETMEM (self, cleanup);
+
+  THREAD_SETMEM (self, cleanup, buffer);
+}
+strong_alias (_pthread_cleanup_push, __pthread_cleanup_push)
+
+
+void
+_pthread_cleanup_pop (buffer, execute)
+     struct _pthread_cleanup_buffer *buffer;
+     int execute;
+{
+  struct pthread *self __attribute ((unused)) = THREAD_SELF;
+
+  THREAD_SETMEM (self, cleanup, buffer->__prev);
+
+  /* If necessary call the cleanup routine after we removed the
+     current cleanup block from the list.  */
+  if (execute)
+    buffer->__routine (buffer->__arg);
+}
+strong_alias (_pthread_cleanup_pop, __pthread_cleanup_pop)
diff --git a/nptl/cleanup_defer.c b/nptl/cleanup_defer.c
index 7945a0ddbb..4c67813d4e 100644
--- a/nptl/cleanup_defer.c
+++ b/nptl/cleanup_defer.c
@@ -17,20 +17,20 @@
    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
    02111-1307 USA.  */
 
+#include <stdlib.h>
 #include "pthreadP.h"
 
 
 void
-_pthread_cleanup_push_defer (buffer, routine, arg)
-     struct _pthread_cleanup_buffer *buffer;
-     void (*routine) (void *);
-     void *arg;
+__cleanup_fct_attribute
+__pthread_register_cancel_defer (__pthread_unwind_buf_t *buf)
 {
+  struct pthread_unwind_buf *ibuf = (struct pthread_unwind_buf *) buf;
   struct pthread *self = THREAD_SELF;
 
-  buffer->__routine = routine;
-  buffer->__arg = arg;
-  buffer->__prev = THREAD_GETMEM (self, cleanup);
+  /* Store old info.  */
+  ibuf->priv.data.prev = THREAD_GETMEM (self, cleanup_jmp_buf);
+  ibuf->priv.data.cleanup = THREAD_GETMEM (self, cleanup);
 
   int cancelhandling = THREAD_GETMEM (self, cancelhandling);
 
@@ -38,61 +38,55 @@ _pthread_cleanup_push_defer (buffer, routine, arg)
   if (__builtin_expect (cancelhandling & CANCELTYPE_BITMASK, 0))
     while (1)
       {
-	int newval = THREAD_ATOMIC_CMPXCHG_VAL (self, cancelhandling,
+	int curval = THREAD_ATOMIC_CMPXCHG_VAL (self, cancelhandling,
 						cancelhandling
 						& ~CANCELTYPE_BITMASK,
 						cancelhandling);
-	if (__builtin_expect (newval == cancelhandling, 1))
+	if (__builtin_expect (curval == cancelhandling, 1))
 	  /* Successfully replaced the value.  */
 	  break;
 
 	/* Prepare for the next round.  */
-	cancelhandling = newval;
+	cancelhandling = curval;
       }
 
-  buffer->__canceltype = (cancelhandling & CANCELTYPE_BITMASK
-			  ? PTHREAD_CANCEL_ASYNCHRONOUS
-			  : PTHREAD_CANCEL_DEFERRED);
+  ibuf->priv.data.canceltype = (cancelhandling & CANCELTYPE_BITMASK
+				? PTHREAD_CANCEL_ASYNCHRONOUS
+				: PTHREAD_CANCEL_DEFERRED);
 
-  THREAD_SETMEM (self, cleanup, buffer);
+  /* Store the new cleanup handler info.  */
+  THREAD_SETMEM (self, cleanup_jmp_buf, buf);
 }
-strong_alias (_pthread_cleanup_push_defer, __pthread_cleanup_push_defer)
 
 
 void
-_pthread_cleanup_pop_restore (buffer, execute)
-     struct _pthread_cleanup_buffer *buffer;
-     int execute;
+__cleanup_fct_attribute
+__pthread_unregister_cancel_restore (__pthread_unwind_buf_t *buf)
 {
   struct pthread *self = THREAD_SELF;
+  struct pthread_unwind_buf *ibuf = (struct pthread_unwind_buf *) buf;
 
-  THREAD_SETMEM (self, cleanup, buffer->__prev);
+  THREAD_SETMEM (self, cleanup_jmp_buf, ibuf->priv.data.prev);
 
   int cancelhandling;
-  if (__builtin_expect (buffer->__canceltype != PTHREAD_CANCEL_DEFERRED, 0)
+  if (ibuf->priv.data.canceltype != PTHREAD_CANCEL_DEFERRED
       && ((cancelhandling = THREAD_GETMEM (self, cancelhandling))
 	  & CANCELTYPE_BITMASK) == 0)
     {
       while (1)
 	{
-	  int newval = THREAD_ATOMIC_CMPXCHG_VAL (self, cancelhandling,
+	  int curval = THREAD_ATOMIC_CMPXCHG_VAL (self, cancelhandling,
 						  cancelhandling
 						  | CANCELTYPE_BITMASK,
 						  cancelhandling);
-	  if (__builtin_expect (newval == cancelhandling, 1))
+	  if (__builtin_expect (curval == cancelhandling, 1))
 	    /* Successfully replaced the value.  */
 	    break;
 
 	  /* Prepare for the next round.  */
-	  cancelhandling = newval;
+	  cancelhandling = curval;
 	}
 
       CANCELLATION_P (self);
     }
-
-  /* If necessary call the cleanup routine after we removed the
-     current cleanup block from the list.  */
-  if (execute)
-    buffer->__routine (buffer->__arg);
 }
-strong_alias (_pthread_cleanup_pop_restore, __pthread_cleanup_pop_restore)
diff --git a/nptl/cleanup_defer_compat.c b/nptl/cleanup_defer_compat.c
new file mode 100644
index 0000000000..a0ed6da88c
--- /dev/null
+++ b/nptl/cleanup_defer_compat.c
@@ -0,0 +1,98 @@
+/* Copyright (C) 2002, 2003 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include "pthreadP.h"
+
+
+void
+_pthread_cleanup_push_defer (buffer, routine, arg)
+     struct _pthread_cleanup_buffer *buffer;
+     void (*routine) (void *);
+     void *arg;
+{
+  struct pthread *self = THREAD_SELF;
+
+  buffer->__routine = routine;
+  buffer->__arg = arg;
+  buffer->__prev = THREAD_GETMEM (self, cleanup);
+
+  int cancelhandling = THREAD_GETMEM (self, cancelhandling);
+
+  /* Disable asynchronous cancellation for now.  */
+  if (__builtin_expect (cancelhandling & CANCELTYPE_BITMASK, 0))
+    while (1)
+      {
+	int curval = THREAD_ATOMIC_CMPXCHG_VAL (self, cancelhandling,
+						cancelhandling
+						& ~CANCELTYPE_BITMASK,
+						cancelhandling);
+	if (__builtin_expect (curval == cancelhandling, 1))
+	  /* Successfully replaced the value.  */
+	  break;
+
+	/* Prepare for the next round.  */
+	cancelhandling = curval;
+      }
+
+  buffer->__canceltype = (cancelhandling & CANCELTYPE_BITMASK
+			  ? PTHREAD_CANCEL_ASYNCHRONOUS
+			  : PTHREAD_CANCEL_DEFERRED);
+
+  THREAD_SETMEM (self, cleanup, buffer);
+}
+strong_alias (_pthread_cleanup_push_defer, __pthread_cleanup_push_defer)
+
+
+void
+_pthread_cleanup_pop_restore (buffer, execute)
+     struct _pthread_cleanup_buffer *buffer;
+     int execute;
+{
+  struct pthread *self = THREAD_SELF;
+
+  THREAD_SETMEM (self, cleanup, buffer->__prev);
+
+  int cancelhandling;
+  if (__builtin_expect (buffer->__canceltype != PTHREAD_CANCEL_DEFERRED, 0)
+      && ((cancelhandling = THREAD_GETMEM (self, cancelhandling))
+	  & CANCELTYPE_BITMASK) == 0)
+    {
+      while (1)
+	{
+	  int curval = THREAD_ATOMIC_CMPXCHG_VAL (self, cancelhandling,
+						  cancelhandling
+						  | CANCELTYPE_BITMASK,
+						  cancelhandling);
+	  if (__builtin_expect (curval == cancelhandling, 1))
+	    /* Successfully replaced the value.  */
+	    break;
+
+	  /* Prepare for the next round.  */
+	  cancelhandling = curval;
+	}
+
+      CANCELLATION_P (self);
+    }
+
+  /* If necessary call the cleanup routine after we removed the
+     current cleanup block from the list.  */
+  if (execute)
+    buffer->__routine (buffer->__arg);
+}
+strong_alias (_pthread_cleanup_pop_restore, __pthread_cleanup_pop_restore)
diff --git a/nptl/descr.h b/nptl/descr.h
index 3625b15088..28b7afa1a1 100644
--- a/nptl/descr.h
+++ b/nptl/descr.h
@@ -32,6 +32,9 @@
 #include <dl-sysdep.h>
 #include "../nptl_db/thread_db.h"
 #include <tls.h>
+#ifdef HAVE_FORCED_UNWIND
+# include <unwind.h>
+#endif
 
 
 #ifndef TCB_ALIGNMENT
@@ -54,6 +57,45 @@
    / PTHREAD_KEY_2NDLEVEL_SIZE)
 
 
+
+
+/* Internal version of the buffer to store cancellation handler
+   information.  */
+struct pthread_unwind_buf
+{
+  union
+  {
+    /* This is the placeholder of the public version.  */
+    void *pad[16];
+
+    struct
+    {
+#ifdef HAVE_FORCED_UNWIND
+      /* First the machine-specific unwind info.  */
+      struct _Unwind_Exception exc;
+#endif
+
+      /* Pointer to the previous cleanup buffer.  */
+      __pthread_unwind_buf_t *prev;
+
+      /* Backward compatibility: state of the old-style cleanup
+	 handler at the time of the previous new-style cleanup handler
+	 installment.  */
+      struct _pthread_cleanup_buffer *cleanup;
+
+      /* Cancellation type before the push call.  */
+      int canceltype;
+    } data;
+  } priv;
+
+  struct
+  {
+    __jmp_buf jmp_buf;
+    int mask_was_saved;
+  } cancel_jmp_buf[1];
+};
+
+
 /* Thread descriptor data structure.  */
 struct pthread
 {
@@ -86,6 +128,10 @@ struct pthread
   /* List of cleanup buffers.  */
   struct _pthread_cleanup_buffer *cleanup;
 
+  /* Unwind information.  */
+  __pthread_unwind_buf_t *cleanup_jmp_buf;
+#define HAVE_CLEANUP_JMP_BUF
+
   /* Flags determining processing of cancellation.  */
   int cancelhandling;
   /* Bit set if cancellation is disabled.  */
@@ -160,10 +206,6 @@ struct pthread
   /* Check whether a thread is detached.  */
 #define IS_DETACHED(pd) ((pd)->joinid == (pd))
 
-  /* Setjmp buffer to be used if try/finally is not available.  */
-  sigjmp_buf cancelbuf;
-#define HAVE_CANCELBUF	1
-
   /* Flags.  Including those copied from the thread attribute.  */
   int flags;
 
diff --git a/nptl/forward.c b/nptl/forward.c
index 290c2d7ec6..ed3e23e68d 100644
--- a/nptl/forward.c
+++ b/nptl/forward.c
@@ -19,10 +19,12 @@
 
 #include <dlfcn.h>
 #include <pthreadP.h>
+#include <signal.h>
 #include <stdlib.h>
 
 #include <shlib-compat.h>
 #include <atomic.h>
+#include <sysdep.h>
 
 
 /* Pointers to the libc functions.  */
@@ -170,3 +172,11 @@ FORWARD (pthread_setcancelstate, (int state, int *oldstate), (state, oldstate),
 	 0)
 
 FORWARD (pthread_setcanceltype, (int type, int *oldtype), (type, oldtype), 0)
+
+FORWARD2(__pthread_unwind,
+	 void attribute_hidden __attribute ((noreturn)) __cleanup_fct_attribute,
+	 (__pthread_unwind_buf_t *buf), (buf), {
+		       /* We cannot call abort() here.  */
+		       INTERNAL_SYSCALL_DECL (err);
+		       INTERNAL_SYSCALL (kill, err, 1, SIGKILL);
+		     })
diff --git a/nptl/init.c b/nptl/init.c
index 7a6883f4cb..9db50036c6 100644
--- a/nptl/init.c
+++ b/nptl/init.c
@@ -121,7 +121,8 @@ static struct pthread_functions pthread_functions =
     .ptr___pthread_setspecific = __pthread_setspecific_internal,
     .ptr__pthread_cleanup_push_defer = __pthread_cleanup_push_defer,
     .ptr__pthread_cleanup_pop_restore = __pthread_cleanup_pop_restore,
-    .ptr_nthreads = &__nptl_nthreads
+    .ptr_nthreads = &__nptl_nthreads,
+    .ptr___pthread_unwind = &__pthread_unwind
   };
 # define ptr_pthread_functions &pthread_functions
 #else
diff --git a/nptl/pthreadP.h b/nptl/pthreadP.h
index db345d505f..d51f837969 100644
--- a/nptl/pthreadP.h
+++ b/nptl/pthreadP.h
@@ -94,6 +94,29 @@ extern int __pthread_debug attribute_hidden;
       }									      \
   } while (0)
 
+
+extern void __pthread_unwind (__pthread_unwind_buf_t *__buf)
+     __cleanup_fct_attribute __attribute ((__noreturn__))
+#ifndef SHARED
+     weak_function
+#endif
+     ;
+
+/* Called when a thread reacts on a cancellation request.  */
+static inline void
+__attribute ((noreturn))
+__do_cancel (void)
+{
+  struct pthread *self = THREAD_SELF;
+
+  /* Make sure we get no more cancellations.  */
+  THREAD_ATOMIC_BIT_SET (self, cancelhandling, EXITING_BIT);
+
+  __pthread_unwind ((__pthread_unwind_buf_t *)
+		    THREAD_GETMEM (self, cleanup_jmp_buf));
+}
+
+
 /* Set cancellation mode to asynchronous.  */
 #define CANCEL_ASYNC() \
   __pthread_enable_asynccancel ()
@@ -143,38 +166,6 @@ extern int __pthread_debug attribute_hidden;
 #endif
 
 
-/* This function is responsible for calling all registered cleanup
-   handlers and then terminate the thread.  This includes dellocating
-   the thread-specific data.  The implementation is complicated by the
-   fact that we have to handle to cancellation handler registration
-   methods: exceptions using try/finally and setjmp.
-
-   The setjmp method is always available.  The user might compile some
-   code which uses this method because no modern compiler is
-   available.  So we have to handle these first since we cannot call
-   the cleanup handlers if the stack frames are gone.  At the same
-   time this opens a hole for the register exception handler blocks
-   since now they might be in danger of using an overwritten stack
-   frame.  The advise is to only use new or only old style cancellation
-   handling.  */
-static inline void
-__do_cancel (void)
-{
-  struct pthread *self = THREAD_SELF;
-
-  /* Make sure we get no more cancellations.  */
-  THREAD_ATOMIC_BIT_SET (self, cancelhandling, EXITING_BIT);
-
-  /* Throw an exception.  */
-  // XXX TBI
-
-  /* If throwing an exception didn't work try the longjmp.  */
-  __libc_longjmp (self->cancelbuf, 1);
-
-  /* NOTREACHED */
-}
-
-
 /* Internal prototypes.  */
 
 /* Thread list handling.  */
@@ -344,7 +335,6 @@ extern int __pthread_cond_wait_2_0 (pthread_cond_2_0_t *cond,
 				    pthread_mutex_t *mutex);
 
 
-
 /* The two functions are in libc.so and not exported.  */
 extern int __libc_enable_asynccancel (void) attribute_hidden;
 extern void __libc_disable_asynccancel (int oldtype)
@@ -372,4 +362,14 @@ extern void __pthread_cleanup_push_defer (struct _pthread_cleanup_buffer *buffer
 extern void __pthread_cleanup_pop_restore (struct _pthread_cleanup_buffer *buffer,
 					   int execute);
 
+/* Old cleanup interfaces, still used in libc.so.  */
+extern void _pthread_cleanup_push (struct _pthread_cleanup_buffer *buffer,
+                                   void (*routine) (void *), void *arg);
+extern void _pthread_cleanup_pop (struct _pthread_cleanup_buffer *buffer,
+                                  int execute);
+extern void _pthread_cleanup_push_defer (struct _pthread_cleanup_buffer *buffer,
+                                         void (*routine) (void *), void *arg);
+extern void _pthread_cleanup_pop_restore (struct _pthread_cleanup_buffer *buffer,
+                                          int execute);
+
 #endif	/* pthreadP.h */
diff --git a/nptl/tst-cleanup0.expect b/nptl/tst-cleanup0.expect
new file mode 100644
index 0000000000..4e3c581802
--- /dev/null
+++ b/nptl/tst-cleanup0.expect
@@ -0,0 +1,3 @@
+ch (3)
+ch (2)
+ch (1)