about summary refs log tree commit diff
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.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.c141
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"