1 /* Test that pthread_cancel succeeds during thread exit.
2    Copyright (C) 2021-2022 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4 
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9 
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, see
17    <https://www.gnu.org/licenses/>.  */
18 
19 /* This test tries to trigger an internal race condition in
20    pthread_cancel, where the cancellation signal is sent after the
21    thread has begun the cancellation process.  This can result in a
22    spurious ESRCH error.  For the original bug 12889, the window is
23    quite small, so the bug was not reproduced in every run.  */
24 
25 #include <stdbool.h>
26 #include <stddef.h>
27 #include <support/check.h>
28 #include <support/xthread.h>
29 #include <support/xunistd.h>
30 #include <sys/select.h>
31 #include <unistd.h>
32 
33 /* Set to true by timeout_thread_function when the test should
34    terminate.  */
35 static bool timeout;
36 
37 static void *
timeout_thread_function(void * unused)38 timeout_thread_function (void *unused)
39 {
40   usleep (5 * 1000 * 1000);
41   __atomic_store_n (&timeout, true, __ATOMIC_RELAXED);
42   return NULL;
43 }
44 
45 /* Used for blocking the select function below.  */
46 static int pipe_fds[2];
47 
48 static void *
canceled_thread_function(void * unused)49 canceled_thread_function (void *unused)
50 {
51   while (true)
52     {
53       fd_set rfs;
54       fd_set wfs;
55       fd_set efs;
56       FD_ZERO (&rfs);
57       FD_ZERO (&wfs);
58       FD_ZERO (&efs);
59       FD_SET (pipe_fds[0], &rfs);
60 
61       /* If the cancellation request is recognized early, the thread
62          begins exiting while the cancellation signal arrives.  */
63       select (FD_SETSIZE, &rfs, &wfs, &efs, NULL);
64     }
65   return NULL;
66 }
67 
68 static int
do_test(void)69 do_test (void)
70 {
71   xpipe (pipe_fds);
72   pthread_t thr_timeout = xpthread_create (NULL, timeout_thread_function, NULL);
73 
74   while (!__atomic_load_n (&timeout, __ATOMIC_RELAXED))
75     {
76       pthread_t thr = xpthread_create (NULL, canceled_thread_function, NULL);
77       xpthread_cancel (thr);
78       TEST_VERIFY (xpthread_join (thr) == PTHREAD_CANCELED);
79     }
80 
81   xpthread_join (thr_timeout);
82   xclose (pipe_fds[0]);
83   xclose (pipe_fds[1]);
84   return 0;
85 }
86 
87 #include <support/test-driver.c>
88