nptl: Move cancel state out of cancelhandling
Now that thread cancellation state is not accessed concurrently anymore, it is possible to move it out the 'cancelhandling'. The code is also simplified: CANCELLATION_P is replaced with a internal pthread_testcancel call and the CANCELSTATE_BIT{MASK} is removed. With this behavior pthread_setcancelstate does not require to act on cancellation if cancel type is asynchronous (is already handled either by pthread_setcanceltype or by the signal handler). Checked on x86_64-linux-gnu and aarch64-linux-gnu.
This commit is contained in:
parent
26cfbb7162
commit
2b51742531
|
@ -1820,7 +1820,6 @@ the beginning of the vector.
|
||||||
@c (disable cancellation around exec_comm; it may do_cancel the
|
@c (disable cancellation around exec_comm; it may do_cancel the
|
||||||
@c second time, if async cancel is enabled)
|
@c second time, if async cancel is enabled)
|
||||||
@c THREAD_ATOMIC_CMPXCHG_VAL dup ok
|
@c THREAD_ATOMIC_CMPXCHG_VAL dup ok
|
||||||
@c CANCEL_ENABLED_AND_CANCELED_AND_ASYNCHRONOUS dup ok
|
|
||||||
@c do_cancel @ascuplugin @ascuheap @acsmem
|
@c do_cancel @ascuplugin @ascuheap @acsmem
|
||||||
@c THREAD_ATOMIC_BIT_SET dup ok
|
@c THREAD_ATOMIC_BIT_SET dup ok
|
||||||
@c pthread_unwind @ascuplugin @ascuheap @acsmem
|
@c pthread_unwind @ascuplugin @ascuheap @acsmem
|
||||||
|
|
|
@ -68,7 +68,7 @@ until the subprogram terminates before you can do anything else.
|
||||||
@c CLEANUP_HANDLER @ascuplugin @ascuheap @acsmem
|
@c CLEANUP_HANDLER @ascuplugin @ascuheap @acsmem
|
||||||
@c libc_cleanup_region_start @ascuplugin @ascuheap @acsmem
|
@c libc_cleanup_region_start @ascuplugin @ascuheap @acsmem
|
||||||
@c pthread_cleanup_push_defer @ascuplugin @ascuheap @acsmem
|
@c pthread_cleanup_push_defer @ascuplugin @ascuheap @acsmem
|
||||||
@c CANCELLATION_P @ascuplugin @ascuheap @acsmem
|
@c __pthread_testcancel @ascuplugin @ascuheap @acsmem
|
||||||
@c CANCEL_ENABLED_AND_CANCELED ok
|
@c CANCEL_ENABLED_AND_CANCELED ok
|
||||||
@c do_cancel @ascuplugin @ascuheap @acsmem
|
@c do_cancel @ascuplugin @ascuheap @acsmem
|
||||||
@c cancel_handler ok
|
@c cancel_handler ok
|
||||||
|
@ -86,7 +86,6 @@ until the subprogram terminates before you can do anything else.
|
||||||
@c SINGLE_THREAD_P ok
|
@c SINGLE_THREAD_P ok
|
||||||
@c LIBC_CANCEL_ASYNC @ascuplugin @ascuheap @acsmem
|
@c LIBC_CANCEL_ASYNC @ascuplugin @ascuheap @acsmem
|
||||||
@c libc_enable_asynccancel @ascuplugin @ascuheap @acsmem
|
@c libc_enable_asynccancel @ascuplugin @ascuheap @acsmem
|
||||||
@c CANCEL_ENABLED_AND_CANCELED_AND_ASYNCHRONOUS dup ok
|
|
||||||
@c do_cancel dup @ascuplugin @ascuheap @acsmem
|
@c do_cancel dup @ascuplugin @ascuheap @acsmem
|
||||||
@c LIBC_CANCEL_RESET ok
|
@c LIBC_CANCEL_RESET ok
|
||||||
@c libc_disable_asynccancel ok
|
@c libc_disable_asynccancel ok
|
||||||
|
|
|
@ -160,6 +160,7 @@ get_cached_stack (size_t *sizep, void **memp)
|
||||||
|
|
||||||
/* Cancellation handling is back to the default. */
|
/* Cancellation handling is back to the default. */
|
||||||
result->cancelhandling = 0;
|
result->cancelhandling = 0;
|
||||||
|
result->cancelstate = PTHREAD_CANCEL_ENABLE;
|
||||||
result->cleanup = NULL;
|
result->cleanup = NULL;
|
||||||
result->setup_failed = 0;
|
result->setup_failed = 0;
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,8 @@ __pthread_enable_asynccancel (void)
|
||||||
oldval);
|
oldval);
|
||||||
if (__glibc_likely (curval == oldval))
|
if (__glibc_likely (curval == oldval))
|
||||||
{
|
{
|
||||||
if (CANCEL_ENABLED_AND_CANCELED_AND_ASYNCHRONOUS (newval))
|
if (self->cancelstate == PTHREAD_CANCEL_ENABLE
|
||||||
|
&& CANCEL_CANCELED_AND_ASYNCHRONOUS (newval))
|
||||||
{
|
{
|
||||||
THREAD_SETMEM (self, result, PTHREAD_CANCELED);
|
THREAD_SETMEM (self, result, PTHREAD_CANCELED);
|
||||||
__do_cancel ();
|
__do_cancel ();
|
||||||
|
|
|
@ -92,7 +92,7 @@ ___pthread_unregister_cancel_restore (__pthread_unwind_buf_t *buf)
|
||||||
cancelhandling = curval;
|
cancelhandling = curval;
|
||||||
}
|
}
|
||||||
|
|
||||||
CANCELLATION_P (self);
|
__pthread_testcancel ();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
versioned_symbol (libc, ___pthread_unregister_cancel_restore,
|
versioned_symbol (libc, ___pthread_unregister_cancel_restore,
|
||||||
|
|
14
nptl/descr.h
14
nptl/descr.h
|
@ -277,9 +277,6 @@ struct pthread
|
||||||
|
|
||||||
/* Flags determining processing of cancellation. */
|
/* Flags determining processing of cancellation. */
|
||||||
int cancelhandling;
|
int cancelhandling;
|
||||||
/* Bit set if cancellation is disabled. */
|
|
||||||
#define CANCELSTATE_BIT 0
|
|
||||||
#define CANCELSTATE_BITMASK (0x01 << CANCELSTATE_BIT)
|
|
||||||
/* Bit set if asynchronous cancellation mode is selected. */
|
/* Bit set if asynchronous cancellation mode is selected. */
|
||||||
#define CANCELTYPE_BIT 1
|
#define CANCELTYPE_BIT 1
|
||||||
#define CANCELTYPE_BITMASK (0x01 << CANCELTYPE_BIT)
|
#define CANCELTYPE_BITMASK (0x01 << CANCELTYPE_BIT)
|
||||||
|
@ -298,11 +295,8 @@ struct pthread
|
||||||
/* Mask for the rest. Helps the compiler to optimize. */
|
/* Mask for the rest. Helps the compiler to optimize. */
|
||||||
#define CANCEL_RESTMASK 0xffffff80
|
#define CANCEL_RESTMASK 0xffffff80
|
||||||
|
|
||||||
#define CANCEL_ENABLED_AND_CANCELED(value) \
|
#define CANCEL_CANCELED_AND_ASYNCHRONOUS(value) \
|
||||||
(((value) & (CANCELSTATE_BITMASK | CANCELED_BITMASK | EXITING_BITMASK \
|
(((value) & (CANCELTYPE_BITMASK | CANCELED_BITMASK \
|
||||||
| CANCEL_RESTMASK | TERMINATED_BITMASK)) == CANCELED_BITMASK)
|
|
||||||
#define CANCEL_ENABLED_AND_CANCELED_AND_ASYNCHRONOUS(value) \
|
|
||||||
(((value) & (CANCELSTATE_BITMASK | CANCELTYPE_BITMASK | CANCELED_BITMASK \
|
|
||||||
| EXITING_BITMASK | CANCEL_RESTMASK | TERMINATED_BITMASK)) \
|
| EXITING_BITMASK | CANCEL_RESTMASK | TERMINATED_BITMASK)) \
|
||||||
== (CANCELTYPE_BITMASK | CANCELED_BITMASK))
|
== (CANCELTYPE_BITMASK | CANCELED_BITMASK))
|
||||||
|
|
||||||
|
@ -404,6 +398,10 @@ struct pthread
|
||||||
/* Indicates whether is a C11 thread created by thrd_creat. */
|
/* Indicates whether is a C11 thread created by thrd_creat. */
|
||||||
bool c11;
|
bool c11;
|
||||||
|
|
||||||
|
/* Thread cancel state (PTHREAD_CANCEL_ENABLE or
|
||||||
|
PTHREAD_CANCEL_DISABLE). */
|
||||||
|
unsigned char cancelstate;
|
||||||
|
|
||||||
/* Used on strsignal. */
|
/* Used on strsignal. */
|
||||||
struct tls_internal_t tls_state;
|
struct tls_internal_t tls_state;
|
||||||
|
|
||||||
|
|
|
@ -79,7 +79,7 @@ __libc_cleanup_pop_restore (struct _pthread_cleanup_buffer *buffer)
|
||||||
cancelhandling = curval;
|
cancelhandling = curval;
|
||||||
}
|
}
|
||||||
|
|
||||||
CANCELLATION_P (self);
|
__pthread_testcancel ();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
libc_hidden_def (__libc_cleanup_pop_restore)
|
libc_hidden_def (__libc_cleanup_pop_restore)
|
||||||
|
|
|
@ -245,18 +245,6 @@ libc_hidden_proto (__pthread_current_priority)
|
||||||
#define INVALID_TD_P(pd) __builtin_expect ((pd)->tid <= 0, 0)
|
#define INVALID_TD_P(pd) __builtin_expect ((pd)->tid <= 0, 0)
|
||||||
#define INVALID_NOT_TERMINATED_TD_P(pd) __builtin_expect ((pd)->tid < 0, 0)
|
#define INVALID_NOT_TERMINATED_TD_P(pd) __builtin_expect ((pd)->tid < 0, 0)
|
||||||
|
|
||||||
/* Cancellation test. */
|
|
||||||
#define CANCELLATION_P(self) \
|
|
||||||
do { \
|
|
||||||
int cancelhandling = THREAD_GETMEM (self, cancelhandling); \
|
|
||||||
if (CANCEL_ENABLED_AND_CANCELED (cancelhandling)) \
|
|
||||||
{ \
|
|
||||||
THREAD_SETMEM (self, result, PTHREAD_CANCELED); \
|
|
||||||
__do_cancel (); \
|
|
||||||
} \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
|
|
||||||
extern void __pthread_unwind (__pthread_unwind_buf_t *__buf)
|
extern void __pthread_unwind (__pthread_unwind_buf_t *__buf)
|
||||||
__cleanup_fct_attribute __attribute ((__noreturn__))
|
__cleanup_fct_attribute __attribute ((__noreturn__))
|
||||||
#if !defined SHARED && !IS_IN (libpthread)
|
#if !defined SHARED && !IS_IN (libpthread)
|
||||||
|
|
|
@ -45,7 +45,7 @@ sigcancel_handler (int sig, siginfo_t *si, void *ctx)
|
||||||
|
|
||||||
int ch = atomic_load_relaxed (&self->cancelhandling);
|
int ch = atomic_load_relaxed (&self->cancelhandling);
|
||||||
/* Cancelation not enabled, not cancelled, or already exitting. */
|
/* Cancelation not enabled, not cancelled, or already exitting. */
|
||||||
if ((ch & CANCELSTATE_BITMASK) != 0
|
if (self->cancelstate == PTHREAD_CANCEL_DISABLE
|
||||||
|| (ch & CANCELED_BITMASK) == 0
|
|| (ch & CANCELED_BITMASK) == 0
|
||||||
|| (ch & EXITING_BITMASK) != 0)
|
|| (ch & EXITING_BITMASK) != 0)
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -59,7 +59,10 @@ __pthread_clockjoin_ex (pthread_t threadid, void **thread_return,
|
||||||
&& (pd->cancelhandling
|
&& (pd->cancelhandling
|
||||||
& (CANCELED_BITMASK | EXITING_BITMASK
|
& (CANCELED_BITMASK | EXITING_BITMASK
|
||||||
| TERMINATED_BITMASK)) == 0))
|
| TERMINATED_BITMASK)) == 0))
|
||||||
&& !CANCEL_ENABLED_AND_CANCELED (self->cancelhandling))
|
&& !(self->cancelstate == PTHREAD_CANCEL_ENABLE
|
||||||
|
&& (pd->cancelhandling & (CANCELED_BITMASK | EXITING_BITMASK
|
||||||
|
| TERMINATED_BITMASK))
|
||||||
|
== CANCELED_BITMASK))
|
||||||
/* This is a deadlock situation. The threads are waiting for each
|
/* This is a deadlock situation. The threads are waiting for each
|
||||||
other to finish. Note that this is a "may" error. To be 100%
|
other to finish. Note that this is a "may" error. To be 100%
|
||||||
sure we catch this error we would have to lock the data
|
sure we catch this error we would have to lock the data
|
||||||
|
|
|
@ -31,39 +31,9 @@ __pthread_setcancelstate (int state, int *oldstate)
|
||||||
|
|
||||||
self = THREAD_SELF;
|
self = THREAD_SELF;
|
||||||
|
|
||||||
int oldval = THREAD_GETMEM (self, cancelhandling);
|
if (oldstate != NULL)
|
||||||
while (1)
|
*oldstate = self->cancelstate;
|
||||||
{
|
self->cancelstate = state;
|
||||||
int newval = (state == PTHREAD_CANCEL_DISABLE
|
|
||||||
? oldval | CANCELSTATE_BITMASK
|
|
||||||
: oldval & ~CANCELSTATE_BITMASK);
|
|
||||||
|
|
||||||
/* Store the old value. */
|
|
||||||
if (oldstate != NULL)
|
|
||||||
*oldstate = ((oldval & CANCELSTATE_BITMASK)
|
|
||||||
? PTHREAD_CANCEL_DISABLE : PTHREAD_CANCEL_ENABLE);
|
|
||||||
|
|
||||||
/* Avoid doing unnecessary work. The atomic operation can
|
|
||||||
potentially be expensive if the memory has to be locked and
|
|
||||||
remote cache lines have to be invalidated. */
|
|
||||||
if (oldval == newval)
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* Update the cancel handling word. This has to be done
|
|
||||||
atomically since other bits could be modified as well. */
|
|
||||||
int curval = THREAD_ATOMIC_CMPXCHG_VAL (self, cancelhandling, newval,
|
|
||||||
oldval);
|
|
||||||
if (__glibc_likely (curval == oldval))
|
|
||||||
{
|
|
||||||
if (CANCEL_ENABLED_AND_CANCELED_AND_ASYNCHRONOUS (newval))
|
|
||||||
__do_cancel ();
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Prepare for the next round. */
|
|
||||||
oldval = curval;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,8 @@ __pthread_setcanceltype (int type, int *oldtype)
|
||||||
oldval);
|
oldval);
|
||||||
if (__glibc_likely (curval == oldval))
|
if (__glibc_likely (curval == oldval))
|
||||||
{
|
{
|
||||||
if (CANCEL_ENABLED_AND_CANCELED_AND_ASYNCHRONOUS (newval))
|
if (self->cancelstate == PTHREAD_CANCEL_ENABLE
|
||||||
|
&& CANCEL_CANCELED_AND_ASYNCHRONOUS (newval))
|
||||||
{
|
{
|
||||||
THREAD_SETMEM (self, result, PTHREAD_CANCELED);
|
THREAD_SETMEM (self, result, PTHREAD_CANCELED);
|
||||||
__do_cancel ();
|
__do_cancel ();
|
||||||
|
|
|
@ -23,7 +23,16 @@
|
||||||
void
|
void
|
||||||
___pthread_testcancel (void)
|
___pthread_testcancel (void)
|
||||||
{
|
{
|
||||||
CANCELLATION_P (THREAD_SELF);
|
struct pthread *self = THREAD_SELF;
|
||||||
|
int cancelhandling = THREAD_GETMEM (self, cancelhandling);
|
||||||
|
if (self->cancelstate == PTHREAD_CANCEL_ENABLE
|
||||||
|
&& (cancelhandling & CANCELED_BITMASK)
|
||||||
|
&& !(cancelhandling & EXITING_BITMASK)
|
||||||
|
&& !(cancelhandling & TERMINATED_BITMASK))
|
||||||
|
{
|
||||||
|
THREAD_SETMEM (self, result, PTHREAD_CANCELED);
|
||||||
|
__do_cancel ();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
versioned_symbol (libc, ___pthread_testcancel, pthread_testcancel, GLIBC_2_34);
|
versioned_symbol (libc, ___pthread_testcancel, pthread_testcancel, GLIBC_2_34);
|
||||||
versioned_symbol (libc, ___pthread_testcancel, __pthread_testcancel,
|
versioned_symbol (libc, ___pthread_testcancel, __pthread_testcancel,
|
||||||
|
|
|
@ -94,4 +94,6 @@ __tls_init_tp (void)
|
||||||
It will be bigger than it actually is, but for unwind.c/pt-longjmp.c
|
It will be bigger than it actually is, but for unwind.c/pt-longjmp.c
|
||||||
purposes this is good enough. */
|
purposes this is good enough. */
|
||||||
THREAD_SETMEM (pd, stackblock_size, (size_t) __libc_stack_end);
|
THREAD_SETMEM (pd, stackblock_size, (size_t) __libc_stack_end);
|
||||||
|
|
||||||
|
THREAD_SETMEM (pd, cancelstate, PTHREAD_CANCEL_ENABLE);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue