1 /* Test basic thread_local support.
2 Copyright (C) 2015-2022 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 #include <errno.h>
20 #include <pthread.h>
21 #include <stdio.h>
22 #include <string.h>
23
24 #include <array>
25 #include <functional>
26 #include <string>
27 #include <thread>
28
29 struct counter
30 {
31 int constructed {};
32 int destructed {};
33
34 void reset ();
35 };
36
37 void
reset()38 counter::reset ()
39 {
40 constructed = 0;
41 destructed = 0;
42 }
43
44 static std::string
to_string(const counter & c)45 to_string (const counter &c)
46 {
47 char buf[128];
48 snprintf (buf, sizeof (buf), "%d/%d",
49 c.constructed, c.destructed);
50 return buf;
51 }
52
53 template <counter *Counter>
54 struct counting
55 {
56 counting () __attribute__ ((noinline, noclone));
57 ~counting () __attribute__ ((noinline, noclone));
58 void operation () __attribute__ ((noinline, noclone));
59 };
60
61 template<counter *Counter>
62 __attribute__ ((noinline, noclone))
counting()63 counting<Counter>::counting ()
64 {
65 ++Counter->constructed;
66 }
67
68 template<counter *Counter>
69 __attribute__ ((noinline, noclone))
~counting()70 counting<Counter>::~counting ()
71 {
72 ++Counter->destructed;
73 }
74
75 template<counter *Counter>
76 void __attribute__ ((noinline, noclone))
operation()77 counting<Counter>::operation ()
78 {
79 // Optimization barrier.
80 asm ("");
81 }
82
83 static counter counter_static;
84 static counter counter_anonymous_namespace;
85 static counter counter_extern;
86 static counter counter_function_local;
87 static bool errors (false);
88
89 static std::string
all_counters()90 all_counters ()
91 {
92 return to_string (counter_static)
93 + ' ' + to_string (counter_anonymous_namespace)
94 + ' ' + to_string (counter_extern)
95 + ' ' + to_string (counter_function_local);
96 }
97
98 static void
check_counters(const char * name,const char * expected)99 check_counters (const char *name, const char *expected)
100 {
101 std::string actual{all_counters ()};
102 if (actual != expected)
103 {
104 printf ("error: %s: (%s) != (%s)\n",
105 name, actual.c_str (), expected);
106 errors = true;
107 }
108 }
109
110 static void
reset_all()111 reset_all ()
112 {
113 counter_static.reset ();
114 counter_anonymous_namespace.reset ();
115 counter_extern.reset ();
116 counter_function_local.reset ();
117 }
118
119 static thread_local counting<&counter_static> counting_static;
120 namespace {
121 thread_local counting<&counter_anonymous_namespace>
122 counting_anonymous_namespace;
123 }
124 extern thread_local counting<&counter_extern> counting_extern;
125 thread_local counting<&counter_extern> counting_extern;
126
127 static void *
thread_without_access(void *)128 thread_without_access (void *)
129 {
130 return nullptr;
131 }
132
133 static void *
thread_with_access(void *)134 thread_with_access (void *)
135 {
136 thread_local counting<&counter_function_local> counting_function_local;
137 counting_function_local.operation ();
138 check_counters ("early in thread_with_access", "0/0 0/0 0/0 1/0");
139 counting_static.operation ();
140 counting_anonymous_namespace.operation ();
141 counting_extern.operation ();
142 check_counters ("in thread_with_access", "1/0 1/0 1/0 1/0");
143 return nullptr;
144 }
145
146 static int
do_test(void)147 do_test (void)
148 {
149 std::function<void (void *(void *))> do_pthread =
150 [](void *(func) (void *))
151 {
152 pthread_t thr;
153 int ret = pthread_create (&thr, nullptr, func, nullptr);
154 if (ret != 0)
155 {
156 errno = ret;
157 printf ("error: pthread_create: %m\n");
158 errors = true;
159 return;
160 }
161 ret = pthread_join (thr, nullptr);
162 if (ret != 0)
163 {
164 errno = ret;
165 printf ("error: pthread_join: %m\n");
166 errors = true;
167 return;
168 }
169 };
170 std::function<void (void *(void *))> do_std_thread =
171 [](void *(func) (void *))
172 {
173 std::thread thr{[func] {func (nullptr);}};
174 thr.join ();
175 };
176
177 std::array<std::pair<const char *, std::function<void (void *(void *))>>, 2>
178 do_thread_X
179 {{
180 {"pthread_create", do_pthread},
181 {"std::thread", do_std_thread},
182 }};
183
184 for (auto do_thread : do_thread_X)
185 {
186 printf ("info: testing %s\n", do_thread.first);
187 check_counters ("initial", "0/0 0/0 0/0 0/0");
188 do_thread.second (thread_without_access);
189 check_counters ("after thread_without_access", "0/0 0/0 0/0 0/0");
190 reset_all ();
191 do_thread.second (thread_with_access);
192 check_counters ("after thread_with_access", "1/1 1/1 1/1 1/1");
193 reset_all ();
194 }
195
196 return errors;
197 }
198
199 #define TEST_FUNCTION do_test ()
200 #include "../test-skeleton.c"
201