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