summaryrefslogtreecommitdiff
path: root/nptl
diff options
context:
space:
mode:
authorUlrich Drepper <drepper@redhat.com>2003-04-12 00:14:16 +0000
committerUlrich Drepper <drepper@redhat.com>2003-04-12 00:14:16 +0000
commit6efd481484e5d8d47b834fd034dc62f4c036d2cf (patch)
treec4bd9e169d534ceaf83f4b3f6b7f034e0e1cb379 /nptl
parent32b0da568de098fc2d62da7cf6a06ddbaba156d7 (diff)
Declare __pthread_unwind. Define __do_cancel to use it. Declare old cleanup handler installation functions.
Diffstat (limited to 'nptl')
-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)