]> git.ipfire.org Git - thirdparty/glibc.git/blame - malloc/tst-malloc-thread-exit.c
Update copyright dates with scripts/update-copyrights.
[thirdparty/glibc.git] / malloc / tst-malloc-thread-exit.c
CommitLineData
3da825ce 1/* Test malloc with concurrent thread termination.
f7a9f785 2 Copyright (C) 2015-2016 Free Software Foundation, Inc.
3da825ce
FW
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 <http://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 <pthread.h>
30#include <stdbool.h>
31#include <stdio.h>
32#include <stdlib.h>
33#include <unistd.h>
34
35#define TIMEOUT 7
36
37static bool termination_requested;
38static int inner_thread_count = 4;
39static size_t malloc_size = 32;
40
41static void
42__attribute__ ((noinline, noclone))
43unoptimized_free (void *ptr)
44{
45 free (ptr);
46}
47
48static void *
49malloc_first_thread (void * closure)
50{
51 pthread_barrier_t *barrier = closure;
52 void *ptr = malloc (malloc_size);
53 if (ptr == NULL)
54 {
55 printf ("error: malloc: %m\n");
56 abort ();
57 }
58 int ret = pthread_barrier_wait (barrier);
59 if (ret != 0 && ret != PTHREAD_BARRIER_SERIAL_THREAD)
60 {
61 errno = ret;
62 printf ("error: pthread_barrier_wait: %m\n");
63 abort ();
64 }
65 unoptimized_free (ptr);
66 return NULL;
67}
68
69static void *
70wait_first_thread (void * closure)
71{
72 pthread_barrier_t *barrier = closure;
73 int ret = pthread_barrier_wait (barrier);
74 if (ret != 0 && ret != PTHREAD_BARRIER_SERIAL_THREAD)
75 {
76 errno = ret;
77 printf ("error: pthread_barrier_wait: %m\n");
78 abort ();
79 }
80 void *ptr = malloc (malloc_size);
81 if (ptr == NULL)
82 {
83 printf ("error: malloc: %m\n");
84 abort ();
85 }
86 unoptimized_free (ptr);
87 return NULL;
88}
89
90static void *
91outer_thread (void *closure)
92{
93 pthread_t *threads = calloc (sizeof (*threads), inner_thread_count);
94 if (threads == NULL)
95 {
96 printf ("error: calloc: %m\n");
97 abort ();
98 }
99
100 while (!__atomic_load_n (&termination_requested, __ATOMIC_RELAXED))
101 {
102 pthread_barrier_t barrier;
103 int ret = pthread_barrier_init (&barrier, NULL, inner_thread_count + 1);
104 if (ret != 0)
105 {
106 errno = ret;
107 printf ("pthread_barrier_init: %m\n");
108 abort ();
109 }
110 for (int i = 0; i < inner_thread_count; ++i)
111 {
112 void *(*func) (void *);
113 if ((i % 2) == 0)
114 func = malloc_first_thread;
115 else
116 func = wait_first_thread;
117 ret = pthread_create (threads + i, NULL, func, &barrier);
118 if (ret != 0)
119 {
120 errno = ret;
121 printf ("error: pthread_create: %m\n");
122 abort ();
123 }
124 }
125 ret = pthread_barrier_wait (&barrier);
126 if (ret != 0 && ret != PTHREAD_BARRIER_SERIAL_THREAD)
127 {
128 errno = ret;
129 printf ("pthread_wait: %m\n");
130 abort ();
131 }
132 for (int i = 0; i < inner_thread_count; ++i)
133 {
134 ret = pthread_join (threads[i], NULL);
135 if (ret != 0)
136 {
137 ret = errno;
138 printf ("error: pthread_join: %m\n");
139 abort ();
140 }
141 }
142 ret = pthread_barrier_destroy (&barrier);
143 if (ret != 0)
144 {
145 ret = errno;
146 printf ("pthread_barrier_destroy: %m\n");
147 abort ();
148 }
149 }
150
151 free (threads);
152
153 return NULL;
154}
155
156static int
157do_test (void)
158{
159 /* The number of top-level threads should be equal to the number of
160 arenas. See arena_get2. */
161 long outer_thread_count = sysconf (_SC_NPROCESSORS_ONLN);
162 if (outer_thread_count >= 1)
163 {
164 /* See NARENAS_FROM_NCORES in malloc.c. */
165 if (sizeof (long) == 4)
166 outer_thread_count *= 2;
167 else
168 outer_thread_count *= 8;
169 }
170
171 /* Leave some room for shutting down all threads gracefully. */
172 int timeout = TIMEOUT - 2;
173
174 pthread_t *threads = calloc (sizeof (*threads), outer_thread_count);
175 if (threads == NULL)
176 {
177 printf ("error: calloc: %m\n");
178 abort ();
179 }
180
181 for (long i = 0; i < outer_thread_count; ++i)
182 {
183 int ret = pthread_create (threads + i, NULL, outer_thread, NULL);
184 if (ret != 0)
185 {
186 errno = ret;
187 printf ("error: pthread_create: %m\n");
188 abort ();
189 }
190 }
191
192 struct timespec ts = {timeout, 0};
193 if (nanosleep (&ts, NULL))
194 {
195 printf ("error: error: nanosleep: %m\n");
196 abort ();
197 }
198
199 __atomic_store_n (&termination_requested, true, __ATOMIC_RELAXED);
200
201 for (long i = 0; i < outer_thread_count; ++i)
202 {
203 int ret = pthread_join (threads[i], NULL);
204 if (ret != 0)
205 {
206 errno = ret;
207 printf ("error: pthread_join: %m\n");
208 abort ();
209 }
210 }
211 free (threads);
212
213 return 0;
214}
215
216#define TEST_FUNCTION do_test ()
217#include "../test-skeleton.c"