Declare __pthread_unwind. Define __do_cancel to use it. Declare old cleanup handler...
authorUlrich Drepper <drepper@redhat.com>
Sat, 12 Apr 2003 00:14:16 +0000 (00:14 +0000)
committerUlrich Drepper <drepper@redhat.com>
Sat, 12 Apr 2003 00:14:16 +0000 (00:14 +0000)
nptl/cleanup.c
nptl/cleanup_compat.c [new file with mode: 0644]
nptl/cleanup_defer.c
nptl/cleanup_defer_compat.c [new file with mode: 0644]
nptl/descr.h
nptl/forward.c
nptl/init.c
nptl/pthreadP.h
nptl/tst-cleanup0.expect [new file with mode: 0644]

index a25b397..2029fe2 100644 (file)
 
 
 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 (file)
index 0000000..a25b397
--- /dev/null
@@ -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)
index 7945a0d..4c67813 100644 (file)
    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 (file)
index 0000000..a0ed6da
--- /dev/null
@@ -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)
index 3625b15..28b7afa 100644 (file)
@@ -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
    / 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;
 
index 290c2d7..ed3e23e 100644 (file)
 
 #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);
+                    })
index 7a6883f..9db5003 100644 (file)
@@ -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
index db345d5..d51f837 100644 (file)
@@ -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 (file)
index 0000000..4e3c581
--- /dev/null
@@ -0,0 +1,3 @@
+ch (3)
+ch (2)
+ch (1)