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