| /* Test malloc with concurrent thread termination. |
| Copyright (C) 2015-2018 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 |
| <http://www.gnu.org/licenses/>. */ |
| |
| /* 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 <errno.h> |
| #include <malloc.h> |
| #include <pthread.h> |
| #include <stdbool.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| |
| #include <support/support.h> |
| #include <support/xthread.h> |
| #include <support/test-driver.h> |
| |
| 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 = xmalloc (malloc_size); |
| xpthread_barrier_wait (barrier); |
| unoptimized_free (ptr); |
| return NULL; |
| } |
| |
| static void * |
| wait_first_thread (void * closure) |
| { |
| pthread_barrier_t *barrier = closure; |
| xpthread_barrier_wait (barrier); |
| void *ptr = xmalloc (malloc_size); |
| unoptimized_free (ptr); |
| return NULL; |
| } |
| |
| static void * |
| outer_thread (void *closure) |
| { |
| pthread_t *threads = xcalloc (sizeof (*threads), inner_thread_count); |
| while (!__atomic_load_n (&termination_requested, __ATOMIC_RELAXED)) |
| { |
| pthread_barrier_t barrier; |
| xpthread_barrier_init (&barrier, NULL, inner_thread_count + 1); |
| 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; |
| threads[i] = xpthread_create (NULL, func, &barrier); |
| } |
| xpthread_barrier_wait (&barrier); |
| for (int i = 0; i < inner_thread_count; ++i) |
| xpthread_join (threads[i]); |
| xpthread_barrier_destroy (&barrier); |
| } |
| |
| free (threads); |
| |
| return NULL; |
| } |
| |
| static int |
| do_test (void) |
| { |
| /* The number of threads should be smaller than the number of |
| arenas, so that there will be some free arenas to add to the |
| arena free list. */ |
| enum { outer_thread_count = 2 }; |
| if (mallopt (M_ARENA_MAX, 8) == 0) |
| { |
| printf ("error: mallopt (M_ARENA_MAX) failed\n"); |
| return 1; |
| } |
| |
| /* Leave some room for shutting down all threads gracefully. */ |
| int timeout = 3; |
| if (timeout > DEFAULT_TIMEOUT) |
| timeout = DEFAULT_TIMEOUT - 1; |
| |
| pthread_t *threads = xcalloc (sizeof (*threads), outer_thread_count); |
| for (long i = 0; i < outer_thread_count; ++i) |
| threads[i] = xpthread_create (NULL, outer_thread, NULL); |
| |
| 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) |
| xpthread_join (threads[i]); |
| free (threads); |
| |
| return 0; |
| } |
| |
| #include <support/test-driver.c> |