diff options
author | Adhemerval Zanella <adhemerval.zanella@linaro.org> | 2024-08-05 11:27:35 -0300 |
---|---|---|
committer | Adhemerval Zanella <adhemerval.zanella@linaro.org> | 2024-08-05 17:07:57 -0300 |
commit | c6af8a9a3ce137a9704825d173be22a2b2d9cb49 (patch) | |
tree | daa57dfeb5ae6e1d1c61b7b414400b3bb28d3546 /stdlib/tst-concurrent-exit.c | |
parent | 5097cd344fd243fb8deb6dec96e8073753f962f9 (diff) | |
download | glibc-c6af8a9a3ce137a9704825d173be22a2b2d9cb49.tar.gz glibc-c6af8a9a3ce137a9704825d173be22a2b2d9cb49.tar.xz glibc-c6af8a9a3ce137a9704825d173be22a2b2d9cb49.zip |
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.c | 141 |
1 files changed, 3 insertions, 138 deletions
diff --git a/stdlib/tst-concurrent-exit.c b/stdlib/tst-concurrent-exit.c index 1141130f87..421c39d631 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" |