about summary refs log tree commit diff
path: root/nptl/sysdeps/pthread
diff options
context:
space:
mode:
Diffstat (limited to 'nptl/sysdeps/pthread')
-rw-r--r--nptl/sysdeps/pthread/bits/libc-lock.h13
-rwxr-xr-xnptl/sysdeps/pthread/configure55
-rw-r--r--nptl/sysdeps/pthread/configure.in13
-rw-r--r--nptl/sysdeps/pthread/pthread-functions.h2
-rw-r--r--nptl/sysdeps/pthread/pthread.h103
5 files changed, 164 insertions, 22 deletions
diff --git a/nptl/sysdeps/pthread/bits/libc-lock.h b/nptl/sysdeps/pthread/bits/libc-lock.h
index 3a3d3cc6d3..945a81cb82 100644
--- a/nptl/sysdeps/pthread/bits/libc-lock.h
+++ b/nptl/sysdeps/pthread/bits/libc-lock.h
@@ -345,6 +345,19 @@ typedef pthread_key_t __libc_key_t;
   } while (0)
 
 
+/* Note that for I/O cleanup handling we are using the old-style
+   cancel handling.  It does not have to be integrated with C++ snce
+   no C++ code is called in the middle.  The old-style handling is
+   faster and the support is not going away.  */
+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);
+
 /* Start critical region with cleanup.  */
 #define __libc_cleanup_region_start(DOIT, FCT, ARG) \
   { struct _pthread_cleanup_buffer _buffer;				      \
diff --git a/nptl/sysdeps/pthread/configure b/nptl/sysdeps/pthread/configure
index 8999d37e5a..50293a4f1c 100755
--- a/nptl/sysdeps/pthread/configure
+++ b/nptl/sysdeps/pthread/configure
@@ -5,3 +5,58 @@ if test "x$libc_cv_gcc___thread" != xyes; then
 echo "$as_me: error: compiler support for __thread is required" >&2;}
    { (exit 1); exit 1; }; }
 fi
+
+
+echo "$as_me:$LINENO: checking for forced unwind support" >&5
+echo $ECHO_N "checking for forced unwind support... $ECHO_C" >&6
+if test "${libc_cv_forced_unwind+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+#line $LINENO "configure"
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <unwind.h>
+int
+main ()
+{
+
+struct _Unwind_Exception exc;
+struct _Unwind_Context *context;
+_Unwind_GetCFA (context)
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+         { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  libc_cv_forced_unwind=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+libc_cv_forced_unwind=no
+fi
+rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $libc_cv_forced_unwind" >&5
+echo "${ECHO_T}$libc_cv_forced_unwind" >&6
+if test $libc_cv_forced_unwind = yes; then
+  cat >>confdefs.h <<\_ACEOF
+#define HAVE_FORCED_UNWIND 1
+_ACEOF
+
+fi
diff --git a/nptl/sysdeps/pthread/configure.in b/nptl/sysdeps/pthread/configure.in
index 8350f86dc0..e4ea6830d6 100644
--- a/nptl/sysdeps/pthread/configure.in
+++ b/nptl/sysdeps/pthread/configure.in
@@ -4,3 +4,16 @@ GLIBC_PROVIDES dnl See aclocal.m4 in the top level source directory.
 if test "x$libc_cv_gcc___thread" != xyes; then
   AC_MSG_ERROR(compiler support for __thread is required)
 fi
+
+dnl Iff <unwind.h> is available, make sure it is the right one and it
+dnl contains struct _Unwind_Exception.
+AC_CACHE_CHECK(dnl
+for forced unwind support, libc_cv_forced_unwind, [dnl
+AC_TRY_LINK([#include <unwind.h>], [
+struct _Unwind_Exception exc;
+struct _Unwind_Context *context;
+_Unwind_GetCFA (context)],
+libc_cv_forced_unwind=yes, libc_cv_forced_unwind=no)])
+if test $libc_cv_forced_unwind = yes; then
+  AC_DEFINE(HAVE_FORCED_UNWIND)
+fi
diff --git a/nptl/sysdeps/pthread/pthread-functions.h b/nptl/sysdeps/pthread/pthread-functions.h
index 9f38e34053..93ba089982 100644
--- a/nptl/sysdeps/pthread/pthread-functions.h
+++ b/nptl/sysdeps/pthread/pthread-functions.h
@@ -85,6 +85,8 @@ struct pthread_functions
 					    int);
 #define HAVE_PTR_NTHREADS
   int *ptr_nthreads;
+  void (*ptr___pthread_unwind) (__pthread_unwind_buf_t *)
+       __attribute ((noreturn)) __cleanup_fct_attribute;
 };
 
 /* Variable in libc.so.  */
diff --git a/nptl/sysdeps/pthread/pthread.h b/nptl/sysdeps/pthread/pthread.h
index 72673d11a6..5373b80dcf 100644
--- a/nptl/sysdeps/pthread/pthread.h
+++ b/nptl/sysdeps/pthread/pthread.h
@@ -26,6 +26,7 @@
 #define __need_sigset_t
 #include <signal.h>
 #include <bits/pthreadtypes.h>
+#include <bits/setjmp.h>
 
 
 /* Detach state.  */
@@ -380,6 +381,24 @@ extern int pthread_cancel (pthread_t __th) __THROW;
 extern void pthread_testcancel (void) __THROW;
 
 
