Changes from previous version [1]
* Rebased against master.
--
The thread cancellation state is not accessed concurrently internally
neither the pthread interface allows changing the state of a different
thread than its own.
By removing the cancel state out of the internal thread cancel handling
state there is no need to check if cancelled bit was set in CAS
operation.
The code is also simplified: the CANCELLATION_P is replaced with a
internal pthread_testcancel call and the CANCELSTATE_BIT{MASK} is
removed.
The second part of this patchset also keeps the pthread_setcanceltype as
cancellation entrypoint by calling pthread_testcancel if the new type
is PTHREAD_CANCEL_ASYNCHRONOUS.
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.
[1] https://patchwork.sourceware.org/project/glibc/patch/20200520174819.1138124-1-adhemerval.zanella@linaro.org/
---
nptl/allocatestack.c | 1 +
nptl/cancellation.c | 3 ++-
nptl/cleanup_defer.c | 2 +-
nptl/cleanup_defer_compat.c | 2 +-
nptl/descr.h | 14 ++++++--------
nptl/pthreadP.h | 12 ------------
nptl/pthread_cancel.c | 3 ++-
nptl/pthread_join_common.c | 5 ++++-
nptl/pthread_setcancelstate.c | 36 +++--------------------------------
nptl/pthread_setcanceltype.c | 3 ++-
nptl/pthread_testcancel.c | 11 ++++++++++-
11 files changed, 32 insertions(+), 60 deletions(-)
@@ -220,6 +220,7 @@ get_cached_stack (size_t *sizep, void **memp)
/* Cancellation handling is back to the default. */
result->cancelhandling = 0;
+ result->cancelstate = PTHREAD_CANCEL_ENABLE;
result->cleanup = NULL;
/* No pending event. */
@@ -45,7 +45,8 @@ __pthread_enable_asynccancel (void)
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);
__do_cancel ();
@@ -86,6 +86,6 @@ __pthread_unregister_cancel_restore (__pthread_unwind_buf_t *buf)
cancelhandling = curval;
}
- CANCELLATION_P (self);
+ __pthread_testcancel ();
}
}
@@ -83,7 +83,7 @@ _pthread_cleanup_pop_restore (struct _pthread_cleanup_buffer *buffer,
cancelhandling = curval;
}
- CANCELLATION_P (self);
+ __pthread_testcancel ();
}
/* If necessary call the cleanup routine after we removed the
@@ -277,9 +277,6 @@ struct pthread
/* Flags determining processing of cancellation. */
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. */
#define CANCELTYPE_BIT 1
#define CANCELTYPE_BITMASK (0x01 << CANCELTYPE_BIT)
@@ -301,11 +298,8 @@ struct pthread
/* Mask for the rest. Helps the compiler to optimize. */
#define CANCEL_RESTMASK 0xffffff80
-#define CANCEL_ENABLED_AND_CANCELED(value) \
- (((value) & (CANCELSTATE_BITMASK | CANCELED_BITMASK | EXITING_BITMASK \
- | CANCEL_RESTMASK | TERMINATED_BITMASK)) == CANCELED_BITMASK)
-#define CANCEL_ENABLED_AND_CANCELED_AND_ASYNCHRONOUS(value) \
- (((value) & (CANCELSTATE_BITMASK | CANCELTYPE_BITMASK | CANCELED_BITMASK \
+#define CANCEL_CANCELED_AND_ASYNCHRONOUS(value) \
+ (((value) & (CANCELTYPE_BITMASK | CANCELED_BITMASK \
| EXITING_BITMASK | CANCEL_RESTMASK | TERMINATED_BITMASK)) \
== (CANCELTYPE_BITMASK | CANCELED_BITMASK))
@@ -409,6 +403,10 @@ struct pthread
/* Used on strsignal. */
struct tls_internal_t tls_state;
+ /* Thread cancel state (PTHREAD_CANCEL_ENABLE or
+ PTHREAD_CANCEL_DISABLE). */
+ unsigned char cancelstate;
+
/* This member must be last. */
char end_padding[];
@@ -253,18 +253,6 @@ extern int __pthread_debug attribute_hidden;
#endif
-/* 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)
__cleanup_fct_attribute __attribute ((__noreturn__))
#if !defined SHARED && !IS_IN (libpthread)
@@ -55,7 +55,8 @@ __pthread_cancel (pthread_t th)
/* If the cancellation is handled asynchronously just send a
signal. We avoid this if possible since it's more
expensive. */
- if (CANCEL_ENABLED_AND_CANCELED_AND_ASYNCHRONOUS (newval))
+ if (pd->cancelstate == PTHREAD_CANCEL_ENABLE
+ && CANCEL_CANCELED_AND_ASYNCHRONOUS (newval))
{
/* Mark the cancellation as "in progress". */
if (atomic_compare_and_exchange_bool_acq (&pd->cancelhandling,
@@ -59,7 +59,10 @@ __pthread_clockjoin_ex (pthread_t threadid, void **thread_return,
&& (pd->cancelhandling
& (CANCELING_BITMASK | CANCELED_BITMASK | EXITING_BITMASK
| 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
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
@@ -31,39 +31,9 @@ __pthread_setcancelstate (int state, int *oldstate)
self = THREAD_SELF;
- int oldval = THREAD_GETMEM (self, cancelhandling);
- while (1)
- {
- 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;
- }
+ if (oldstate != NULL)
+ *oldstate = self->cancelstate;
+ self->cancelstate = state;
return 0;
}
@@ -53,7 +53,8 @@ __pthread_setcanceltype (int type, int *oldtype)
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);
__do_cancel ();
@@ -23,7 +23,16 @@
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 ();
+ }
}
strong_alias (__pthread_testcancel, pthread_testcancel)
hidden_def (__pthread_testcancel)