1 /* Support file for atexit/exit, etc. race tests (BZ #27749).
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 /* The atexit/exit, at_quick_exit/quick_exit, __cxa_atexit/exit, etc.
20    exhibited data race while calling destructors.
21 
22    This test registers destructors from the background thread, and checks that
23    the same destructor is not called more than once.  */
24 
25 #include <stdatomic.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <support/check.h>
29 #include <support/xthread.h>
30 #include <support/xunistd.h>
31 #include <sys/wait.h>
32 #include <unistd.h>
33 
34 static atomic_int registered;
35 static atomic_int todo = 100000;
36 
37 static void
atexit_cb(void * arg)38 atexit_cb (void *arg)
39 {
40   atomic_fetch_sub (&registered, 1);
41   static void *prev;
42   if (arg == prev)
43     FAIL_EXIT1 ("%s: %p\n", __func__, arg);
44   prev = arg;
45 
46   while (atomic_load (&todo) > 0 && atomic_load (&registered) < 100)
47     ;
48 }
49 
50 int __cxa_atexit (void (*func) (void *), void *arg, void *d);
51 
52 static void *
thread_func(void * arg)53 thread_func (void *arg)
54 {
55   void *cb_arg = NULL;
56   while (atomic_load (&todo) > 0)
57     {
58       if (atomic_load (&registered) < 10000)
59         {
60           int n = 10;
61           for (int i = 0; i < n; ++i)
62             __cxa_atexit (&atexit_cb, ++cb_arg, 0);
63           atomic_fetch_add (&registered, n);
64           atomic_fetch_sub (&todo, n);
65         }
66     }
67 
68   return NULL;
69 }
70 
71 _Noreturn static void
test_and_exit(void)72 test_and_exit (void)
73 {
74   pthread_attr_t attr;
75 
76   xpthread_attr_init (&attr);
77   xpthread_attr_setdetachstate (&attr, 1);
78 
79   xpthread_create (&attr, thread_func, NULL);
80   xpthread_attr_destroy (&attr);
81 
82   while (atomic_load (&registered) == 0)
83     ;
84   exit (0);
85 }
86 
87 static int
do_test(void)88 do_test (void)
89 {
90   for (int i = 0; i < 20; ++i)
91     {
92       for (int i = 0; i < 10; ++i)
93         if (xfork () == 0)
94           test_and_exit ();
95 
96       for (int i = 0; i < 10; ++i)
97         {
98           int status;
99           xwaitpid (0, &status, 0);
100           if (!WIFEXITED (status))
101             FAIL_EXIT1 ("Failed iterations %d", i);
102           TEST_COMPARE (WEXITSTATUS (status), 0);
103         }
104     }
105 
106   return 0;
107 }
108 
109 #define TEST_FUNCTION do_test
110 #include <support/test-driver.c>
111