From 6efd481484e5d8d47b834fd034dc62f4c036d2cf Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Sat, 12 Apr 2003 00:14:16 +0000 Subject: Declare __pthread_unwind. Define __do_cancel to use it. Declare old cleanup handler installation functions. --- nptl/cleanup.c | 32 ++++++--------- nptl/cleanup_compat.c | 55 +++++++++++++++++++++++++ nptl/cleanup_defer.c | 52 +++++++++++------------- nptl/cleanup_defer_compat.c | 98 +++++++++++++++++++++++++++++++++++++++++++++ nptl/descr.h | 50 +++++++++++++++++++++-- nptl/forward.c | 10 +++++ nptl/init.c | 3 +- nptl/pthreadP.h | 66 +++++++++++++++--------------- nptl/tst-cleanup0.expect | 3 ++ 9 files changed, 282 insertions(+), 87 deletions(-) create mode 100644 nptl/cleanup_compat.c create mode 100644 nptl/cleanup_defer_compat.c create mode 100644 nptl/tst-cleanup0.expect (limited to 'nptl') diff --git a/nptl/cleanup.c b/nptl/cleanup.c index a25b397..2029fe2 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 0000000..a25b397 --- /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 , 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 +#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 7945a0d..4c67813 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 #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 0000000..a0ed6da --- /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 , 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 3625b15..28b7afa 100644 --- a/nptl/descr.h +++ b/nptl/descr.h @@ -32,6 +32,9 @@ #include #include "../nptl_db/thread_db.h" #include +#ifdef HAVE_FORCED_UNWIND +# include +#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 290c2d7..ed3e23e 100644 --- a/nptl/forward.c +++ b/nptl/forward.c @@ -19,10 +19,12 @@ #include #include +#include #include #include #include +#include /* 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 7a6883f..9db5003 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 db345d5..d51f837 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 0000000..4e3c581 --- /dev/null +++ b/nptl/tst-cleanup0.expect @@ -0,0 +1,3 @@ +ch (3) +ch (2) +ch (1) -- cgit v1.1