/* Test malloc with concurrent thread creation and termination. Copyright (C) 2015 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 . */ #include #include #include #include #include #include #include static void usage (char **argv) { fprintf (stderr, "usage: %s OUTER INNER TIMEOUT\n", argv[0]); exit (2); } static _Atomic bool termination_requested; static int inner_count; 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_count); if (threads == NULL) { printf ("error: calloc: %m\n"); abort (); } while (!atomic_load_explicit (&termination_requested, memory_order_relaxed)) { pthread_barrier_t barrier; int ret = pthread_barrier_init (&barrier, NULL, inner_count + 1); if (ret != 0) { errno = ret; printf ("pthread_barrier_init: %m\n"); abort (); } for (int i = 0; i < inner_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_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; } int main (int argc, char **argv) { if (argc != 4) usage (argv); int outer_count = atoi (argv[1]); inner_count = atoi (argv[2]); int timeout = atoi (argv[3]); if ((inner_count % 2) != 0) ++inner_count; if (outer_count <= 0 || inner_count <= 0 || timeout <= 0) usage (argv); pthread_t *threads = calloc (sizeof (*threads), outer_count); if (threads == NULL) { printf ("error: calloc: %m\n"); abort (); } for (int i = 0; i < outer_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_explicit (&termination_requested, true, memory_order_relaxed); for (int i = 0; i < outer_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; }