mirror of
https://sourceware.org/git/glibc.git
synced 2024-11-24 14:00:30 +00:00
Declare __pthread_unwind. Define __do_cancel to use it. Declare old cleanup handler installation functions.
This commit is contained in:
parent
32b0da568d
commit
6efd481484
@ -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
55
nptl/cleanup_compat.c
Normal 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)
|
@ -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)
|
||||
|
98
nptl/cleanup_defer_compat.c
Normal file
98
nptl/cleanup_defer_compat.c
Normal 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)
|
50
nptl/descr.h
50
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;
|
||||
|
||||
|
@ -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);
|
||||
})
|
||||
|
@ -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
|
||||
|
@ -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
3
nptl/tst-cleanup0.expect
Normal file
@ -0,0 +1,3 @@
|
||||
ch (3)
|
||||
ch (2)
|
||||
ch (1)
|
Loading…
Reference in New Issue
Block a user