+/* Cancellation handling with integration into exception handling.  */
+
+typedef struct
+{
+  void *__pad[16];
+  struct
+  {
+    __jmp_buf __cancel_jmp_buf;
+    int __mask_was_saved;
+  } __cancel_jmp_buf[1];
+} __pthread_unwind_buf_t __attribute__ ((__aligned__));
+
+/* No special attributes by default.  */
+#ifndef __cleanup_fct_attribute
+# define __cleanup_fct_attribute
+#endif
+
+
 /* Install a cleanup handler: ROUTINE will be called with arguments ARG
    when the thread is cancelled or calls pthread_exit.  ROUTINE will also
    be called with arguments ARG when the matching pthread_cleanup_pop
@@ -387,43 +406,83 @@ extern void pthread_testcancel (void) __THROW;
 
    pthread_cleanup_push and pthread_cleanup_pop are macros and must always
    be used in matching pairs at the same nesting level of braces.  */
-#define pthread_cleanup_push(routine,arg) \
-  { struct _pthread_cleanup_buffer _buffer;				      \
-    _pthread_cleanup_push (&_buffer, (routine), (arg));
-
-extern void _pthread_cleanup_push (struct _pthread_cleanup_buffer *__buffer,
-				   void (*__routine) (void *), void *__arg)
-     __THROW;
+#define pthread_cleanup_push(routine, arg) \
+  do {									      \
+    __pthread_unwind_buf_t __cancel_buf;				      \
+    void (*__cancel_routine) (void *) = (routine);			      \
+    void *__cancel_arg = (arg);						      \
+    int not_first_call = __sigsetjmp ((struct __jmp_buf_tag *)		      \
+				      __cancel_buf.__cancel_jmp_buf, 0);      \
+    if (__builtin_expect (not_first_call, 0))				      \
+      {									      \
+	__cancel_routine (__cancel_arg);				      \
+	__pthread_unwind_next (&__cancel_buf);				      \
+	/* NOTREACHED */						      \
+      }									      \
+									      \
+    __pthread_register_cancel (&__cancel_buf);				      \
+    do {
+extern void __pthread_register_cancel (__pthread_unwind_buf_t *__buf)
+     __cleanup_fct_attribute;
 
 /* Remove a cleanup handler installed by the matching pthread_cleanup_push.
    If EXECUTE is non-zero, the handler function is called. */
 #define pthread_cleanup_pop(execute) \
-    _pthread_cleanup_pop (&_buffer, (execute)); }
-
-extern void _pthread_cleanup_pop (struct _pthread_cleanup_buffer *__buffer,
-				  int __execute) __THROW;
+    } while (0);							      \
+    __pthread_unregister_cancel (&__cancel_buf);			      \
+    if (execute)							      \
+      __cancel_routine (__cancel_arg);					      \
+  } while (0)
+extern void __pthread_unregister_cancel (__pthread_unwind_buf_t *__buf)
+  __cleanup_fct_attribute;
 
 #ifdef __USE_GNU
 /* Install a cleanup handler as pthread_cleanup_push does, but also
    saves the current cancellation type and sets it to deferred
    cancellation.  */
-# define pthread_cleanup_push_defer_np(routine,arg) \
-  { struct _pthread_cleanup_buffer _buffer;				      \
-    _pthread_cleanup_push_defer (&_buffer, (routine), (arg));
-
-extern void _pthread_cleanup_push_defer (struct _pthread_cleanup_buffer *__buffer,
-					 void (*__routine) (void *),
-					 void *__arg) __THROW;
+# define pthread_cleanup_push_defer(routine, arg) \
+  do {									      \
+    __pthread_unwind_buf_t __cancel_buf;				      \
+    void (*__cancel_routine) (void *) = (routine);			      \
+    void *__cancel_arg = (arg);						      \
+    int not_first_call = __sigsetjmp ((struct __jmp_buf_tag *)		      \
+				      __cancel_buf.__cancel_jmp_buf, 0);      \
+    if (__builtin_expect (not_first_call, 0))				      \
+      {									      \
+	__cancel_routine (__cancel_arg);				      \
+	__pthread_unwind_next (&__cancel_buf);				      \
+	/* NOTREACHED */						      \
+      }									      \
+									      \
+    __pthread_register_cancel_defer (&__cancel_buf);			      \
+    do {
+extern void __pthread_register_cancel_defer (__pthread_unwind_buf_t *__buf)
+     __cleanup_fct_attribute;
 
 /* Remove a cleanup handler as pthread_cleanup_pop does, but also
    restores the cancellation type that was in effect when the matching
    pthread_cleanup_push_defer was called.  */
-# define pthread_cleanup_pop_restore_np(execute) \
-  _pthread_cleanup_pop_restore (&_buffer, (execute)); }
+# define pthread_cleanup_pop_cleanup(execute) \
+    } while (0);							      \
+    __pthread_unregister_cancel_restore (&__cancel_buf);		      \
+    if (execute)							      \
+      __cancel_routine (__cancel_arg);					      \
+  } while (0)
+extern void __pthread_unregister_cancel_restore (__pthread_unwind_buf_t *__buf)
+  __cleanup_fct_attribute;
+#endif
 
-extern void _pthread_cleanup_pop_restore (struct _pthread_cleanup_buffer *__buffer,
-					  int __execute) __THROW;
+/* Internal interface to initiate cleanup.  */
+extern void __pthread_unwind_next (__pthread_unwind_buf_t *__buf)
+     __cleanup_fct_attribute __attribute ((__noreturn__))
+#ifndef SHARED
+     __attribute ((__weak__))
 #endif
+     ;
+
+/* Function used in the macros.  */
+struct __jmp_buf_tag;
+extern int __sigsetjmp (struct __jmp_buf_tag __env[1], int __savemask) __THROW;
 
 
 /* Mutex handling.  */