1 /* Test malloc with concurrent thread termination.
2    Copyright (C) 2015-2021 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4 
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9 
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, see
17    <https://www.gnu.org/licenses/>.  */
18 
19 /* This thread spawns a number of outer threads, equal to the arena
20    limit.  The outer threads run a loop which start and join two
21    different kinds of threads: the first kind allocates (attaching an
22    arena to the thread; malloc_first_thread) and waits, the second
23    kind waits and allocates (wait_first_threads).  Both kinds of
24    threads exit immediately after waiting.  The hope is that this will
25    exhibit races in thread termination and arena management,
26    particularly related to the arena free list.  */
27 
28 #include <errno.h>
29 #include <malloc.h>
30 #include <pthread.h>
31 #include <stdbool.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 
36 #include <support/support.h>
37 #include <support/xthread.h>
38 #include <support/test-driver.h>
39 
40 static bool termination_requested;
41 static int inner_thread_count = 4;
42 static size_t malloc_size = 32;
43 
44 static void
45 __attribute__ ((noinline, noclone))
unoptimized_free(void * ptr)46 unoptimized_free (void *ptr)
47 {
48   free (ptr);
49 }
50 
51 static void *
malloc_first_thread(void * closure)52 malloc_first_thread (void * closure)
53 {
54   pthread_barrier_t *barrier = closure;
55   void *ptr = xmalloc (malloc_size);
56   xpthread_barrier_wait (barrier);
57   unoptimized_free (ptr);
58   return NULL;
59 }
60 
61 static void *
wait_first_thread(void * closure)62 wait_first_thread (void * closure)
63 {
64   pthread_barrier_t *barrier = closure;
65   xpthread_barrier_wait (barrier);
66   void *ptr = xmalloc (malloc_size);
67   unoptimized_free (ptr);
68   return NULL;
69 }
70 
71 static void *
outer_thread(void * closure)72 outer_thread (void *closure)
73 {
74   pthread_t *threads = xcalloc (sizeof (*threads), inner_thread_count);
75   while (!__atomic_load_n (&termination_requested, __ATOMIC_RELAXED))
76     {
77       pthread_barrier_t barrier;
78       xpthread_barrier_init (&barrier, NULL, inner_thread_count + 1);
79       for (int i = 0; i < inner_thread_count; ++i)
80         {
81           void *(*func) (void *);
82           if ((i  % 2) == 0)
83             func = malloc_first_thread;
84           else
85             func = wait_first_thread;
86           threads[i] = xpthread_create (NULL, func, &barrier);
87         }
88       xpthread_barrier_wait (&barrier);
89       for (int i = 0; i < inner_thread_count; ++i)
90         xpthread_join (threads[i]);
91       xpthread_barrier_destroy (&barrier);
92     }
93 
94   free (threads);
95 
96   return NULL;
97 }
98 
99 static int
do_test(void)100 do_test (void)
101 {
102   /* The number of threads should be smaller than the number of
103      arenas, so that there will be some free arenas to add to the
104      arena free list.  */
105   enum { outer_thread_count = 2 };
106   if (mallopt (M_ARENA_MAX, 8) == 0)
107     {
108       printf ("error: mallopt (M_ARENA_MAX) failed\n");
109       return 1;
110     }
111 
112   /* Leave some room for shutting down all threads gracefully.  */
113   int timeout = 3;
114   if (timeout > DEFAULT_TIMEOUT)
115     timeout = DEFAULT_TIMEOUT - 1;
116 
117   pthread_t *threads = xcalloc (sizeof (*threads), outer_thread_count);
118   for (long i = 0; i < outer_thread_count; ++i)
119     threads[i] = xpthread_create (NULL, outer_thread, NULL);
120 
121   struct timespec ts = {timeout, 0};
122   if (nanosleep (&ts, NULL))
123     {
124       printf ("error: error: nanosleep: %m\n");
125       abort ();
126     }
127 
128   __atomic_store_n (&termination_requested, true, __ATOMIC_RELAXED);
129 
130   for (long i = 0; i < outer_thread_count; ++i)
131     xpthread_join (threads[i]);
132   free (threads);
133 
134   return 0;
135 }
136 
137 #include <support/test-driver.c>
138