/* Test malloc with concurrent thread termination. Copyright (C) 2015-2016 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, see . */ /* This thread spawns a number of outer threads, equal to the arena limit. The outer threads run a loop which start and join two different kinds of threads: the first kind allocates (attaching an arena to the thread; malloc_first_thread) and waits, the second kind waits and allocates (wait_first_threads). Both kinds of threads exit immediately after waiting. The hope is that this will exhibit races in thread termination and arena management, particularly related to the arena free list. */ #include #include #include #include #include #include #define TIMEOUT 7 static bool termination_requested; static int inner_thread_count = 4; static size_t malloc_size = 32; static void __attribute__ ((noinline, noclone)) unoptimized_free (void *ptr) { free (ptr); } static void * malloc_first_thread (void * closure) { pthread_barrier_t *barrier = closure; void *ptr = malloc (malloc_size); if (ptr == NULL) { printf ("error: malloc: %m\n"); abort (); } int ret = pthread_barrier_wait (barrier); if (ret != 0 && ret != PTHREAD_BARRIER_SERIAL_THREAD) { errno = ret; printf ("error: pthread_barrier_wait: %m\n"); abort (); } unoptimized_free (ptr); return NULL; } static void * wait_first_thread (void * closure) { pthread_barrier_t *barrier = closure; int ret = pthread_barrier_wait (barrier); if (ret != 0 && ret != PTHREAD_BARRIER_SERIAL_THREAD) { errno = ret; printf ("error: pthread_barrier_wait: %m\n"); abort (); } void *ptr = malloc (malloc_size); if (ptr == NULL) { printf ("error: malloc: %m\n"); abort (); } unoptimized_free (ptr); return NULL; } static void * outer_thread (void *closure) { pthread_t *threads = calloc (sizeof (*threads), inner_thread_count); if (threads == NULL) { printf ("error: calloc: %m\n"); abort (); } while (!__atomic_load_n (&termination_requested, __ATOMIC_RELAXED)) { pthread_barrier_t barrier; int ret = pthread_barrier_init (&barrier, NULL, inner_thread_count + 1); if (ret != 0) { errno = ret; printf ("pthread_barrier_init: %m\n"); abort (); } for (int i = 0; i < inner_thread_count; ++i) { void *(*func) (void *); if ((i % 2) == 0) func = malloc_first_thread; else func = wait_first_thread; ret = pthread_create (threads + i, NULL, func, &barrier); if (ret != 0) { errno = ret; printf ("error: pthread_create: %m\n"); abort (); } } ret = pthread_barrier_wait (&barrier); if (ret != 0 && ret != PTHREAD_BARRIER_SERIAL_THREAD) { errno = ret; printf ("pthread_wait: %m\n"); abort (); } for (int i = 0; i < inner_thread_count; ++i) { ret = pthread_join (threads[i], NULL); if (ret != 0) { ret = errno; printf ("error: pthread_join: %m\n"); abort (); } } ret = pthread_barrier_destroy (&barrier); if (ret != 0) { ret = errno; printf ("pthread_barrier_destroy: %m\n"); abort (); } } free (threads); return NULL; } static int do_test (void) { /* The number of top-level threads should be equal to the number of arenas. See arena_get2. */ long outer_thread_count = sysconf (_SC_NPROCESSORS_ONLN); if (outer_thread_count >= 1) { /* See NARENAS_FROM_NCORES in malloc.c. */ if (sizeof (long) == 4) outer_thread_count *= 2; else outer_thread_count *= 8; } /* Leave some room for shutting down all threads gracefully. */ int timeout = TIMEOUT - 2; pthread_t *threads = calloc (sizeof (*threads), outer_thread_count); if (threads == NULL) { printf ("error: calloc: %m\n"); abort (); } for (long i = 0; i < outer_thread_count; ++i) { int ret = pthread_create (threads + i, NULL, outer_thread, NULL); if (ret != 0) { errno = ret; printf ("error: pthread_create: %m\n"); abort (); } } struct timespec ts = {timeout, 0}; if (nanosleep (&ts, NULL)) { printf ("error: error: nanosleep: %m\n"); abort (); } __atomic_store_n (&termination_requested, true, __ATOMIC_RELAXED); for (long i = 0; i < outer_thread_count; ++i) { int ret = pthread_join (threads[i], NULL); if (ret != 0) { errno = ret; printf ("error: pthread_join: %m\n"); abort (); } } free (threads); return 0; } #define TEST_FUNCTION do_test () #include "../test-skeleton.c"