Declare __pthread_unwind. Define __do_cancel to use it. Declare old cleanup handler installation functions.

This commit is contained in:
Ulrich Drepper 2003-04-12 00:14:16 +00:00
parent 32b0da568d
commit 6efd481484
9 changed files with 282 additions and 87 deletions

View file

@ -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)

55
nptl/cleanup_compat.c Normal file
View file

@ -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)

View file

@ -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)

View file

@ -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)

View 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
@ -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;

View file

@ -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);
})

View 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

View 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 */

3
nptl/tst-cleanup0.expect Normal file
View file

@ -0,0 +1,3 @@
ch (3)
ch (2)
ch (1)