xref: /aosp_15_r20/bionic/tests/thread_local_test.cpp (revision 8d67ca893c1523eb926b9080dbe4e2ffd2a27ba1)
1*8d67ca89SAndroid Build Coastguard Worker /*
2*8d67ca89SAndroid Build Coastguard Worker  * Copyright (C) 2015 The Android Open Source Project
3*8d67ca89SAndroid Build Coastguard Worker  *
4*8d67ca89SAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*8d67ca89SAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*8d67ca89SAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*8d67ca89SAndroid Build Coastguard Worker  *
8*8d67ca89SAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*8d67ca89SAndroid Build Coastguard Worker  *
10*8d67ca89SAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*8d67ca89SAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*8d67ca89SAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*8d67ca89SAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*8d67ca89SAndroid Build Coastguard Worker  * limitations under the License.
15*8d67ca89SAndroid Build Coastguard Worker  */
16*8d67ca89SAndroid Build Coastguard Worker 
17*8d67ca89SAndroid Build Coastguard Worker #include <gtest/gtest.h>
18*8d67ca89SAndroid Build Coastguard Worker #include <stdint.h>
19*8d67ca89SAndroid Build Coastguard Worker #include <string.h>
20*8d67ca89SAndroid Build Coastguard Worker 
21*8d67ca89SAndroid Build Coastguard Worker __thread int local_var = 100;
22*8d67ca89SAndroid Build Coastguard Worker int shared_var = 200;
23*8d67ca89SAndroid Build Coastguard Worker 
reset_vars()24*8d67ca89SAndroid Build Coastguard Worker static void reset_vars() {
25*8d67ca89SAndroid Build Coastguard Worker   local_var = 1000;
26*8d67ca89SAndroid Build Coastguard Worker   shared_var = 2000;
27*8d67ca89SAndroid Build Coastguard Worker   // local_var should be reset by threads
28*8d67ca89SAndroid Build Coastguard Worker }
29*8d67ca89SAndroid Build Coastguard Worker 
30*8d67ca89SAndroid Build Coastguard Worker typedef void* (*MyThread)(void*);
31*8d67ca89SAndroid Build Coastguard Worker 
inc_shared_var(void * p)32*8d67ca89SAndroid Build Coastguard Worker static void* inc_shared_var(void* p) {
33*8d67ca89SAndroid Build Coastguard Worker   int *data = reinterpret_cast<int*>(p);
34*8d67ca89SAndroid Build Coastguard Worker   shared_var++;
35*8d67ca89SAndroid Build Coastguard Worker   *data = shared_var;
36*8d67ca89SAndroid Build Coastguard Worker   return nullptr;
37*8d67ca89SAndroid Build Coastguard Worker }
38*8d67ca89SAndroid Build Coastguard Worker 
inc_local_var(void * p)39*8d67ca89SAndroid Build Coastguard Worker static void* inc_local_var(void* p) {
40*8d67ca89SAndroid Build Coastguard Worker   int *data = reinterpret_cast<int*>(p);
41*8d67ca89SAndroid Build Coastguard Worker   local_var++;
42*8d67ca89SAndroid Build Coastguard Worker   *data = local_var;
43*8d67ca89SAndroid Build Coastguard Worker   return nullptr;
44*8d67ca89SAndroid Build Coastguard Worker }
45*8d67ca89SAndroid Build Coastguard Worker 
run_one_thread(MyThread foo)46*8d67ca89SAndroid Build Coastguard Worker static int run_one_thread(MyThread foo) {
47*8d67ca89SAndroid Build Coastguard Worker   pthread_t t;
48*8d67ca89SAndroid Build Coastguard Worker   int data;
49*8d67ca89SAndroid Build Coastguard Worker   int error = pthread_create(&t, nullptr, foo, &data);
50*8d67ca89SAndroid Build Coastguard Worker   if (!error)
51*8d67ca89SAndroid Build Coastguard Worker       error = pthread_join(t, nullptr);
52*8d67ca89SAndroid Build Coastguard Worker   return error ? error : data;
53*8d67ca89SAndroid Build Coastguard Worker }
54*8d67ca89SAndroid Build Coastguard Worker 
TEST(thread_local_storage,shared)55*8d67ca89SAndroid Build Coastguard Worker TEST(thread_local_storage, shared) {
56*8d67ca89SAndroid Build Coastguard Worker   reset_vars();
57*8d67ca89SAndroid Build Coastguard Worker   ASSERT_EQ(local_var, 1000);
58*8d67ca89SAndroid Build Coastguard Worker   ASSERT_EQ(shared_var, 2000);
59*8d67ca89SAndroid Build Coastguard Worker 
60*8d67ca89SAndroid Build Coastguard Worker   // Update shared_var, local_var remains 1000.
61*8d67ca89SAndroid Build Coastguard Worker   ASSERT_EQ(run_one_thread(inc_shared_var), 2001);
62*8d67ca89SAndroid Build Coastguard Worker   ASSERT_EQ(local_var, 1000);
63*8d67ca89SAndroid Build Coastguard Worker   ASSERT_EQ(shared_var, 2001);
64*8d67ca89SAndroid Build Coastguard Worker 
65*8d67ca89SAndroid Build Coastguard Worker   ASSERT_EQ(run_one_thread(inc_shared_var), 2002);
66*8d67ca89SAndroid Build Coastguard Worker   ASSERT_EQ(local_var, 1000);
67*8d67ca89SAndroid Build Coastguard Worker   ASSERT_EQ(shared_var, 2002);
68*8d67ca89SAndroid Build Coastguard Worker 
69*8d67ca89SAndroid Build Coastguard Worker   ASSERT_EQ(run_one_thread(inc_shared_var), 2003);
70*8d67ca89SAndroid Build Coastguard Worker   ASSERT_EQ(local_var, 1000);
71*8d67ca89SAndroid Build Coastguard Worker   ASSERT_EQ(shared_var, 2003);
72*8d67ca89SAndroid Build Coastguard Worker }
73*8d67ca89SAndroid Build Coastguard Worker 
TEST(thread_local_storage,local)74*8d67ca89SAndroid Build Coastguard Worker TEST(thread_local_storage, local) {
75*8d67ca89SAndroid Build Coastguard Worker   reset_vars();
76*8d67ca89SAndroid Build Coastguard Worker   ASSERT_EQ(local_var, 1000);
77*8d67ca89SAndroid Build Coastguard Worker   ASSERT_EQ(shared_var, 2000);
78*8d67ca89SAndroid Build Coastguard Worker 
79*8d67ca89SAndroid Build Coastguard Worker   // When a child thread updates its own TLS variable,
80*8d67ca89SAndroid Build Coastguard Worker   // this thread's local_var and shared_var are not changed.
81*8d67ca89SAndroid Build Coastguard Worker   // TLS local_var is initialized to 100 in a thread.
82*8d67ca89SAndroid Build Coastguard Worker   ASSERT_EQ(run_one_thread(inc_local_var), 101);
83*8d67ca89SAndroid Build Coastguard Worker   ASSERT_EQ(local_var, 1000);
84*8d67ca89SAndroid Build Coastguard Worker   ASSERT_EQ(shared_var, 2000);
85*8d67ca89SAndroid Build Coastguard Worker 
86*8d67ca89SAndroid Build Coastguard Worker   ASSERT_EQ(run_one_thread(inc_local_var), 101);
87*8d67ca89SAndroid Build Coastguard Worker   ASSERT_EQ(local_var, 1000);
88*8d67ca89SAndroid Build Coastguard Worker   ASSERT_EQ(shared_var, 2000);
89*8d67ca89SAndroid Build Coastguard Worker 
90*8d67ca89SAndroid Build Coastguard Worker   ASSERT_EQ(run_one_thread(inc_local_var), 101);
91*8d67ca89SAndroid Build Coastguard Worker   ASSERT_EQ(local_var, 1000);
92*8d67ca89SAndroid Build Coastguard Worker   ASSERT_EQ(shared_var, 2000);
93*8d67ca89SAndroid Build Coastguard Worker }
94*8d67ca89SAndroid Build Coastguard Worker 
95*8d67ca89SAndroid Build Coastguard Worker // Test TLS initialization of more complicated type, array of struct.
96*8d67ca89SAndroid Build Coastguard Worker struct Point {
97*8d67ca89SAndroid Build Coastguard Worker   int x, y;
98*8d67ca89SAndroid Build Coastguard Worker };
99*8d67ca89SAndroid Build Coastguard Worker 
100*8d67ca89SAndroid Build Coastguard Worker typedef Point Triangle[3];
101*8d67ca89SAndroid Build Coastguard Worker 
102*8d67ca89SAndroid Build Coastguard Worker __thread Triangle local_triangle = {{10,10}, {20,20}, {30,30}};
103*8d67ca89SAndroid Build Coastguard Worker Triangle shared_triangle = {{1,1}, {2,2}, {3,3}};
104*8d67ca89SAndroid Build Coastguard Worker 
reset_triangle()105*8d67ca89SAndroid Build Coastguard Worker static void reset_triangle() {
106*8d67ca89SAndroid Build Coastguard Worker   static const Triangle t1 = {{3,3}, {4,4}, {5,5}};
107*8d67ca89SAndroid Build Coastguard Worker   static const Triangle t2 = {{2,2}, {3,3}, {4,4}};
108*8d67ca89SAndroid Build Coastguard Worker   memcpy(local_triangle, t1, sizeof(local_triangle));
109*8d67ca89SAndroid Build Coastguard Worker   memcpy(shared_triangle, t2, sizeof(shared_triangle));
110*8d67ca89SAndroid Build Coastguard Worker }
111*8d67ca89SAndroid Build Coastguard Worker 
move_shared_triangle(void * p)112*8d67ca89SAndroid Build Coastguard Worker static void* move_shared_triangle(void* p) {
113*8d67ca89SAndroid Build Coastguard Worker   int *data = reinterpret_cast<int*>(p);
114*8d67ca89SAndroid Build Coastguard Worker   shared_triangle[1].y++;
115*8d67ca89SAndroid Build Coastguard Worker   *data = shared_triangle[1].y;
116*8d67ca89SAndroid Build Coastguard Worker   return nullptr;
117*8d67ca89SAndroid Build Coastguard Worker }
118*8d67ca89SAndroid Build Coastguard Worker 
move_local_triangle(void * p)119*8d67ca89SAndroid Build Coastguard Worker static void* move_local_triangle(void* p) {
120*8d67ca89SAndroid Build Coastguard Worker   int *data = reinterpret_cast<int*>(p);
121*8d67ca89SAndroid Build Coastguard Worker   local_triangle[1].y++;
122*8d67ca89SAndroid Build Coastguard Worker   *data = local_triangle[1].y;
123*8d67ca89SAndroid Build Coastguard Worker   return nullptr;
124*8d67ca89SAndroid Build Coastguard Worker }
125*8d67ca89SAndroid Build Coastguard Worker 
TEST(thread_local_storage,shared_triangle)126*8d67ca89SAndroid Build Coastguard Worker TEST(thread_local_storage, shared_triangle) {
127*8d67ca89SAndroid Build Coastguard Worker   reset_triangle();
128*8d67ca89SAndroid Build Coastguard Worker   ASSERT_EQ(local_triangle[1].y, 4);
129*8d67ca89SAndroid Build Coastguard Worker   ASSERT_EQ(shared_triangle[1].y, 3);
130*8d67ca89SAndroid Build Coastguard Worker 
131*8d67ca89SAndroid Build Coastguard Worker   // Update shared_triangle, local_triangle remains 1000.
132*8d67ca89SAndroid Build Coastguard Worker   ASSERT_EQ(run_one_thread(move_shared_triangle), 4);
133*8d67ca89SAndroid Build Coastguard Worker   ASSERT_EQ(local_triangle[1].y, 4);
134*8d67ca89SAndroid Build Coastguard Worker   ASSERT_EQ(shared_triangle[1].y, 4);
135*8d67ca89SAndroid Build Coastguard Worker 
136*8d67ca89SAndroid Build Coastguard Worker   ASSERT_EQ(run_one_thread(move_shared_triangle), 5);
137*8d67ca89SAndroid Build Coastguard Worker   ASSERT_EQ(local_triangle[1].y, 4);
138*8d67ca89SAndroid Build Coastguard Worker   ASSERT_EQ(shared_triangle[1].y, 5);
139*8d67ca89SAndroid Build Coastguard Worker 
140*8d67ca89SAndroid Build Coastguard Worker   ASSERT_EQ(run_one_thread(move_shared_triangle), 6);
141*8d67ca89SAndroid Build Coastguard Worker   ASSERT_EQ(local_triangle[1].y, 4);
142*8d67ca89SAndroid Build Coastguard Worker   ASSERT_EQ(shared_triangle[1].y, 6);
143*8d67ca89SAndroid Build Coastguard Worker }
144*8d67ca89SAndroid Build Coastguard Worker 
TEST(thread_local_storage,local_triangle)145*8d67ca89SAndroid Build Coastguard Worker TEST(thread_local_storage, local_triangle) {
146*8d67ca89SAndroid Build Coastguard Worker   reset_triangle();
147*8d67ca89SAndroid Build Coastguard Worker   ASSERT_EQ(local_triangle[1].y, 4);
148*8d67ca89SAndroid Build Coastguard Worker   ASSERT_EQ(shared_triangle[1].y, 3);
149*8d67ca89SAndroid Build Coastguard Worker 
150*8d67ca89SAndroid Build Coastguard Worker   // Update local_triangle, parent thread's
151*8d67ca89SAndroid Build Coastguard Worker   // shared_triangle and local_triangle are unchanged.
152*8d67ca89SAndroid Build Coastguard Worker   ASSERT_EQ(run_one_thread(move_local_triangle), 21);
153*8d67ca89SAndroid Build Coastguard Worker   ASSERT_EQ(local_triangle[1].y, 4);
154*8d67ca89SAndroid Build Coastguard Worker   ASSERT_EQ(shared_triangle[1].y, 3);
155*8d67ca89SAndroid Build Coastguard Worker 
156*8d67ca89SAndroid Build Coastguard Worker   ASSERT_EQ(run_one_thread(move_local_triangle), 21);
157*8d67ca89SAndroid Build Coastguard Worker   ASSERT_EQ(local_triangle[1].y, 4);
158*8d67ca89SAndroid Build Coastguard Worker   ASSERT_EQ(shared_triangle[1].y, 3);
159*8d67ca89SAndroid Build Coastguard Worker 
160*8d67ca89SAndroid Build Coastguard Worker   ASSERT_EQ(run_one_thread(move_local_triangle), 21);
161*8d67ca89SAndroid Build Coastguard Worker   ASSERT_EQ(local_triangle[1].y, 4);
162*8d67ca89SAndroid Build Coastguard Worker   ASSERT_EQ(shared_triangle[1].y, 3);
163*8d67ca89SAndroid Build Coastguard Worker }
164*8d67ca89SAndroid Build Coastguard Worker 
165*8d67ca89SAndroid Build Coastguard Worker // Test emutls runtime data structures and __emutls_get_address function.
166*8d67ca89SAndroid Build Coastguard Worker typedef unsigned int gcc_word __attribute__((mode(word)));
167*8d67ca89SAndroid Build Coastguard Worker typedef unsigned int gcc_pointer __attribute__((mode(pointer)));
168*8d67ca89SAndroid Build Coastguard Worker struct gcc_emutls_object {  // for libgcc
169*8d67ca89SAndroid Build Coastguard Worker   gcc_word size;
170*8d67ca89SAndroid Build Coastguard Worker   gcc_word align;
171*8d67ca89SAndroid Build Coastguard Worker   union {
172*8d67ca89SAndroid Build Coastguard Worker     gcc_pointer offset;
173*8d67ca89SAndroid Build Coastguard Worker     void* ptr;
174*8d67ca89SAndroid Build Coastguard Worker   } loc;
175*8d67ca89SAndroid Build Coastguard Worker   void* templ;
176*8d67ca89SAndroid Build Coastguard Worker };
177*8d67ca89SAndroid Build Coastguard Worker 
178*8d67ca89SAndroid Build Coastguard Worker typedef struct __emutls_control {  // for clang/llvm
179*8d67ca89SAndroid Build Coastguard Worker   size_t size;
180*8d67ca89SAndroid Build Coastguard Worker   size_t align;
181*8d67ca89SAndroid Build Coastguard Worker   union {
182*8d67ca89SAndroid Build Coastguard Worker     uintptr_t index;
183*8d67ca89SAndroid Build Coastguard Worker     void* address;
184*8d67ca89SAndroid Build Coastguard Worker   } object;
185*8d67ca89SAndroid Build Coastguard Worker   void* value;
186*8d67ca89SAndroid Build Coastguard Worker } __emutls_control;
187*8d67ca89SAndroid Build Coastguard Worker 
TEST(thread_local_storage,type_size)188*8d67ca89SAndroid Build Coastguard Worker TEST(thread_local_storage, type_size) {
189*8d67ca89SAndroid Build Coastguard Worker   static_assert(sizeof(size_t) == sizeof(gcc_word),
190*8d67ca89SAndroid Build Coastguard Worker                 "size_t != gcc_word");
191*8d67ca89SAndroid Build Coastguard Worker   static_assert(sizeof(uintptr_t) == sizeof(gcc_pointer),
192*8d67ca89SAndroid Build Coastguard Worker                 "uintptr_t != gcc_pointer");
193*8d67ca89SAndroid Build Coastguard Worker   static_assert(sizeof(uintptr_t) == sizeof(void*),
194*8d67ca89SAndroid Build Coastguard Worker                 "sizoeof(uintptr_t) != sizeof(void*)");
195*8d67ca89SAndroid Build Coastguard Worker   static_assert(sizeof(__emutls_control) == sizeof(struct gcc_emutls_object),
196*8d67ca89SAndroid Build Coastguard Worker                 "sizeof(__emutls_control) != sizeof(struct gcc_emutls_object)");
197*8d67ca89SAndroid Build Coastguard Worker }
198*8d67ca89SAndroid Build Coastguard Worker 
199*8d67ca89SAndroid Build Coastguard Worker extern "C" void* __emutls_get_address(__emutls_control*);
200*8d67ca89SAndroid Build Coastguard Worker 
TEST(thread_local_storage,init_value)201*8d67ca89SAndroid Build Coastguard Worker TEST(thread_local_storage, init_value) {
202*8d67ca89SAndroid Build Coastguard Worker   char tls_value1[] = "123456789";
203*8d67ca89SAndroid Build Coastguard Worker   char tls_value2[] = "abcdefghi";
204*8d67ca89SAndroid Build Coastguard Worker   constexpr size_t num_saved_values = 10;
205*8d67ca89SAndroid Build Coastguard Worker   __emutls_control tls_var[num_saved_values];
206*8d67ca89SAndroid Build Coastguard Worker   size_t prev_index = 0;
207*8d67ca89SAndroid Build Coastguard Worker   void* saved_gap[num_saved_values];
208*8d67ca89SAndroid Build Coastguard Worker   void* saved_p[num_saved_values];
209*8d67ca89SAndroid Build Coastguard Worker   ASSERT_TRUE(strlen(tls_value2) <= strlen(tls_value1));
210*8d67ca89SAndroid Build Coastguard Worker   __emutls_control c =
211*8d67ca89SAndroid Build Coastguard Worker       {strlen(tls_value1) + 1, 1, {0}, tls_value1};
212*8d67ca89SAndroid Build Coastguard Worker   for (size_t n = 0; n < num_saved_values; n++) {
213*8d67ca89SAndroid Build Coastguard Worker     memcpy(&tls_var[n], &c, sizeof(c));
214*8d67ca89SAndroid Build Coastguard Worker     tls_var[n].align = (1 << n);
215*8d67ca89SAndroid Build Coastguard Worker   }
216*8d67ca89SAndroid Build Coastguard Worker   for (size_t n = 0; n < num_saved_values; n++) {
217*8d67ca89SAndroid Build Coastguard Worker     // Try to mess up malloc space so that the next malloc will not have the
218*8d67ca89SAndroid Build Coastguard Worker     // required alignment, but __emutls_get_address should still return an
219*8d67ca89SAndroid Build Coastguard Worker     // aligned address.
220*8d67ca89SAndroid Build Coastguard Worker     saved_gap[n] = malloc(1);
221*8d67ca89SAndroid Build Coastguard Worker     void* p = __emutls_get_address(&tls_var[n]);
222*8d67ca89SAndroid Build Coastguard Worker     saved_p[n] = p;
223*8d67ca89SAndroid Build Coastguard Worker     ASSERT_TRUE(p != nullptr);
224*8d67ca89SAndroid Build Coastguard Worker     ASSERT_TRUE(tls_var[n].object.index != 0);
225*8d67ca89SAndroid Build Coastguard Worker     // check if p is a new object.
226*8d67ca89SAndroid Build Coastguard Worker     if (n > 0) {
227*8d67ca89SAndroid Build Coastguard Worker       // In single-thread environment, object.address == p.
228*8d67ca89SAndroid Build Coastguard Worker       // In multi-threads environment, object.index is increased.
229*8d67ca89SAndroid Build Coastguard Worker       ASSERT_TRUE(prev_index + 1 == tls_var[n].object.index ||
230*8d67ca89SAndroid Build Coastguard Worker                   p == tls_var[n].object.address);
231*8d67ca89SAndroid Build Coastguard Worker       ASSERT_TRUE(p != saved_p[n - 1]);
232*8d67ca89SAndroid Build Coastguard Worker     }
233*8d67ca89SAndroid Build Coastguard Worker     prev_index = tls_var[n].object.index;
234*8d67ca89SAndroid Build Coastguard Worker     // check if p is aligned
235*8d67ca89SAndroid Build Coastguard Worker     uintptr_t align = (1 << n);
236*8d67ca89SAndroid Build Coastguard Worker     uintptr_t address= reinterpret_cast<uintptr_t>(p);
237*8d67ca89SAndroid Build Coastguard Worker     ASSERT_EQ((address & ~(align - 1)), address);
238*8d67ca89SAndroid Build Coastguard Worker     // check if *p is initialized
239*8d67ca89SAndroid Build Coastguard Worker     ASSERT_STREQ(tls_value1, static_cast<char*>(p));
240*8d67ca89SAndroid Build Coastguard Worker     // change value in *p
241*8d67ca89SAndroid Build Coastguard Worker     memcpy(p, tls_value2, strlen(tls_value2) + 1);
242*8d67ca89SAndroid Build Coastguard Worker   }
243*8d67ca89SAndroid Build Coastguard Worker   for (size_t n = 0; n < num_saved_values; n++) {
244*8d67ca89SAndroid Build Coastguard Worker     free(saved_gap[n]);
245*8d67ca89SAndroid Build Coastguard Worker   }
246*8d67ca89SAndroid Build Coastguard Worker   for (size_t n = 0; n < num_saved_values; n++) {
247*8d67ca89SAndroid Build Coastguard Worker     void* p = __emutls_get_address(&tls_var[n]);
248*8d67ca89SAndroid Build Coastguard Worker     ASSERT_EQ(p, saved_p[n]);
249*8d67ca89SAndroid Build Coastguard Worker     // check if *p has the new value
250*8d67ca89SAndroid Build Coastguard Worker     ASSERT_STREQ(tls_value2, static_cast<char*>(p));
251*8d67ca89SAndroid Build Coastguard Worker   }
252*8d67ca89SAndroid Build Coastguard Worker }
253