From c6af8a9a3ce137a9704825d173be22a2b2d9cb49 Mon Sep 17 00:00:00 2001 From: Adhemerval Zanella Date: Mon, 5 Aug 2024 11:27:35 -0300 Subject: 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 --- stdlib/tst-concurrent-exit.c | 141 +------------------------------------------ 1 file changed, 3 insertions(+), 138 deletions(-) (limited to 'stdlib/tst-concurrent-exit.c') 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 . */ -#include -#include -#include -#include -#include -#include -#include +#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 +#include "tst-concurrent-exit-skeleton.c" -- cgit v1.1