nptl: Handle spurious EINTR when thread cancellation is disabled (BZ#29029)
upstream link is https://sourceware.org/bugzilla/show_bug.cgi?id=29029
This commit is contained in:
parent
7bceaad3d8
commit
752db166d0
248
0009_nptl_2.17_adapt_for_bug_29029.patch
Normal file
248
0009_nptl_2.17_adapt_for_bug_29029.patch
Normal file
@ -0,0 +1,248 @@
|
|||||||
|
From ffdf4f3f121f35834131396c52049745cca21a22 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Yang Yanchao <yangyanchao6@huawei.com>
|
||||||
|
Date: Mon, 18 Apr 2022 11:10:46 +0800
|
||||||
|
Subject: [PATCH] nptl 2.17 adapt for bug 29029
|
||||||
|
|
||||||
|
upstream bug 29029 has revert some struct and function,
|
||||||
|
this commit is to adapt the change.
|
||||||
|
|
||||||
|
---
|
||||||
|
nptl_2_17/cancellation.c | 91 +++++++++++++++++++++++++++++++++
|
||||||
|
nptl_2_17/cancellation_2_17.c | 60 ++++++++++++++++------
|
||||||
|
nptl_2_17/compat_pthread_2_17.h | 21 --------
|
||||||
|
3 files changed, 136 insertions(+), 36 deletions(-)
|
||||||
|
create mode 100644 nptl_2_17/cancellation.c
|
||||||
|
|
||||||
|
diff --git a/nptl_2_17/cancellation.c b/nptl_2_17/cancellation.c
|
||||||
|
new file mode 100644
|
||||||
|
index 00000000..f4b08902
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/nptl_2_17/cancellation.c
|
||||||
|
@@ -0,0 +1,91 @@
|
||||||
|
+/* Copyright (C) 2002-2022 Free Software Foundation, Inc.
|
||||||
|
+ This file is part of the GNU C Library.
|
||||||
|
+
|
||||||
|
+ 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, see
|
||||||
|
+ <https://www.gnu.org/licenses/>. */
|
||||||
|
+
|
||||||
|
+#include <setjmp.h>
|
||||||
|
+#include <stdlib.h>
|
||||||
|
+#include "pthreadP.h"
|
||||||
|
+#include <futex-internal.h>
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+/* The next two functions are similar to pthread_setcanceltype() but
|
||||||
|
+ more specialized for the use in the cancelable functions like write().
|
||||||
|
+ They do not need to check parameters etc. These functions must be
|
||||||
|
+ AS-safe, with the exception of the actual cancellation, because they
|
||||||
|
+ are called by wrappers around AS-safe functions like write().*/
|
||||||
|
+int
|
||||||
|
+__pthread_enable_asynccancel (void)
|
||||||
|
+{
|
||||||
|
+ struct pthread *self = THREAD_SELF;
|
||||||
|
+ int oldval = atomic_load_relaxed (&self->cancelhandling);
|
||||||
|
+
|
||||||
|
+ while (1)
|
||||||
|
+ {
|
||||||
|
+ int newval = oldval | CANCELTYPE_BITMASK;
|
||||||
|
+
|
||||||
|
+ if (newval == oldval)
|
||||||
|
+ break;
|
||||||
|
+
|
||||||
|
+ if (atomic_compare_exchange_weak_acquire (&self->cancelhandling,
|
||||||
|
+ &oldval, newval))
|
||||||
|
+ {
|
||||||
|
+ if (cancel_enabled_and_canceled_and_async (newval))
|
||||||
|
+ {
|
||||||
|
+ self->result = PTHREAD_CANCELED;
|
||||||
|
+ __do_cancel ();
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return oldval;
|
||||||
|
+}
|
||||||
|
+libc_hidden_def (__pthread_enable_asynccancel)
|
||||||
|
+
|
||||||
|
+/* See the comment for __pthread_enable_asynccancel regarding
|
||||||
|
+ the AS-safety of this function. */
|
||||||
|
+void
|
||||||
|
+__pthread_disable_asynccancel (int oldtype)
|
||||||
|
+{
|
||||||
|
+ /* If asynchronous cancellation was enabled before we do not have
|
||||||
|
+ anything to do. */
|
||||||
|
+ if (oldtype & CANCELTYPE_BITMASK)
|
||||||
|
+ return;
|
||||||
|
+
|
||||||
|
+ struct pthread *self = THREAD_SELF;
|
||||||
|
+ int newval;
|
||||||
|
+ int oldval = atomic_load_relaxed (&self->cancelhandling);
|
||||||
|
+ do
|
||||||
|
+ {
|
||||||
|
+ newval = oldval & ~CANCELTYPE_BITMASK;
|
||||||
|
+ }
|
||||||
|
+ while (!atomic_compare_exchange_weak_acquire (&self->cancelhandling,
|
||||||
|
+ &oldval, newval));
|
||||||
|
+
|
||||||
|
+ /* We cannot return when we are being canceled. Upon return the
|
||||||
|
+ thread might be things which would have to be undone. The
|
||||||
|
+ following loop should loop until the cancellation signal is
|
||||||
|
+ delivered. */
|
||||||
|
+ while (__glibc_unlikely ((newval & (CANCELING_BITMASK | CANCELED_BITMASK))
|
||||||
|
+ == CANCELING_BITMASK))
|
||||||
|
+ {
|
||||||
|
+ futex_wait_simple ((unsigned int *) &self->cancelhandling, newval,
|
||||||
|
+ FUTEX_PRIVATE);
|
||||||
|
+ newval = atomic_load_relaxed (&self->cancelhandling);
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+libc_hidden_def (__pthread_disable_asynccancel)
|
||||||
|
diff --git a/nptl_2_17/cancellation_2_17.c b/nptl_2_17/cancellation_2_17.c
|
||||||
|
index 5c9ce572..9bc199e7 100644
|
||||||
|
--- a/nptl_2_17/cancellation_2_17.c
|
||||||
|
+++ b/nptl_2_17/cancellation_2_17.c
|
||||||
|
@@ -1,6 +1,5 @@
|
||||||
|
-/* Copyright (C) 2002-2018 Free Software Foundation, Inc.
|
||||||
|
+/* Copyright (C) 2002-2022 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
|
||||||
|
@@ -14,30 +13,42 @@
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with the GNU C Library; if not, see
|
||||||
|
- <http://www.gnu.org/licenses/>. */
|
||||||
|
+ <https://www.gnu.org/licenses/>. */
|
||||||
|
|
||||||
|
#include "pthreadP_2_17.h"
|
||||||
|
#include <setjmp.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <futex-internal.h>
|
||||||
|
|
||||||
|
+/* The next two functions are similar to pthread_setcanceltype() but
|
||||||
|
+ more specialized for the use in the cancelable functions like write().
|
||||||
|
+ They do not need to check parameters etc. These functions must be
|
||||||
|
+ AS-safe, with the exception of the actual cancellation, because they
|
||||||
|
+ are called by wrappers around AS-safe functions like write().*/
|
||||||
|
int
|
||||||
|
__pthread_enable_asynccancel (void)
|
||||||
|
{
|
||||||
|
struct pthread *self = THREAD_SELF;
|
||||||
|
+ int oldval = atomic_load_relaxed (&self->cancelhandling);
|
||||||
|
|
||||||
|
- int oldval = THREAD_GETMEM (self, canceltype);
|
||||||
|
- THREAD_SETMEM (self, canceltype, PTHREAD_CANCEL_ASYNCHRONOUS);
|
||||||
|
+ while (1)
|
||||||
|
+ {
|
||||||
|
+ int newval = oldval | CANCELTYPE_BITMASK;
|
||||||
|
|
||||||
|
- int ch = THREAD_GETMEM (self, cancelhandling);
|
||||||
|
+ if (newval == oldval)
|
||||||
|
+ break;
|
||||||
|
|
||||||
|
- if (self->cancelstate == PTHREAD_CANCEL_ENABLE
|
||||||
|
- && (ch & CANCELED_BITMASK)
|
||||||
|
- && !(ch & EXITING_BITMASK)
|
||||||
|
- && !(ch & TERMINATED_BITMASK))
|
||||||
|
- {
|
||||||
|
- THREAD_SETMEM (self, result, PTHREAD_CANCELED);
|
||||||
|
- __do_cancel ();
|
||||||
|
+ if (atomic_compare_exchange_weak_acquire (&self->cancelhandling,
|
||||||
|
+ &oldval, newval))
|
||||||
|
+ {
|
||||||
|
+ if (cancel_enabled_and_canceled_and_async (newval))
|
||||||
|
+ {
|
||||||
|
+ self->result = PTHREAD_CANCELED;
|
||||||
|
+ __do_cancel ();
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
|
||||||
|
return oldval;
|
||||||
|
@@ -51,10 +62,29 @@ __pthread_disable_asynccancel (int oldtype)
|
||||||
|
{
|
||||||
|
/* If asynchronous cancellation was enabled before we do not have
|
||||||
|
anything to do. */
|
||||||
|
- if (oldtype == PTHREAD_CANCEL_ASYNCHRONOUS)
|
||||||
|
+ if (oldtype & CANCELTYPE_BITMASK)
|
||||||
|
return;
|
||||||
|
|
||||||
|
struct pthread *self = THREAD_SELF;
|
||||||
|
- self->canceltype = PTHREAD_CANCEL_DEFERRED;
|
||||||
|
+ int newval;
|
||||||
|
+ int oldval = atomic_load_relaxed (&self->cancelhandling);
|
||||||
|
+ do
|
||||||
|
+ {
|
||||||
|
+ newval = oldval & ~CANCELTYPE_BITMASK;
|
||||||
|
+ }
|
||||||
|
+ while (!atomic_compare_exchange_weak_acquire (&self->cancelhandling,
|
||||||
|
+ &oldval, newval));
|
||||||
|
+
|
||||||
|
+ /* We cannot return when we are being canceled. Upon return the
|
||||||
|
+ thread might be things which would have to be undone. The
|
||||||
|
+ following loop should loop until the cancellation signal is
|
||||||
|
+ delivered. */
|
||||||
|
+ while (__glibc_unlikely ((newval & (CANCELING_BITMASK | CANCELED_BITMASK))
|
||||||
|
+ == CANCELING_BITMASK))
|
||||||
|
+ {
|
||||||
|
+ futex_wait_simple ((unsigned int *) &self->cancelhandling, newval,
|
||||||
|
+ FUTEX_PRIVATE);
|
||||||
|
+ newval = atomic_load_relaxed (&self->cancelhandling);
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
libc_hidden_def (__pthread_disable_asynccancel)
|
||||||
|
diff --git a/nptl_2_17/compat_pthread_2_17.h b/nptl_2_17/compat_pthread_2_17.h
|
||||||
|
index d13051ba..5109ebc3 100644
|
||||||
|
--- a/nptl_2_17/compat_pthread_2_17.h
|
||||||
|
+++ b/nptl_2_17/compat_pthread_2_17.h
|
||||||
|
@@ -19,30 +19,9 @@
|
||||||
|
#define __PTHREAD_MUTEX_USE_UNION 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
-#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)
|
||||||
|
- /* Bit set if canceling has been initiated. */
|
||||||
|
-#define CANCELING_BIT 2
|
||||||
|
-#define CANCELING_BITMASK (0x01 << CANCELING_BIT)
|
||||||
|
- /* Bit set if canceled. */
|
||||||
|
-#define CANCELED_BIT 3
|
||||||
|
-#define CANCELED_BITMASK (0x01 << CANCELED_BIT)
|
||||||
|
- /* Bit set if thread is exiting. */
|
||||||
|
-#define EXITING_BIT 4
|
||||||
|
-#define EXITING_BITMASK (0x01 << EXITING_BIT)
|
||||||
|
- /* Bit set if thread terminated and TCB is freed. */
|
||||||
|
-#define TERMINATED_BIT 5
|
||||||
|
-#define TERMINATED_BITMASK (0x01 << TERMINATED_BIT)
|
||||||
|
- /* Bit set if thread is supposed to change XID. */
|
||||||
|
-#define SETXID_BIT 6
|
||||||
|
-#define SETXID_BITMASK (0x01 << SETXID_BIT)
|
||||||
|
/* Mask for the rest. Helps the compiler to optimize. */
|
||||||
|
#define CANCEL_RESTMASK 0xffffff80
|
||||||
|
|
||||||
|
-
|
||||||
|
#define CANCEL_ENABLED_AND_CANCELED_AND_ASYNCHRONOUS(value) \
|
||||||
|
(((value) & (CANCELSTATE_BITMASK | CANCELTYPE_BITMASK | CANCELED_BITMASK \
|
||||||
|
| EXITING_BITMASK | CANCEL_RESTMASK | TERMINATED_BITMASK)) \
|
||||||
|
--
|
||||||
|
2.33.0
|
||||||
|
|
||||||
30
glibc.spec
30
glibc.spec
@ -66,7 +66,7 @@
|
|||||||
##############################################################################
|
##############################################################################
|
||||||
Name: glibc
|
Name: glibc
|
||||||
Version: 2.34
|
Version: 2.34
|
||||||
Release: 74
|
Release: 75
|
||||||
Summary: The GNU libc libraries
|
Summary: The GNU libc libraries
|
||||||
License: %{all_license}
|
License: %{all_license}
|
||||||
URL: http://www.gnu.org/software/glibc/
|
URL: http://www.gnu.org/software/glibc/
|
||||||
@ -212,6 +212,8 @@ Patch126: localedef-Handle-symbolic-links-when-generating-loca.patch
|
|||||||
Patch127: libio-Ensure-output-buffer-for-wchars-bug-28828.patch
|
Patch127: libio-Ensure-output-buffer-for-wchars-bug-28828.patch
|
||||||
Patch128: libio-Flush-only-_IO_str_overflow-must-not-return-EO.patch
|
Patch128: libio-Flush-only-_IO_str_overflow-must-not-return-EO.patch
|
||||||
Patch129: linux-Fix-__closefrom_fallback-iterates-until-max-in.patch
|
Patch129: linux-Fix-__closefrom_fallback-iterates-until-max-in.patch
|
||||||
|
Patch130: nptl-Handle-spurious-EINTR-when-thread-cancellation-.patch
|
||||||
|
Patch131: nptl-Fix-pthread_cancel-cancelhandling-atomic-operat.patch
|
||||||
|
|
||||||
Patch9000: turn-default-value-of-x86_rep_stosb_threshold_form_2K_to_1M.patch
|
Patch9000: turn-default-value-of-x86_rep_stosb_threshold_form_2K_to_1M.patch
|
||||||
Patch9001: delete-no-hard-link-to-avoid-all_language-package-to.patch
|
Patch9001: delete-no-hard-link-to-avoid-all_language-package-to.patch
|
||||||
@ -223,17 +225,18 @@ Patch9006: 0005-add-pthread-functions_h.patch
|
|||||||
Patch9007: 0006-add-elsion-function-which-moved-to-libc-in-glibc-2.34.patch
|
Patch9007: 0006-add-elsion-function-which-moved-to-libc-in-glibc-2.34.patch
|
||||||
Patch9008: 0007-add-lowlevellock_2_17_c.patch
|
Patch9008: 0007-add-lowlevellock_2_17_c.patch
|
||||||
Patch9009: 0008-add-pause_nocancel_2_17.patch
|
Patch9009: 0008-add-pause_nocancel_2_17.patch
|
||||||
Patch9010: 0009-add-unwind-with-longjmp.patch
|
Patch9010: 0009_nptl_2.17_adapt_for_bug_29029.patch
|
||||||
Patch9011: delete-check-installed-headers-c-and-check-installed.patch
|
Patch9011: 0009-add-unwind-with-longjmp.patch
|
||||||
Patch9012: fix-CVE-2019-1010023.patch
|
Patch9012: delete-check-installed-headers-c-and-check-installed.patch
|
||||||
Patch9013: fix-tst-glibcsyscalls-due-to-kernel-reserved-some-sy.patch
|
Patch9013: fix-CVE-2019-1010023.patch
|
||||||
Patch9014: use-region-to-instead-of-country-for-extract-timezon.patch
|
Patch9014: fix-tst-glibcsyscalls-due-to-kernel-reserved-some-sy.patch
|
||||||
Patch9015: strcmp-delete-align-for-loop_aligned.patch
|
Patch9015: use-region-to-instead-of-country-for-extract-timezon.patch
|
||||||
Patch9016: 0001-elf-dynamic-linker-load-shared-object-use-hugepage-a.patch
|
Patch9016: strcmp-delete-align-for-loop_aligned.patch
|
||||||
Patch9017: 0002-elf-ld.so-add-testcase-for-ld.so-load-shared-object-.patch
|
Patch9017: 0001-elf-dynamic-linker-load-shared-object-use-hugepage-a.patch
|
||||||
Patch9018: 0003-elf-ld.so-use-special-mmap-for-hugepage-to-get-symbo.patch
|
Patch9018: 0002-elf-ld.so-add-testcase-for-ld.so-load-shared-object-.patch
|
||||||
Patch9019: malloc-use-__get_nprocs-replace-__get_nprocs_sched.patch
|
Patch9019: 0003-elf-ld.so-use-special-mmap-for-hugepage-to-get-symbo.patch
|
||||||
Patch9020: use-mlock-to-determine-hugepage-RLIMIT_MEMLOCK-soft-.patch
|
Patch9020: malloc-use-__get_nprocs-replace-__get_nprocs_sched.patch
|
||||||
|
Patch9021: use-mlock-to-determine-hugepage-RLIMIT_MEMLOCK-soft-.patch
|
||||||
|
|
||||||
Obsoletes: nscd < 2.35
|
Obsoletes: nscd < 2.35
|
||||||
Provides: ldconfig rtld(GNU_HASH) bundled(gnulib)
|
Provides: ldconfig rtld(GNU_HASH) bundled(gnulib)
|
||||||
@ -1305,6 +1308,9 @@ fi
|
|||||||
%endif
|
%endif
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
|
* Mon Apr 18 2022 Qingqing Li <liqingqing3@huawei.com> - 2.34-75
|
||||||
|
- nptl: Handle spurious EINTR when thread cancellation is disabled (BZ#29029)
|
||||||
|
|
||||||
* Sat Apr 9 2022 Qingqing Li <liqingqing3@huawei.com> - 2.34-74
|
* Sat Apr 9 2022 Qingqing Li <liqingqing3@huawei.com> - 2.34-74
|
||||||
- libio: Ensure output buffer for wchars bug 28828
|
- libio: Ensure output buffer for wchars bug 28828
|
||||||
- libio: libio Flush onlu _IO_str_overflow must not return EOF bug 28949
|
- libio: libio Flush onlu _IO_str_overflow must not return EOF bug 28949
|
||||||
|
|||||||
38
nptl-Fix-pthread_cancel-cancelhandling-atomic-operat.patch
Normal file
38
nptl-Fix-pthread_cancel-cancelhandling-atomic-operat.patch
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
From 62be9681677e7ce820db721c126909979382d379 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
||||||
|
Date: Wed, 20 Apr 2022 12:01:43 -0300
|
||||||
|
Subject: [PATCH] nptl: Fix pthread_cancel cancelhandling atomic operations
|
||||||
|
|
||||||
|
The 404656009b reversion did not setup the atomic loop to set the
|
||||||
|
cancel bits correctly. The fix is essentially what pthread_cancel
|
||||||
|
did prior 26cfbb7162ad.
|
||||||
|
|
||||||
|
Checked on x86_64-linux-gnu and aarch64-linux-gnu.
|
||||||
|
---
|
||||||
|
nptl/pthread_cancel.c | 3 ++-
|
||||||
|
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/nptl/pthread_cancel.c b/nptl/pthread_cancel.c
|
||||||
|
index c76882e..e67b2df 100644
|
||||||
|
--- a/nptl/pthread_cancel.c
|
||||||
|
+++ b/nptl/pthread_cancel.c
|
||||||
|
@@ -121,6 +121,7 @@ __pthread_cancel (pthread_t th)
|
||||||
|
int newval;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
+ again:
|
||||||
|
newval = oldval | CANCELING_BITMASK | CANCELED_BITMASK;
|
||||||
|
if (oldval == newval)
|
||||||
|
break;
|
||||||
|
@@ -134,7 +135,7 @@ __pthread_cancel (pthread_t th)
|
||||||
|
int newval2 = oldval | CANCELING_BITMASK;
|
||||||
|
if (!atomic_compare_exchange_weak_acquire (&pd->cancelhandling,
|
||||||
|
&oldval, newval2))
|
||||||
|
- continue;
|
||||||
|
+ goto again;
|
||||||
|
|
||||||
|
if (pd == THREAD_SELF)
|
||||||
|
/* This is not merely an optimization: An application may
|
||||||
|
--
|
||||||
|
1.8.3.1
|
||||||
|
|
||||||
869
nptl-Handle-spurious-EINTR-when-thread-cancellation-.patch
Normal file
869
nptl-Handle-spurious-EINTR-when-thread-cancellation-.patch
Normal file
@ -0,0 +1,869 @@
|
|||||||
|
From 290db09546b260a30137d03ce97a857e6f15b648 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
||||||
|
Date: Wed, 6 Apr 2022 12:24:42 -0300
|
||||||
|
Subject: [PATCH] nptl: Handle spurious EINTR when thread cancellation is
|
||||||
|
disabled (BZ#29029)
|
||||||
|
|
||||||
|
Some Linux interfaces never restart after being interrupted by a signal
|
||||||
|
handler, regardless of the use of SA_RESTART [1]. It means that for
|
||||||
|
pthread cancellation, if the target thread disables cancellation with
|
||||||
|
pthread_setcancelstate and calls such interfaces (like poll or select),
|
||||||
|
it should not see spurious EINTR failures due the internal SIGCANCEL.
|
||||||
|
|
||||||
|
However recent changes made pthread_cancel to always sent the internal
|
||||||
|
signal, regardless of the target thread cancellation status or type.
|
||||||
|
To fix it, the previous semantic is restored, where the cancel signal
|
||||||
|
is only sent if the target thread has cancelation enabled in
|
||||||
|
asynchronous mode.
|
||||||
|
|
||||||
|
The cancel state and cancel type is moved back to cancelhandling
|
||||||
|
and atomic operation are used to synchronize between threads. The
|
||||||
|
patch essentially revert the following commits:
|
||||||
|
|
||||||
|
8c1c0aae20 nptl: Move cancel type out of cancelhandling
|
||||||
|
2b51742531 nptl: Move cancel state out of cancelhandling
|
||||||
|
26cfbb7162 nptl: Remove CANCELING_BITMASK
|
||||||
|
|
||||||
|
However I changed the atomic operation to follow the internal C11
|
||||||
|
semantic and removed the MACRO usage, it simplifies a bit the
|
||||||
|
resulting code (and removes another usage of the old atomic macros).
|
||||||
|
|
||||||
|
Checked on x86_64-linux-gnu, i686-linux-gnu, aarch64-linux-gnu,
|
||||||
|
and powerpc64-linux-gnu.
|
||||||
|
|
||||||
|
[1] https://man7.org/linux/man-pages/man7/signal.7.html
|
||||||
|
|
||||||
|
Reviewed-by: Florian Weimer <fweimer@redhat.com>
|
||||||
|
Tested-by: Aurelien Jarno <aurelien@aurel32.net>
|
||||||
|
|
||||||
|
(cherry-picked from commit 404656009b459658138ed1bd18f3c6cf3863e6a6)
|
||||||
|
---
|
||||||
|
NEWS | 2 +
|
||||||
|
manual/process.texi | 3 +-
|
||||||
|
nptl/allocatestack.c | 2 -
|
||||||
|
nptl/cancellation.c | 50 +++++++---
|
||||||
|
nptl/cleanup_defer.c | 42 +++++++--
|
||||||
|
nptl/descr.h | 41 +++++---
|
||||||
|
nptl/libc-cleanup.c | 39 +++++++-
|
||||||
|
nptl/pthread_cancel.c | 110 ++++++++++++++++------
|
||||||
|
nptl/pthread_join_common.c | 7 +-
|
||||||
|
nptl/pthread_setcancelstate.c | 26 +++++-
|
||||||
|
nptl/pthread_setcanceltype.c | 31 +++++-
|
||||||
|
nptl/pthread_testcancel.c | 9 +-
|
||||||
|
sysdeps/nptl/dl-tls_init_tp.c | 3 -
|
||||||
|
sysdeps/nptl/pthreadP.h | 2 +-
|
||||||
|
sysdeps/pthread/Makefile | 1 +
|
||||||
|
sysdeps/pthread/tst-cancel29.c | 207 +++++++++++++++++++++++++++++++++++++++++
|
||||||
|
16 files changed, 484 insertions(+), 91 deletions(-)
|
||||||
|
create mode 100644 sysdeps/pthread/tst-cancel29.c
|
||||||
|
|
||||||
|
diff --git a/NEWS b/NEWS
|
||||||
|
index 96e0589..55aea24 100644
|
||||||
|
--- a/NEWS
|
||||||
|
+++ b/NEWS
|
||||||
|
@@ -37,6 +37,8 @@ The following bugs are resolved with this release:
|
||||||
|
recvmsg()
|
||||||
|
[28865] linux: _SC_NPROCESSORS_CONF and _SC_NPROCESSORS_ONLN are inaccurate
|
||||||
|
without /sys and /proc
|
||||||
|
+ [29029] nptl: poll() spuriously returns EINTR during thread
|
||||||
|
+ cancellation and with cancellation disabled
|
||||||
|
|
||||||
|
|
||||||
|
Version 2.34
|
||||||
|
diff --git a/manual/process.texi b/manual/process.texi
|
||||||
|
index 28c9531..9307379 100644
|
||||||
|
--- a/manual/process.texi
|
||||||
|
+++ b/manual/process.texi
|
||||||
|
@@ -68,8 +68,7 @@ until the subprogram terminates before you can do anything else.
|
||||||
|
@c CLEANUP_HANDLER @ascuplugin @ascuheap @acsmem
|
||||||
|
@c libc_cleanup_region_start @ascuplugin @ascuheap @acsmem
|
||||||
|
@c pthread_cleanup_push_defer @ascuplugin @ascuheap @acsmem
|
||||||
|
-@c __pthread_testcancel @ascuplugin @ascuheap @acsmem
|
||||||
|
-@c CANCEL_ENABLED_AND_CANCELED ok
|
||||||
|
+@c cancel_enabled_and_canceled @ascuplugin @ascuheap @acsmem
|
||||||
|
@c do_cancel @ascuplugin @ascuheap @acsmem
|
||||||
|
@c cancel_handler ok
|
||||||
|
@c kill syscall ok
|
||||||
|
diff --git a/nptl/allocatestack.c b/nptl/allocatestack.c
|
||||||
|
index 554a721..9610175 100644
|
||||||
|
--- a/nptl/allocatestack.c
|
||||||
|
+++ b/nptl/allocatestack.c
|
||||||
|
@@ -120,8 +120,6 @@ get_cached_stack (size_t *sizep, void **memp)
|
||||||
|
|
||||||
|
/* Cancellation handling is back to the default. */
|
||||||
|
result->cancelhandling = 0;
|
||||||
|
- result->cancelstate = PTHREAD_CANCEL_ENABLE;
|
||||||
|
- result->canceltype = PTHREAD_CANCEL_DEFERRED;
|
||||||
|
result->cleanup = NULL;
|
||||||
|
result->setup_failed = 0;
|
||||||
|
|
||||||
|
diff --git a/nptl/cancellation.c b/nptl/cancellation.c
|
||||||
|
index 0596278..e97d56f 100644
|
||||||
|
--- a/nptl/cancellation.c
|
||||||
|
+++ b/nptl/cancellation.c
|
||||||
|
@@ -31,19 +31,26 @@ int
|
||||||
|
__pthread_enable_asynccancel (void)
|
||||||
|
{
|
||||||
|
struct pthread *self = THREAD_SELF;
|
||||||
|
+ int oldval = atomic_load_relaxed (&self->cancelhandling);
|
||||||
|
|
||||||
|
- int oldval = THREAD_GETMEM (self, canceltype);
|
||||||
|
- THREAD_SETMEM (self, canceltype, PTHREAD_CANCEL_ASYNCHRONOUS);
|
||||||
|
+ while (1)
|
||||||
|
+ {
|
||||||
|
+ int newval = oldval | CANCELTYPE_BITMASK;
|
||||||
|
|
||||||
|
- int ch = THREAD_GETMEM (self, cancelhandling);
|
||||||
|
+ if (newval == oldval)
|
||||||
|
+ break;
|
||||||
|
|
||||||
|
- if (self->cancelstate == PTHREAD_CANCEL_ENABLE
|
||||||
|
- && (ch & CANCELED_BITMASK)
|
||||||
|
- && !(ch & EXITING_BITMASK)
|
||||||
|
- && !(ch & TERMINATED_BITMASK))
|
||||||
|
- {
|
||||||
|
- THREAD_SETMEM (self, result, PTHREAD_CANCELED);
|
||||||
|
- __do_cancel ();
|
||||||
|
+ if (atomic_compare_exchange_weak_acquire (&self->cancelhandling,
|
||||||
|
+ &oldval, newval))
|
||||||
|
+ {
|
||||||
|
+ if (cancel_enabled_and_canceled_and_async (newval))
|
||||||
|
+ {
|
||||||
|
+ self->result = PTHREAD_CANCELED;
|
||||||
|
+ __do_cancel ();
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
|
||||||
|
return oldval;
|
||||||
|
@@ -57,10 +64,29 @@ __pthread_disable_asynccancel (int oldtype)
|
||||||
|
{
|
||||||
|
/* If asynchronous cancellation was enabled before we do not have
|
||||||
|
anything to do. */
|
||||||
|
- if (oldtype == PTHREAD_CANCEL_ASYNCHRONOUS)
|
||||||
|
+ if (oldtype & CANCELTYPE_BITMASK)
|
||||||
|
return;
|
||||||
|
|
||||||
|
struct pthread *self = THREAD_SELF;
|
||||||
|
- self->canceltype = PTHREAD_CANCEL_DEFERRED;
|
||||||
|
+ int newval;
|
||||||
|
+ int oldval = atomic_load_relaxed (&self->cancelhandling);
|
||||||
|
+ do
|
||||||
|
+ {
|
||||||
|
+ newval = oldval & ~CANCELTYPE_BITMASK;
|
||||||
|
+ }
|
||||||
|
+ while (!atomic_compare_exchange_weak_acquire (&self->cancelhandling,
|
||||||
|
+ &oldval, newval));
|
||||||
|
+
|
||||||
|
+ /* We cannot return when we are being canceled. Upon return the
|
||||||
|
+ thread might be things which would have to be undone. The
|
||||||
|
+ following loop should loop until the cancellation signal is
|
||||||
|
+ delivered. */
|
||||||
|
+ while (__glibc_unlikely ((newval & (CANCELING_BITMASK | CANCELED_BITMASK))
|
||||||
|
+ == CANCELING_BITMASK))
|
||||||
|
+ {
|
||||||
|
+ futex_wait_simple ((unsigned int *) &self->cancelhandling, newval,
|
||||||
|
+ FUTEX_PRIVATE);
|
||||||
|
+ newval = atomic_load_relaxed (&self->cancelhandling);
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
libc_hidden_def (__pthread_disable_asynccancel)
|
||||||
|
diff --git a/nptl/cleanup_defer.c b/nptl/cleanup_defer.c
|
||||||
|
index 7e858d0..35ba40f 100644
|
||||||
|
--- a/nptl/cleanup_defer.c
|
||||||
|
+++ b/nptl/cleanup_defer.c
|
||||||
|
@@ -31,9 +31,22 @@ ___pthread_register_cancel_defer (__pthread_unwind_buf_t *buf)
|
||||||
|
ibuf->priv.data.prev = THREAD_GETMEM (self, cleanup_jmp_buf);
|
||||||
|
ibuf->priv.data.cleanup = THREAD_GETMEM (self, cleanup);
|
||||||
|
|
||||||
|
- /* Disable asynchronous cancellation for now. */
|
||||||
|
- ibuf->priv.data.canceltype = THREAD_GETMEM (self, canceltype);
|
||||||
|
- THREAD_SETMEM (self, canceltype, PTHREAD_CANCEL_DEFERRED);
|
||||||
|
+ int cancelhandling = atomic_load_relaxed (&self->cancelhandling);
|
||||||
|
+ if (__glibc_unlikely (cancelhandling & CANCELTYPE_BITMASK))
|
||||||
|
+ {
|
||||||
|
+ int newval;
|
||||||
|
+ do
|
||||||
|
+ {
|
||||||
|
+ newval = cancelhandling & ~CANCELTYPE_BITMASK;
|
||||||
|
+ }
|
||||||
|
+ while (!atomic_compare_exchange_weak_acquire (&self->cancelhandling,
|
||||||
|
+ &cancelhandling,
|
||||||
|
+ newval));
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ ibuf->priv.data.canceltype = (cancelhandling & CANCELTYPE_BITMASK
|
||||||
|
+ ? PTHREAD_CANCEL_ASYNCHRONOUS
|
||||||
|
+ : PTHREAD_CANCEL_DEFERRED);
|
||||||
|
|
||||||
|
/* Store the new cleanup handler info. */
|
||||||
|
THREAD_SETMEM (self, cleanup_jmp_buf, (struct pthread_unwind_buf *) buf);
|
||||||
|
@@ -55,9 +68,26 @@ ___pthread_unregister_cancel_restore (__pthread_unwind_buf_t *buf)
|
||||||
|
|
||||||
|
THREAD_SETMEM (self, cleanup_jmp_buf, ibuf->priv.data.prev);
|
||||||
|
|
||||||
|
- THREAD_SETMEM (self, canceltype, ibuf->priv.data.canceltype);
|
||||||
|
- if (ibuf->priv.data.canceltype == PTHREAD_CANCEL_ASYNCHRONOUS)
|
||||||
|
- __pthread_testcancel ();
|
||||||
|
+ if (ibuf->priv.data.canceltype == PTHREAD_CANCEL_DEFERRED)
|
||||||
|
+ return;
|
||||||
|
+
|
||||||
|
+ int cancelhandling = atomic_load_relaxed (&self->cancelhandling);
|
||||||
|
+ if (cancelhandling & CANCELTYPE_BITMASK)
|
||||||
|
+ {
|
||||||
|
+ int newval;
|
||||||
|
+ do
|
||||||
|
+ {
|
||||||
|
+ newval = cancelhandling | CANCELTYPE_BITMASK;
|
||||||
|
+ }
|
||||||
|
+ while (!atomic_compare_exchange_weak_acquire (&self->cancelhandling,
|
||||||
|
+ &cancelhandling, newval));
|
||||||
|
+
|
||||||
|
+ if (cancel_enabled_and_canceled (cancelhandling))
|
||||||
|
+ {
|
||||||
|
+ self->result = PTHREAD_CANCELED;
|
||||||
|
+ __do_cancel ();
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
versioned_symbol (libc, ___pthread_unregister_cancel_restore,
|
||||||
|
__pthread_unregister_cancel_restore, GLIBC_2_34);
|
||||||
|
diff --git a/nptl/descr.h b/nptl/descr.h
|
||||||
|
index 4de8413..03a4358 100644
|
||||||
|
--- a/nptl/descr.h
|
||||||
|
+++ b/nptl/descr.h
|
||||||
|
@@ -277,18 +277,27 @@ struct pthread
|
||||||
|
|
||||||
|
/* Flags determining processing of cancellation. */
|
||||||
|
int cancelhandling;
|
||||||
|
+ /* Bit set if cancellation is disabled. */
|
||||||
|
+#define CANCELSTATE_BIT 0
|
||||||
|
+#define CANCELSTATE_BITMASK (1 << CANCELSTATE_BIT)
|
||||||
|
+ /* Bit set if asynchronous cancellation mode is selected. */
|
||||||
|
+#define CANCELTYPE_BIT 1
|
||||||
|
+#define CANCELTYPE_BITMASK (1 << CANCELTYPE_BIT)
|
||||||
|
+ /* Bit set if canceling has been initiated. */
|
||||||
|
+#define CANCELING_BIT 2
|
||||||
|
+#define CANCELING_BITMASK (1 << CANCELING_BIT)
|
||||||
|
/* Bit set if canceled. */
|
||||||
|
#define CANCELED_BIT 3
|
||||||
|
-#define CANCELED_BITMASK (0x01 << CANCELED_BIT)
|
||||||
|
+#define CANCELED_BITMASK (1 << CANCELED_BIT)
|
||||||
|
/* Bit set if thread is exiting. */
|
||||||
|
#define EXITING_BIT 4
|
||||||
|
-#define EXITING_BITMASK (0x01 << EXITING_BIT)
|
||||||
|
+#define EXITING_BITMASK (1 << EXITING_BIT)
|
||||||
|
/* Bit set if thread terminated and TCB is freed. */
|
||||||
|
#define TERMINATED_BIT 5
|
||||||
|
-#define TERMINATED_BITMASK (0x01 << TERMINATED_BIT)
|
||||||
|
+#define TERMINATED_BITMASK (1 << TERMINATED_BIT)
|
||||||
|
/* Bit set if thread is supposed to change XID. */
|
||||||
|
#define SETXID_BIT 6
|
||||||
|
-#define SETXID_BITMASK (0x01 << SETXID_BIT)
|
||||||
|
+#define SETXID_BITMASK (1 << SETXID_BIT)
|
||||||
|
|
||||||
|
/* Flags. Including those copied from the thread attribute. */
|
||||||
|
int flags;
|
||||||
|
@@ -388,14 +397,6 @@ struct pthread
|
||||||
|
/* Indicates whether is a C11 thread created by thrd_creat. */
|
||||||
|
bool c11;
|
||||||
|
|
||||||
|
- /* Thread cancel state (PTHREAD_CANCEL_ENABLE or
|
||||||
|
- PTHREAD_CANCEL_DISABLE). */
|
||||||
|
- unsigned char cancelstate;
|
||||||
|
-
|
||||||
|
- /* Thread cancel type (PTHREAD_CANCEL_DEFERRED or
|
||||||
|
- PTHREAD_CANCEL_ASYNCHRONOUS). */
|
||||||
|
- unsigned char canceltype;
|
||||||
|
-
|
||||||
|
/* Used in __pthread_kill_internal to detected a thread that has
|
||||||
|
exited or is about to exit. exit_lock must only be acquired
|
||||||
|
after blocking signals. */
|
||||||
|
@@ -412,6 +413,22 @@ struct pthread
|
||||||
|
(sizeof (struct pthread) - offsetof (struct pthread, end_padding))
|
||||||
|
} __attribute ((aligned (TCB_ALIGNMENT)));
|
||||||
|
|
||||||
|
+static inline bool
|
||||||
|
+cancel_enabled_and_canceled (int value)
|
||||||
|
+{
|
||||||
|
+ return (value & (CANCELSTATE_BITMASK | CANCELED_BITMASK | EXITING_BITMASK
|
||||||
|
+ | TERMINATED_BITMASK))
|
||||||
|
+ == CANCELED_BITMASK;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static inline bool
|
||||||
|
+cancel_enabled_and_canceled_and_async (int value)
|
||||||
|
+{
|
||||||
|
+ return ((value) & (CANCELSTATE_BITMASK | CANCELTYPE_BITMASK | CANCELED_BITMASK
|
||||||
|
+ | EXITING_BITMASK | TERMINATED_BITMASK))
|
||||||
|
+ == (CANCELTYPE_BITMASK | CANCELED_BITMASK);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
/* This yields the pointer that TLS support code calls the thread pointer. */
|
||||||
|
#if TLS_TCB_AT_TP
|
||||||
|
# define TLS_TPADJ(pd) (pd)
|
||||||
|
diff --git a/nptl/libc-cleanup.c b/nptl/libc-cleanup.c
|
||||||
|
index 180d15b..fccb1ab 100644
|
||||||
|
--- a/nptl/libc-cleanup.c
|
||||||
|
+++ b/nptl/libc-cleanup.c
|
||||||
|
@@ -27,9 +27,24 @@ __libc_cleanup_push_defer (struct _pthread_cleanup_buffer *buffer)
|
||||||
|
|
||||||
|
buffer->__prev = THREAD_GETMEM (self, cleanup);
|
||||||
|
|
||||||
|
+ int cancelhandling = atomic_load_relaxed (&self->cancelhandling);
|
||||||
|
+
|
||||||
|
/* Disable asynchronous cancellation for now. */
|
||||||
|
- buffer->__canceltype = THREAD_GETMEM (self, canceltype);
|
||||||
|
- THREAD_SETMEM (self, canceltype, PTHREAD_CANCEL_DEFERRED);
|
||||||
|
+ if (__glibc_unlikely (cancelhandling & CANCELTYPE_BITMASK))
|
||||||
|
+ {
|
||||||
|
+ int newval;
|
||||||
|
+ do
|
||||||
|
+ {
|
||||||
|
+ newval = cancelhandling & ~CANCELTYPE_BITMASK;
|
||||||
|
+ }
|
||||||
|
+ while (!atomic_compare_exchange_weak_acquire (&self->cancelhandling,
|
||||||
|
+ &cancelhandling,
|
||||||
|
+ newval));
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ buffer->__canceltype = (cancelhandling & CANCELTYPE_BITMASK
|
||||||
|
+ ? PTHREAD_CANCEL_ASYNCHRONOUS
|
||||||
|
+ : PTHREAD_CANCEL_DEFERRED);
|
||||||
|
|
||||||
|
THREAD_SETMEM (self, cleanup, buffer);
|
||||||
|
}
|
||||||
|
@@ -42,8 +57,22 @@ __libc_cleanup_pop_restore (struct _pthread_cleanup_buffer *buffer)
|
||||||
|
|
||||||
|
THREAD_SETMEM (self, cleanup, buffer->__prev);
|
||||||
|
|
||||||
|
- THREAD_SETMEM (self, canceltype, buffer->__canceltype);
|
||||||
|
- if (buffer->__canceltype == PTHREAD_CANCEL_ASYNCHRONOUS)
|
||||||
|
- __pthread_testcancel ();
|
||||||
|
+ int cancelhandling = atomic_load_relaxed (&self->cancelhandling);
|
||||||
|
+ if (cancelhandling & CANCELTYPE_BITMASK)
|
||||||
|
+ {
|
||||||
|
+ int newval;
|
||||||
|
+ do
|
||||||
|
+ {
|
||||||
|
+ newval = cancelhandling | CANCELTYPE_BITMASK;
|
||||||
|
+ }
|
||||||
|
+ while (!atomic_compare_exchange_weak_acquire (&self->cancelhandling,
|
||||||
|
+ &cancelhandling, newval));
|
||||||
|
+
|
||||||
|
+ if (cancel_enabled_and_canceled (cancelhandling))
|
||||||
|
+ {
|
||||||
|
+ self->result = PTHREAD_CANCELED;
|
||||||
|
+ __do_cancel ();
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
libc_hidden_def (__libc_cleanup_pop_restore)
|
||||||
|
diff --git a/nptl/pthread_cancel.c b/nptl/pthread_cancel.c
|
||||||
|
index 9bac6e3..2680b55 100644
|
||||||
|
--- a/nptl/pthread_cancel.c
|
||||||
|
+++ b/nptl/pthread_cancel.c
|
||||||
|
@@ -43,18 +43,29 @@ sigcancel_handler (int sig, siginfo_t *si, void *ctx)
|
||||||
|
|
||||||
|
struct pthread *self = THREAD_SELF;
|
||||||
|
|
||||||
|
- int ch = atomic_load_relaxed (&self->cancelhandling);
|
||||||
|
- /* Cancelation not enabled, not cancelled, or already exitting. */
|
||||||
|
- if (self->cancelstate == PTHREAD_CANCEL_DISABLE
|
||||||
|
- || (ch & CANCELED_BITMASK) == 0
|
||||||
|
- || (ch & EXITING_BITMASK) != 0)
|
||||||
|
- return;
|
||||||
|
-
|
||||||
|
- /* Set the return value. */
|
||||||
|
- THREAD_SETMEM (self, result, PTHREAD_CANCELED);
|
||||||
|
- /* Make sure asynchronous cancellation is still enabled. */
|
||||||
|
- if (self->canceltype == PTHREAD_CANCEL_ASYNCHRONOUS)
|
||||||
|
- __do_cancel ();
|
||||||
|
+ int oldval = atomic_load_relaxed (&self->cancelhandling);
|
||||||
|
+ while (1)
|
||||||
|
+ {
|
||||||
|
+ /* We are canceled now. When canceled by another thread this flag
|
||||||
|
+ is already set but if the signal is directly send (internally or
|
||||||
|
+ from another process) is has to be done here. */
|
||||||
|
+ int newval = oldval | CANCELING_BITMASK | CANCELED_BITMASK;
|
||||||
|
+
|
||||||
|
+ if (oldval == newval || (oldval & EXITING_BITMASK) != 0)
|
||||||
|
+ /* Already canceled or exiting. */
|
||||||
|
+ break;
|
||||||
|
+
|
||||||
|
+ if (atomic_compare_exchange_weak_acquire (&self->cancelhandling,
|
||||||
|
+ &oldval, newval))
|
||||||
|
+ {
|
||||||
|
+ self->result = PTHREAD_CANCELED;
|
||||||
|
+
|
||||||
|
+ /* Make sure asynchronous cancellation is still enabled. */
|
||||||
|
+ if ((oldval & CANCELTYPE_BITMASK) != 0)
|
||||||
|
+ /* Run the registered destructors and terminate the thread. */
|
||||||
|
+ __do_cancel ();
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
@@ -93,29 +104,70 @@ __pthread_cancel (pthread_t th)
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
- int oldch = atomic_fetch_or_acquire (&pd->cancelhandling, CANCELED_BITMASK);
|
||||||
|
- if ((oldch & CANCELED_BITMASK) != 0)
|
||||||
|
- return 0;
|
||||||
|
-
|
||||||
|
- if (pd == THREAD_SELF)
|
||||||
|
+ /* Some syscalls are never restarted after being interrupted by a signal
|
||||||
|
+ handler, regardless of the use of SA_RESTART (they always fail with
|
||||||
|
+ EINTR). So pthread_cancel cannot send SIGCANCEL unless the cancellation
|
||||||
|
+ is enabled and set as asynchronous (in this case the cancellation will
|
||||||
|
+ be acted in the cancellation handler instead by the syscall wrapper).
|
||||||
|
+ Otherwise the target thread is set as 'cancelling' (CANCELING_BITMASK)
|
||||||
|
+ by atomically setting 'cancelhandling' and the cancelation will be acted
|
||||||
|
+ upon on next cancellation entrypoing in the target thread.
|
||||||
|
+
|
||||||
|
+ It also requires to atomically check if cancellation is enabled and
|
||||||
|
+ asynchronous, so both cancellation state and type are tracked on
|
||||||
|
+ 'cancelhandling'. */
|
||||||
|
+
|
||||||
|
+ int result = 0;
|
||||||
|
+ int oldval = atomic_load_relaxed (&pd->cancelhandling);
|
||||||
|
+ int newval;
|
||||||
|
+ do
|
||||||
|
{
|
||||||
|
- /* A single-threaded process should be able to kill itself, since there
|
||||||
|
- is nothing in the POSIX specification that says that it cannot. So
|
||||||
|
- we set multiple_threads to true so that cancellation points get
|
||||||
|
- executed. */
|
||||||
|
- THREAD_SETMEM (THREAD_SELF, header.multiple_threads, 1);
|
||||||
|
+ newval = oldval | CANCELING_BITMASK | CANCELED_BITMASK;
|
||||||
|
+ if (oldval == newval)
|
||||||
|
+ break;
|
||||||
|
+
|
||||||
|
+ /* 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_async (newval))
|
||||||
|
+ {
|
||||||
|
+ /* Mark the cancellation as "in progress". */
|
||||||
|
+ int newval2 = oldval | CANCELING_BITMASK;
|
||||||
|
+ if (!atomic_compare_exchange_weak_acquire (&pd->cancelhandling,
|
||||||
|
+ &oldval, newval2))
|
||||||
|
+ continue;
|
||||||
|
+
|
||||||
|
+ if (pd == THREAD_SELF)
|
||||||
|
+ /* This is not merely an optimization: An application may
|
||||||
|
+ call pthread_cancel (pthread_self ()) without calling
|
||||||
|
+ pthread_create, so the signal handler may not have been
|
||||||
|
+ set up for a self-cancel. */
|
||||||
|
+ {
|
||||||
|
+ pd->result = PTHREAD_CANCELED;
|
||||||
|
+ if ((newval & CANCELTYPE_BITMASK) != 0)
|
||||||
|
+ __do_cancel ();
|
||||||
|
+ }
|
||||||
|
+ else
|
||||||
|
+ /* The cancellation handler will take care of marking the
|
||||||
|
+ thread as canceled. */
|
||||||
|
+ result = __pthread_kill_internal (th, SIGCANCEL);
|
||||||
|
+
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /* A single-threaded process should be able to kill itself, since
|
||||||
|
+ there is nothing in the POSIX specification that says that it
|
||||||
|
+ cannot. So we set multiple_threads to true so that cancellation
|
||||||
|
+ points get executed. */
|
||||||
|
+ THREAD_SETMEM (THREAD_SELF, header.multiple_threads, 1);
|
||||||
|
#ifndef TLS_MULTIPLE_THREADS_IN_TCB
|
||||||
|
__libc_multiple_threads = 1;
|
||||||
|
#endif
|
||||||
|
-
|
||||||
|
- THREAD_SETMEM (pd, result, PTHREAD_CANCELED);
|
||||||
|
- if (pd->cancelstate == PTHREAD_CANCEL_ENABLE
|
||||||
|
- && pd->canceltype == PTHREAD_CANCEL_ASYNCHRONOUS)
|
||||||
|
- __do_cancel ();
|
||||||
|
- return 0;
|
||||||
|
}
|
||||||
|
+ while (!atomic_compare_exchange_weak_acquire (&pd->cancelhandling, &oldval,
|
||||||
|
+ newval));
|
||||||
|
|
||||||
|
- return __pthread_kill_internal (th, SIGCANCEL);
|
||||||
|
+ return result;
|
||||||
|
}
|
||||||
|
versioned_symbol (libc, __pthread_cancel, pthread_cancel, GLIBC_2_34);
|
||||||
|
|
||||||
|
diff --git a/nptl/pthread_join_common.c b/nptl/pthread_join_common.c
|
||||||
|
index 7303069..617056e 100644
|
||||||
|
--- a/nptl/pthread_join_common.c
|
||||||
|
+++ b/nptl/pthread_join_common.c
|
||||||
|
@@ -57,12 +57,9 @@ __pthread_clockjoin_ex (pthread_t threadid, void **thread_return,
|
||||||
|
if ((pd == self
|
||||||
|
|| (self->joinid == pd
|
||||||
|
&& (pd->cancelhandling
|
||||||
|
- & (CANCELED_BITMASK | EXITING_BITMASK
|
||||||
|
+ & (CANCELING_BITMASK | CANCELED_BITMASK | EXITING_BITMASK
|
||||||
|
| TERMINATED_BITMASK)) == 0))
|
||||||
|
- && !(self->cancelstate == PTHREAD_CANCEL_ENABLE
|
||||||
|
- && (pd->cancelhandling & (CANCELED_BITMASK | EXITING_BITMASK
|
||||||
|
- | TERMINATED_BITMASK))
|
||||||
|
- == CANCELED_BITMASK))
|
||||||
|
+ && !cancel_enabled_and_canceled (self->cancelhandling))
|
||||||
|
/* 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
|
||||||
|
diff --git a/nptl/pthread_setcancelstate.c b/nptl/pthread_setcancelstate.c
|
||||||
|
index 7e2b6e4..cb567be 100644
|
||||||
|
--- a/nptl/pthread_setcancelstate.c
|
||||||
|
+++ b/nptl/pthread_setcancelstate.c
|
||||||
|
@@ -31,9 +31,29 @@ __pthread_setcancelstate (int state, int *oldstate)
|
||||||
|
|
||||||
|
self = THREAD_SELF;
|
||||||
|
|
||||||
|
- if (oldstate != NULL)
|
||||||
|
- *oldstate = self->cancelstate;
|
||||||
|
- self->cancelstate = state;
|
||||||
|
+ int oldval = atomic_load_relaxed (&self->cancelhandling);
|
||||||
|
+ while (1)
|
||||||
|
+ {
|
||||||
|
+ int newval = (state == PTHREAD_CANCEL_DISABLE
|
||||||
|
+ ? oldval | CANCELSTATE_BITMASK
|
||||||
|
+ : oldval & ~CANCELSTATE_BITMASK);
|
||||||
|
+
|
||||||
|
+ if (oldstate != NULL)
|
||||||
|
+ *oldstate = ((oldval & CANCELSTATE_BITMASK)
|
||||||
|
+ ? PTHREAD_CANCEL_DISABLE : PTHREAD_CANCEL_ENABLE);
|
||||||
|
+
|
||||||
|
+ if (oldval == newval)
|
||||||
|
+ break;
|
||||||
|
+
|
||||||
|
+ if (atomic_compare_exchange_weak_acquire (&self->cancelhandling,
|
||||||
|
+ &oldval, newval))
|
||||||
|
+ {
|
||||||
|
+ if (cancel_enabled_and_canceled_and_async (newval))
|
||||||
|
+ __do_cancel ();
|
||||||
|
+
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
diff --git a/nptl/pthread_setcanceltype.c b/nptl/pthread_setcanceltype.c
|
||||||
|
index e7b24ae..e08ff7b 100644
|
||||||
|
--- a/nptl/pthread_setcanceltype.c
|
||||||
|
+++ b/nptl/pthread_setcanceltype.c
|
||||||
|
@@ -29,11 +29,32 @@ __pthread_setcanceltype (int type, int *oldtype)
|
||||||
|
|
||||||
|
volatile struct pthread *self = THREAD_SELF;
|
||||||
|
|
||||||
|
- if (oldtype != NULL)
|
||||||
|
- *oldtype = self->canceltype;
|
||||||
|
- self->canceltype = type;
|
||||||
|
- if (type == PTHREAD_CANCEL_ASYNCHRONOUS)
|
||||||
|
- __pthread_testcancel ();
|
||||||
|
+ int oldval = atomic_load_relaxed (&self->cancelhandling);
|
||||||
|
+ while (1)
|
||||||
|
+ {
|
||||||
|
+ int newval = (type == PTHREAD_CANCEL_ASYNCHRONOUS
|
||||||
|
+ ? oldval | CANCELTYPE_BITMASK
|
||||||
|
+ : oldval & ~CANCELTYPE_BITMASK);
|
||||||
|
+
|
||||||
|
+ if (oldtype != NULL)
|
||||||
|
+ *oldtype = ((oldval & CANCELTYPE_BITMASK)
|
||||||
|
+ ? PTHREAD_CANCEL_ASYNCHRONOUS : PTHREAD_CANCEL_DEFERRED);
|
||||||
|
+
|
||||||
|
+ if (oldval == newval)
|
||||||
|
+ break;
|
||||||
|
+
|
||||||
|
+ if (atomic_compare_exchange_weak_acquire (&self->cancelhandling,
|
||||||
|
+ &oldval, newval))
|
||||||
|
+ {
|
||||||
|
+ if (cancel_enabled_and_canceled_and_async (newval))
|
||||||
|
+ {
|
||||||
|
+ THREAD_SETMEM (self, result, PTHREAD_CANCELED);
|
||||||
|
+ __do_cancel ();
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
diff --git a/nptl/pthread_testcancel.c b/nptl/pthread_testcancel.c
|
||||||
|
index 31185d8..2523021 100644
|
||||||
|
--- a/nptl/pthread_testcancel.c
|
||||||
|
+++ b/nptl/pthread_testcancel.c
|
||||||
|
@@ -24,13 +24,10 @@ void
|
||||||
|
___pthread_testcancel (void)
|
||||||
|
{
|
||||||
|
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))
|
||||||
|
+ int cancelhandling = atomic_load_relaxed (&self->cancelhandling);
|
||||||
|
+ if (cancel_enabled_and_canceled (cancelhandling))
|
||||||
|
{
|
||||||
|
- THREAD_SETMEM (self, result, PTHREAD_CANCELED);
|
||||||
|
+ self->result = PTHREAD_CANCELED;
|
||||||
|
__do_cancel ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
diff --git a/sysdeps/nptl/dl-tls_init_tp.c b/sysdeps/nptl/dl-tls_init_tp.c
|
||||||
|
index ca494dd..c2054f8 100644
|
||||||
|
--- a/sysdeps/nptl/dl-tls_init_tp.c
|
||||||
|
+++ b/sysdeps/nptl/dl-tls_init_tp.c
|
||||||
|
@@ -94,7 +94,4 @@ __tls_init_tp (void)
|
||||||
|
It will be bigger than it actually is, but for unwind.c/pt-longjmp.c
|
||||||
|
purposes this is good enough. */
|
||||||
|
THREAD_SETMEM (pd, stackblock_size, (size_t) __libc_stack_end);
|
||||||
|
-
|
||||||
|
- THREAD_SETMEM (pd, cancelstate, PTHREAD_CANCEL_ENABLE);
|
||||||
|
- THREAD_SETMEM (pd, canceltype, PTHREAD_CANCEL_DEFERRED);
|
||||||
|
}
|
||||||
|
diff --git a/sysdeps/nptl/pthreadP.h b/sysdeps/nptl/pthreadP.h
|
||||||
|
index 374657a..b968afc 100644
|
||||||
|
--- a/sysdeps/nptl/pthreadP.h
|
||||||
|
+++ b/sysdeps/nptl/pthreadP.h
|
||||||
|
@@ -276,7 +276,7 @@ __do_cancel (void)
|
||||||
|
struct pthread *self = THREAD_SELF;
|
||||||
|
|
||||||
|
/* Make sure we get no more cancellations. */
|
||||||
|
- THREAD_ATOMIC_BIT_SET (self, cancelhandling, EXITING_BIT);
|
||||||
|
+ atomic_bit_set (&self->cancelhandling, EXITING_BIT);
|
||||||
|
|
||||||
|
__pthread_unwind ((__pthread_unwind_buf_t *)
|
||||||
|
THREAD_GETMEM (self, cleanup_jmp_buf));
|
||||||
|
diff --git a/sysdeps/pthread/Makefile b/sysdeps/pthread/Makefile
|
||||||
|
index c657101..00419c4 100644
|
||||||
|
--- a/sysdeps/pthread/Makefile
|
||||||
|
+++ b/sysdeps/pthread/Makefile
|
||||||
|
@@ -69,6 +69,7 @@ tests += tst-cnd-basic tst-mtx-trylock tst-cnd-broadcast \
|
||||||
|
tst-cancel12 tst-cancel13 tst-cancel14 tst-cancel15 tst-cancel16 \
|
||||||
|
tst-cancel18 tst-cancel19 tst-cancel20 tst-cancel21 \
|
||||||
|
tst-cancel22 tst-cancel23 tst-cancel26 tst-cancel27 tst-cancel28 \
|
||||||
|
+ tst-cancel29 \
|
||||||
|
tst-cleanup0 tst-cleanup1 tst-cleanup2 tst-cleanup3 \
|
||||||
|
tst-clock1 \
|
||||||
|
tst-cond-except \
|
||||||
|
diff --git a/sysdeps/pthread/tst-cancel29.c b/sysdeps/pthread/tst-cancel29.c
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000..4f0d99e
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/sysdeps/pthread/tst-cancel29.c
|
||||||
|
@@ -0,0 +1,207 @@
|
||||||
|
+/* Check if a thread that disables cancellation and which call functions
|
||||||
|
+ that might be interrupted by a signal do not see the internal SIGCANCEL.
|
||||||
|
+
|
||||||
|
+ Copyright (C) 2022 Free Software Foundation, Inc.
|
||||||
|
+ This file is part of the GNU C Library.
|
||||||
|
+
|
||||||
|
+ 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, see
|
||||||
|
+ <https://www.gnu.org/licenses/>. */
|
||||||
|
+
|
||||||
|
+#include <array_length.h>
|
||||||
|
+#include <errno.h>
|
||||||
|
+#include <inttypes.h>
|
||||||
|
+#include <poll.h>
|
||||||
|
+#include <support/check.h>
|
||||||
|
+#include <support/support.h>
|
||||||
|
+#include <support/temp_file.h>
|
||||||
|
+#include <support/xthread.h>
|
||||||
|
+#include <sys/socket.h>
|
||||||
|
+#include <signal.h>
|
||||||
|
+#include <stdio.h>
|
||||||
|
+#include <unistd.h>
|
||||||
|
+
|
||||||
|
+/* On Linux some interfaces are never restarted after being interrupted by
|
||||||
|
+ a signal handler, regardless of the use of SA_RESTART. It means that
|
||||||
|
+ if asynchronous cancellation is not enabled, the pthread_cancel can not
|
||||||
|
+ set the internal SIGCANCEL otherwise the interface might see a spurious
|
||||||
|
+ EINTR failure. */
|
||||||
|
+
|
||||||
|
+static pthread_barrier_t b;
|
||||||
|
+
|
||||||
|
+/* Cleanup handling test. */
|
||||||
|
+static int cl_called;
|
||||||
|
+static void
|
||||||
|
+cl (void *arg)
|
||||||
|
+{
|
||||||
|
+ ++cl_called;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void *
|
||||||
|
+tf_sigtimedwait (void *arg)
|
||||||
|
+{
|
||||||
|
+ pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, NULL);
|
||||||
|
+ xpthread_barrier_wait (&b);
|
||||||
|
+
|
||||||
|
+ int r;
|
||||||
|
+ pthread_cleanup_push (cl, NULL);
|
||||||
|
+
|
||||||
|
+ sigset_t mask;
|
||||||
|
+ sigemptyset (&mask);
|
||||||
|
+ r = sigtimedwait (&mask, NULL, &(struct timespec) { 0, 250000000 });
|
||||||
|
+ if (r != -1)
|
||||||
|
+ return (void*) -1;
|
||||||
|
+ if (errno != EAGAIN)
|
||||||
|
+ return (void*) -2;
|
||||||
|
+
|
||||||
|
+ pthread_cleanup_pop (0);
|
||||||
|
+ return NULL;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void *
|
||||||
|
+tf_poll (void *arg)
|
||||||
|
+{
|
||||||
|
+ pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, NULL);
|
||||||
|
+ xpthread_barrier_wait (&b);
|
||||||
|
+
|
||||||
|
+ int r;
|
||||||
|
+ pthread_cleanup_push (cl, NULL);
|
||||||
|
+
|
||||||
|
+ r = poll (NULL, 0, 250);
|
||||||
|
+ if (r != 0)
|
||||||
|
+ return (void*) -1;
|
||||||
|
+
|
||||||
|
+ pthread_cleanup_pop (0);
|
||||||
|
+ return NULL;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void *
|
||||||
|
+tf_ppoll (void *arg)
|
||||||
|
+{
|
||||||
|
+ pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, NULL);
|
||||||
|
+
|
||||||
|
+ xpthread_barrier_wait (&b);
|
||||||
|
+
|
||||||
|
+ int r;
|
||||||
|
+ pthread_cleanup_push (cl, NULL);
|
||||||
|
+
|
||||||
|
+ r = ppoll (NULL, 0, &(struct timespec) { 0, 250000000 }, NULL);
|
||||||
|
+ if (r != 0)
|
||||||
|
+ return (void*) -1;
|
||||||
|
+
|
||||||
|
+ pthread_cleanup_pop (0);
|
||||||
|
+ return NULL;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void *
|
||||||
|
+tf_select (void *arg)
|
||||||
|
+{
|
||||||
|
+ pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, NULL);
|
||||||
|
+ xpthread_barrier_wait (&b);
|
||||||
|
+
|
||||||
|
+ int r;
|
||||||
|
+ pthread_cleanup_push (cl, NULL);
|
||||||
|
+
|
||||||
|
+ r = select (0, NULL, NULL, NULL, &(struct timeval) { 0, 250000 });
|
||||||
|
+ if (r != 0)
|
||||||
|
+ return (void*) -1;
|
||||||
|
+
|
||||||
|
+ pthread_cleanup_pop (0);
|
||||||
|
+ return NULL;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void *
|
||||||
|
+tf_pselect (void *arg)
|
||||||
|
+{
|
||||||
|
+ pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, NULL);
|
||||||
|
+ xpthread_barrier_wait (&b);
|
||||||
|
+
|
||||||
|
+ int r;
|
||||||
|
+ pthread_cleanup_push (cl, NULL);
|
||||||
|
+
|
||||||
|
+ r = pselect (0, NULL, NULL, NULL, &(struct timespec) { 0, 250000000 }, NULL);
|
||||||
|
+ if (r != 0)
|
||||||
|
+ return (void*) -1;
|
||||||
|
+
|
||||||
|
+ pthread_cleanup_pop (0);
|
||||||
|
+ return NULL;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void *
|
||||||
|
+tf_clock_nanosleep (void *arg)
|
||||||
|
+{
|
||||||
|
+ pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, NULL);
|
||||||
|
+ xpthread_barrier_wait (&b);
|
||||||
|
+
|
||||||
|
+ int r;
|
||||||
|
+ pthread_cleanup_push (cl, NULL);
|
||||||
|
+
|
||||||
|
+ r = clock_nanosleep (CLOCK_REALTIME, 0, &(struct timespec) { 0, 250000000 },
|
||||||
|
+ NULL);
|
||||||
|
+ if (r != 0)
|
||||||
|
+ return (void*) -1;
|
||||||
|
+
|
||||||
|
+ pthread_cleanup_pop (0);
|
||||||
|
+ return NULL;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+struct cancel_test_t
|
||||||
|
+{
|
||||||
|
+ const char *name;
|
||||||
|
+ void * (*cf) (void *);
|
||||||
|
+} tests[] =
|
||||||
|
+{
|
||||||
|
+ { "sigtimedwait", tf_sigtimedwait, },
|
||||||
|
+ { "poll", tf_poll, },
|
||||||
|
+ { "ppoll", tf_ppoll, },
|
||||||
|
+ { "select", tf_select, },
|
||||||
|
+ { "pselect", tf_pselect , },
|
||||||
|
+ { "clock_nanosleep", tf_clock_nanosleep, },
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static int
|
||||||
|
+do_test (void)
|
||||||
|
+{
|
||||||
|
+ for (int i = 0; i < array_length (tests); i++)
|
||||||
|
+ {
|
||||||
|
+ xpthread_barrier_init (&b, NULL, 2);
|
||||||
|
+
|
||||||
|
+ cl_called = 0;
|
||||||
|
+
|
||||||
|
+ pthread_t th = xpthread_create (NULL, tests[i].cf, NULL);
|
||||||
|
+
|
||||||
|
+ xpthread_barrier_wait (&b);
|
||||||
|
+
|
||||||
|
+ struct timespec ts = { .tv_sec = 0, .tv_nsec = 100000000 };
|
||||||
|
+ while (nanosleep (&ts, &ts) != 0)
|
||||||
|
+ continue;
|
||||||
|
+
|
||||||
|
+ xpthread_cancel (th);
|
||||||
|
+
|
||||||
|
+ void *status = xpthread_join (th);
|
||||||
|
+ if (status != NULL)
|
||||||
|
+ printf ("test '%s' failed: %" PRIdPTR "\n", tests[i].name,
|
||||||
|
+ (intptr_t) status);
|
||||||
|
+ TEST_VERIFY (status == NULL);
|
||||||
|
+
|
||||||
|
+ xpthread_barrier_destroy (&b);
|
||||||
|
+
|
||||||
|
+ TEST_COMPARE (cl_called, 0);
|
||||||
|
+
|
||||||
|
+ printf ("in-time cancel test of '%s' successful\n", tests[i].name);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+#include <support/test-driver.c>
|
||||||
|
--
|
||||||
|
1.8.3.1
|
||||||
|
|
||||||
Loading…
x
Reference in New Issue
Block a user