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 |
38 | counter::reset () |
39 | { |
40 | constructed = 0; |
41 | destructed = 0; |
42 | } |
43 | |
44 | static std::string |
45 | to_string (const counter &c) |
46 | { |
47 | char buf[128]; |
48 | snprintf (s: buf, maxlen: sizeof (buf), format: "%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)) |
63 | counting<Counter>::counting () |
64 | { |
65 | ++Counter->constructed; |
66 | } |
67 | |
68 | template<counter *Counter> |
69 | __attribute__ ((noinline, noclone)) |
70 | counting<Counter>::~counting () |
71 | { |
72 | ++Counter->destructed; |
73 | } |
74 | |
75 | template<counter *Counter> |
76 | void __attribute__ ((noinline, noclone)) |
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 |
90 | all_counters () |
91 | { |
92 | return to_string (c: counter_static) |
93 | + ' ' + to_string (c: counter_anonymous_namespace) |
94 | + ' ' + to_string (c: counter_extern) |
95 | + ' ' + to_string (c: counter_function_local); |
96 | } |
97 | |
98 | static void |
99 | check_counters (const char *name, const char *expected) |
100 | { |
101 | std::string actual{all_counters ()}; |
102 | if (actual != expected) |
103 | { |
104 | printf (format: "error: %s: (%s) != (%s)\n" , |
105 | name, actual.c_str (), expected); |
106 | errors = true; |
107 | } |
108 | } |
109 | |
110 | static void |
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 * |
128 | thread_without_access (void *) |
129 | { |
130 | return nullptr; |
131 | } |
132 | |
133 | static 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 (name: "early in thread_with_access" , expected: "0/0 0/0 0/0 1/0" ); |
139 | counting_static.operation (); |
140 | counting_anonymous_namespace.operation (); |
141 | counting_extern.operation (); |
142 | check_counters (name: "in thread_with_access" , expected: "1/0 1/0 1/0 1/0" ); |
143 | return nullptr; |
144 | } |
145 | |
146 | static int |
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 (newthread: &thr, attr: nullptr, start_routine: func, arg: nullptr); |
154 | if (ret != 0) |
155 | { |
156 | errno = ret; |
157 | printf (format: "error: pthread_create: %m\n" ); |
158 | errors = true; |
159 | return; |
160 | } |
161 | ret = pthread_join (th: thr, thread_return: nullptr); |
162 | if (ret != 0) |
163 | { |
164 | errno = ret; |
165 | printf (format: "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 | {._M_elems: { |
180 | {"pthread_create" , do_pthread}, |
181 | {"std::thread" , do_std_thread}, |
182 | }}; |
183 | |
184 | for (auto do_thread : do_thread_X) |
185 | { |
186 | printf (format: "info: testing %s\n" , do_thread.first); |
187 | check_counters (name: "initial" , expected: "0/0 0/0 0/0 0/0" ); |
188 | do_thread.second (thread_without_access); |
189 | check_counters (name: "after thread_without_access" , expected: "0/0 0/0 0/0 0/0" ); |
190 | reset_all (); |
191 | do_thread.second (thread_with_access); |
192 | check_counters (name: "after thread_with_access" , expected: "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 | |