[PATCH 6/6] nptl: Make pthread_{clock, timed}join{_np} act on all cancellation (BZ 33717)
Adhemerval Zanella
adhemerval.zanella@linaro.org
Thu Dec 11 20:47:22 GMT 2025
The pthread_join/pthread_timedjoin_np/pthread_clockjoin_np will not act
on cancellation if 1. some other thread is already waiting on the 'joinid'
or 2. If the thread has already exited.
On nptl/pthread_join_common.c, the 1. is due to the CAS doing an early
return:
80 else if (__glibc_unlikely (atomic_compare_exchange_weak_acquire (&pd->joinid,
81 &self,
82 NULL)))
83 /* There is already somebody waiting for the thread. */
84 return EINVAL;
And 2. is due to the pd->tid equal to 0:
99 pid_t tid;
100 while ((tid = atomic_load_acquire (&pd->tid)) != 0)
101 {
The easiest solution would be to add an __pthread_testcancel () on
__pthread_clockjoin_ex () if 'cancel' is true.
Checked on aarch64-linux-gnu, arm-linux-gnueabihf, x86_64-linux-gnu,
and i686-linux-gnu.
---
nptl/Makefile | 1 +
nptl/pthread_join_common.c | 3 ++
nptl/tst-cancel34.c | 107 +++++++++++++++++++++++++++++++++++++
3 files changed, 111 insertions(+)
create mode 100644 nptl/tst-cancel34.c
diff --git a/nptl/Makefile b/nptl/Makefile
index 963b32123c..c7f2c24339 100644
--- a/nptl/Makefile
+++ b/nptl/Makefile
@@ -278,6 +278,7 @@ tests = \
tst-cancel24 \
tst-cancel31 \
tst-cancel33 \
+ tst-cancel34 \
tst-cleanup5 \
tst-cond26 \
tst-context1 \
diff --git a/nptl/pthread_join_common.c b/nptl/pthread_join_common.c
index 6cdb710bc9..ddf10133c1 100644
--- a/nptl/pthread_join_common.c
+++ b/nptl/pthread_join_common.c
@@ -28,6 +28,9 @@ __pthread_clockjoin_ex (pthread_t threadid, void **thread_return,
const struct __timespec64 *abstime,
bool cancel)
{
+ if (cancel)
+ __pthread_testcancel ();
+
struct pthread *pd = (struct pthread *) threadid;
/* Make sure the clock and time specified are valid. */
diff --git a/nptl/tst-cancel34.c b/nptl/tst-cancel34.c
new file mode 100644
index 0000000000..ad050c1dbb
--- /dev/null
+++ b/nptl/tst-cancel34.c
@@ -0,0 +1,107 @@
+/* Check if pthread_join acts a cancellation entrypoiny (BZ 33717)
+ Copyright (C) 2025 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 <support/check.h>
+#include <support/xunistd.h>
+#include <support/xthread.h>
+#include <support/process_state.h>
+
+static pthread_barrier_t b1;
+static pthread_barrier_t b2;
+
+static void *
+thr_func2 (void *arg)
+{
+ xpthread_barrier_wait (&b2);
+ return NULL;
+}
+
+static void *
+thr_func1 (void *arg)
+{
+ int (*join_func)(pthread_t, void **) = arg;
+
+ xpthread_barrier_init (&b2, NULL, 2);
+
+ pthread_t thr = xpthread_create (NULL, thr_func2, NULL);
+
+ pid_t tid = pthread_gettid_np (thr);
+ /* Synchronize the inner thread so the pthread_gettid_np returns a valid
+ TID. */
+ xpthread_barrier_wait (&b2);
+
+ support_thread_state_wait (tid, support_process_state_dead);
+
+ /* Synchronize with parent thread so the cancellation is issues after
+ the inner thread has finished. */
+ xpthread_barrier_wait (&b1);
+
+ /* Wait for the cancellation. */
+ xpthread_barrier_wait (&b1);
+
+ join_func (thr, NULL);
+
+ support_record_failure ();
+
+ return NULL;
+}
+
+static void
+do_test_common (int (*join_func)(pthread_t, void **))
+{
+ xpthread_barrier_init (&b1, NULL, 2);
+
+ pthread_t thr = xpthread_create (NULL, thr_func1, join_func);
+
+ /* Wait until the inner thread is created and terminated
+ (support_process_state_dead) before issuing the pthread_cancel. */
+ xpthread_barrier_wait (&b1);
+
+ xpthread_cancel (thr);
+
+ /* The deferred cancellation is pending on the thread, issue the
+ routine to check. */
+ xpthread_barrier_wait (&b1);
+
+ TEST_VERIFY (xpthread_join (thr) == PTHREAD_CANCELED);
+}
+
+static int
+pthread_timedjoin_np_wrapper (pthread_t thr, void **retval)
+{
+ return pthread_timedjoin_np (thr, retval, NULL);
+}
+
+static int
+pthread_clockjoin_np_wrapper (pthread_t thr, void **retval)
+{
+ return pthread_clockjoin_np (thr, retval, CLOCK_REALTIME, NULL);
+}
+
+static int
+do_test (void)
+{
+ do_test_common (pthread_join);
+ do_test_common (pthread_timedjoin_np_wrapper);
+ do_test_common (pthread_clockjoin_np_wrapper);
+
+ return 0;
+}
+
+#define TIMEOUT 3
+#include <support/test-driver.c>
--
2.43.0
More information about the Libc-alpha
mailing list