aboutsummaryrefslogtreecommitdiff
path: root/stdlib/tst-concurrent-exit.c
diff options
context:
space:
mode:
authorAdhemerval Zanella <adhemerval.zanella@linaro.org>2024-08-05 11:27:35 -0300
committerAdhemerval Zanella <adhemerval.zanella@linaro.org>2024-08-05 17:07:57 -0300
commitc6af8a9a3ce137a9704825d173be22a2b2d9cb49 (patch)
treedaa57dfeb5ae6e1d1c61b7b414400b3bb28d3546 /stdlib/tst-concurrent-exit.c
parent5097cd344fd243fb8deb6dec96e8073753f962f9 (diff)
downloadglibc-c6af8a9a3ce137a9704825d173be22a2b2d9cb49.zip
glibc-c6af8a9a3ce137a9704825d173be22a2b2d9cb49.tar.gz
glibc-c6af8a9a3ce137a9704825d173be22a2b2d9cb49.tar.bz2
stdlib: Allow concurrent quick_exit (BZ 31997)
As for exit, also allows concurrent quick_exit to avoid race conditions when it is called concurrently. Since it uses the same internal function as exit, the __exit_lock lock is moved to __run_exit_handlers. It also solved a potential concurrent when calling exit and quick_exit concurrently. The test case 'expected' is expanded to a value larger than the minimum required by C/POSIX (32 entries) so at_quick_exit() will require libc to allocate a new block. This makes the test mre likely to trigger concurrent issues (through free() at __run_exit_handlers) if quick_exit() interacts with the at_quick_exit list concurrently. This is also the latest interpretation of the Austin Ticket [1]. Checked on x86_64-linux-gnu. [1] https://austingroupbugs.net/view.php?id=1845 Reviewed-by: Carlos O'Donell <carlos@redhat.com>
Diffstat (limited to 'stdlib/tst-concurrent-exit.c')
-rw-r--r--stdlib/tst-concurrent-exit.c141
1 files changed, 3 insertions, 138 deletions
diff --git a/stdlib/tst-concurrent-exit.c b/stdlib/tst-concurrent-exit.c
index 1141130..421c39d 100644
--- a/stdlib/tst-concurrent-exit.c
+++ b/stdlib/tst-concurrent-exit.c
@@ -16,142 +16,7 @@
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
-#include <array_length.h>
-#include <stdlib.h>
-#include <support/check.h>
-#include <support/xthread.h>
-#include <stdio.h>
-#include <support/xunistd.h>
-#include <string.h>
+#define EXIT(__r) exit (__r)
+#define ATEXIT(__f) atexit (__f)
-#define MAX_atexit 32
-
-static pthread_barrier_t barrier;
-
-static void *
-tf (void *closure)
-{
- xpthread_barrier_wait (&barrier);
- exit (0);
-
- return NULL;
-}
-
-static const char expected[] = "00000000000000000000000003021121130211";
-static char crumbs[sizeof (expected)];
-static int next_slot = 0;
-
-static void
-exit_with_flush (int code)
-{
- fflush (stdout);
- /* glibc allows recursive exit, the atexit handlers execution will be
- resumed from the where the previous exit was interrupted. */
- exit (code);
-}
-
-/* Take some time, so another thread potentially issue exit. */
-#define SETUP_NANOSLEEP \
- if (nanosleep (&(struct timespec) { .tv_sec = 0, .tv_nsec = 1000L }, \
- NULL) != 0) \
- FAIL_EXIT1 ("nanosleep: %m")
-
-static void
-fn0 (void)
-{
- crumbs[next_slot++] = '0';
- SETUP_NANOSLEEP;
-}
-
-static void
-fn1 (void)
-{
- crumbs[next_slot++] = '1';
- SETUP_NANOSLEEP;
-}
-
-static void
-fn2 (void)
-{
- crumbs[next_slot++] = '2';
- atexit (fn1);
- SETUP_NANOSLEEP;
-}
-
-static void
-fn3 (void)
-{
- crumbs[next_slot++] = '3';
- atexit (fn2);
- atexit (fn0);
- SETUP_NANOSLEEP;
-}
-
-static void
-fn_final (void)
-{
- TEST_COMPARE_STRING (crumbs, expected);
- exit_with_flush (0);
-}
-
-_Noreturn static void
-child (void)
-{
- enum { nthreads = 8 };
-
- xpthread_barrier_init (&barrier, NULL, nthreads + 1);
-
- pthread_t thr[nthreads];
- for (int i = 0; i < nthreads; i++)
- thr[i] = xpthread_create (NULL, tf, NULL);
-
- xpthread_barrier_wait (&barrier);
-
- for (int i = 0; i < nthreads; i++)
- {
- pthread_join (thr[i], NULL);
- /* It should not be reached, it means that thread did not exit for
- some reason. */
- support_record_failure ();
- }
-
- exit (2);
-}
-
-static int
-do_test (void)
-{
- /* Register a large number of handler that will trigger a heap allocation
- for the handle state. On exit, each block will be freed after the
- handle is processed. */
- int slots_remaining = MAX_atexit;
-
- /* Register this first so it can verify expected order of the rest. */
- atexit (fn_final); --slots_remaining;
-
- TEST_VERIFY_EXIT (atexit (fn1) == 0); --slots_remaining;
- TEST_VERIFY_EXIT (atexit (fn3) == 0); --slots_remaining;
- TEST_VERIFY_EXIT (atexit (fn1) == 0); --slots_remaining;
- TEST_VERIFY_EXIT (atexit (fn2) == 0); --slots_remaining;
- TEST_VERIFY_EXIT (atexit (fn1) == 0); --slots_remaining;
- TEST_VERIFY_EXIT (atexit (fn3) == 0); --slots_remaining;
-
- while (slots_remaining > 0)
- {
- TEST_VERIFY_EXIT (atexit (fn0) == 0); --slots_remaining;
- }
-
- pid_t pid = xfork ();
- if (pid != 0)
- {
- int status;
- xwaitpid (pid, &status, 0);
- TEST_VERIFY (WIFEXITED (status));
- }
- else
- child ();
-
- return 0;
-}
-
-#include <support/test-driver.c>
+#include "tst-concurrent-exit-skeleton.c"