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