blob: aeba2ba865cb477864237c9a3ab6af01fd733900 [file] [log] [blame]
Chih-Hung Hsieh7656d0c2015-07-27 10:46:21 -07001/*
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>
Chih-Hung Hsieh83380b52015-08-14 14:11:53 -070018#include <stdint.h>
19#include <string.h>
Chih-Hung Hsieh7656d0c2015-07-27 10:46:21 -070020
21#ifdef __GNUC__
22// Gcc has a bug with -O -fdata-section for the arm target: http://b/22772147.
23// Until that bug is fixed, disable optimization since
24// it is not essential for this test.
25#pragma GCC optimize("-O0")
26#endif
27
28__thread int local_var = 100;
29int shared_var = 200;
30
31static void reset_vars() {
32 local_var = 1000;
33 shared_var = 2000;
34 // local_var should be reset by threads
35}
36
37typedef void* (*MyThread)(void*);
38
39static void* inc_shared_var(void* p) {
40 int *data = reinterpret_cast<int*>(p);
41 shared_var++;
42 *data = shared_var;
43 return nullptr;
44}
45
46static void* inc_local_var(void* p) {
47 int *data = reinterpret_cast<int*>(p);
48 local_var++;
49 *data = local_var;
50 return nullptr;
51}
52
53static int run_one_thread(MyThread foo) {
54 pthread_t t;
55 int data;
56 int error = pthread_create(&t, nullptr, foo, &data);
57 if (!error)
58 error = pthread_join(t, nullptr);
59 return error ? error : data;
60}
61
62TEST(thread_local_storage, shared) {
63 reset_vars();
64 ASSERT_EQ(local_var, 1000);
65 ASSERT_EQ(shared_var, 2000);
66
67 // Update shared_var, local_var remains 1000.
68 ASSERT_EQ(run_one_thread(inc_shared_var), 2001);
69 ASSERT_EQ(local_var, 1000);
70 ASSERT_EQ(shared_var, 2001);
71
72 ASSERT_EQ(run_one_thread(inc_shared_var), 2002);
73 ASSERT_EQ(local_var, 1000);
74 ASSERT_EQ(shared_var, 2002);
75
76 ASSERT_EQ(run_one_thread(inc_shared_var), 2003);
77 ASSERT_EQ(local_var, 1000);
78 ASSERT_EQ(shared_var, 2003);
79}
80
81TEST(thread_local_storage, local) {
82 reset_vars();
83 ASSERT_EQ(local_var, 1000);
84 ASSERT_EQ(shared_var, 2000);
85
86 // When a child thread updates its own TLS variable,
87 // this thread's local_var and shared_var are not changed.
88 // TLS local_var is initialized to 100 in a thread.
89 ASSERT_EQ(run_one_thread(inc_local_var), 101);
90 ASSERT_EQ(local_var, 1000);
91 ASSERT_EQ(shared_var, 2000);
92
93 ASSERT_EQ(run_one_thread(inc_local_var), 101);
94 ASSERT_EQ(local_var, 1000);
95 ASSERT_EQ(shared_var, 2000);
96
97 ASSERT_EQ(run_one_thread(inc_local_var), 101);
98 ASSERT_EQ(local_var, 1000);
99 ASSERT_EQ(shared_var, 2000);
100}
101
102// Test TLS initialization of more complicated type, array of struct.
103struct Point {
104 int x, y;
105};
106
107typedef Point Triangle[3];
108
109__thread Triangle local_triangle = {{10,10}, {20,20}, {30,30}};
110Triangle shared_triangle = {{1,1}, {2,2}, {3,3}};
111
112static void reset_triangle() {
113 static const Triangle t1 = {{3,3}, {4,4}, {5,5}};
114 static const Triangle t2 = {{2,2}, {3,3}, {4,4}};
115 memcpy(local_triangle, t1, sizeof(local_triangle));
116 memcpy(shared_triangle, t2, sizeof(shared_triangle));
117}
118
119static void* move_shared_triangle(void* p) {
120 int *data = reinterpret_cast<int*>(p);
121 shared_triangle[1].y++;
122 *data = shared_triangle[1].y;
123 return nullptr;
124}
125
126static void* move_local_triangle(void* p) {
127 int *data = reinterpret_cast<int*>(p);
128 local_triangle[1].y++;
129 *data = local_triangle[1].y;
130 return nullptr;
131}
132
133TEST(thread_local_storage, shared_triangle) {
134 reset_triangle();
135 ASSERT_EQ(local_triangle[1].y, 4);
136 ASSERT_EQ(shared_triangle[1].y, 3);
137
138 // Update shared_triangle, local_triangle remains 1000.
139 ASSERT_EQ(run_one_thread(move_shared_triangle), 4);
140 ASSERT_EQ(local_triangle[1].y, 4);
141 ASSERT_EQ(shared_triangle[1].y, 4);
142
143 ASSERT_EQ(run_one_thread(move_shared_triangle), 5);
144 ASSERT_EQ(local_triangle[1].y, 4);
145 ASSERT_EQ(shared_triangle[1].y, 5);
146
147 ASSERT_EQ(run_one_thread(move_shared_triangle), 6);
148 ASSERT_EQ(local_triangle[1].y, 4);
149 ASSERT_EQ(shared_triangle[1].y, 6);
150}
151
152TEST(thread_local_storage, local_triangle) {
153 reset_triangle();
154 ASSERT_EQ(local_triangle[1].y, 4);
155 ASSERT_EQ(shared_triangle[1].y, 3);
156
157 // Update local_triangle, parent thread's
158 // shared_triangle and local_triangle are unchanged.
159 ASSERT_EQ(run_one_thread(move_local_triangle), 21);
160 ASSERT_EQ(local_triangle[1].y, 4);
161 ASSERT_EQ(shared_triangle[1].y, 3);
162
163 ASSERT_EQ(run_one_thread(move_local_triangle), 21);
164 ASSERT_EQ(local_triangle[1].y, 4);
165 ASSERT_EQ(shared_triangle[1].y, 3);
166
167 ASSERT_EQ(run_one_thread(move_local_triangle), 21);
168 ASSERT_EQ(local_triangle[1].y, 4);
169 ASSERT_EQ(shared_triangle[1].y, 3);
170}
Chih-Hung Hsieh83380b52015-08-14 14:11:53 -0700171
172// Test emutls runtime data structures and __emutls_get_address function.
173typedef unsigned int gcc_word __attribute__((mode(word)));
174typedef unsigned int gcc_pointer __attribute__((mode(pointer)));
175struct gcc_emutls_object { // for libgcc
176 gcc_word size;
177 gcc_word align;
178 union {
179 gcc_pointer offset;
180 void* ptr;
181 } loc;
182 void* templ;
183};
184
185typedef struct __emutls_control { // for clang/llvm
186 size_t size;
187 size_t align;
188 union {
189 uintptr_t index;
190 void* address;
191 } object;
192 void* value;
193} __emutls_control;
194
195TEST(thread_local_storage, type_size) {
196 static_assert(sizeof(size_t) == sizeof(gcc_word),
197 "size_t != gcc_word");
198 static_assert(sizeof(uintptr_t) == sizeof(gcc_pointer),
199 "uintptr_t != gcc_pointer");
200 static_assert(sizeof(uintptr_t) == sizeof(void*),
201 "sizoeof(uintptr_t) != sizeof(void*)");
202 static_assert(sizeof(__emutls_control) == sizeof(struct gcc_emutls_object),
203 "sizeof(__emutls_control) != sizeof(struct gcc_emutls_object)");
204}
205
206extern "C" void* __emutls_get_address(__emutls_control*);
207
208TEST(thread_local_storage, init_value) {
209 char tls_value1[] = "123456789";
210 char tls_value2[] = "abcdefghi";
211 constexpr size_t num_saved_values = 10;
212 __emutls_control tls_var[num_saved_values];
213 size_t prev_index = 0;
214 void* saved_gap[num_saved_values];
215 void* saved_p[num_saved_values];
216 ASSERT_TRUE(strlen(tls_value2) <= strlen(tls_value1));
217 __emutls_control c =
218 {strlen(tls_value1) + 1, 1, {0}, tls_value1};
219 for (size_t n = 0; n < num_saved_values; n++) {
220 memcpy(&tls_var[n], &c, sizeof(c));
221 tls_var[n].align = (1 << n);
222 }
223 for (size_t n = 0; n < num_saved_values; n++) {
224 // Try to mess up malloc space so that the next malloc will not have the
225 // required alignment, but __emutls_get_address should still return an
226 // aligned address.
227 saved_gap[n] = malloc(1);
228 void* p = __emutls_get_address(&tls_var[n]);
229 saved_p[n] = p;
230 ASSERT_TRUE(p != nullptr);
231 ASSERT_TRUE(tls_var[n].object.index != 0);
232 // check if p is a new object.
233 if (n > 0) {
234 // In single-thread environment, object.address == p.
235 // In multi-threads environment, object.index is increased.
236 ASSERT_TRUE(prev_index + 1 == tls_var[n].object.index ||
237 p == tls_var[n].object.address);
238 ASSERT_TRUE(p != saved_p[n - 1]);
239 }
240 prev_index = tls_var[n].object.index;
241 // check if p is aligned
242 uintptr_t align = (1 << n);
243 uintptr_t address= reinterpret_cast<uintptr_t>(p);
244 ASSERT_EQ((address & ~(align - 1)), address);
245 // check if *p is initialized
246 ASSERT_STREQ(tls_value1, static_cast<char*>(p));
247 // change value in *p
248 memcpy(p, tls_value2, strlen(tls_value2) + 1);
249 }
250 for (size_t n = 0; n < num_saved_values; n++) {
251 free(saved_gap[n]);
252 }
253 for (size_t n = 0; n < num_saved_values; n++) {
254 void* p = __emutls_get_address(&tls_var[n]);
255 ASSERT_EQ(p, saved_p[n]);
256 // check if *p has the new value
257 ASSERT_STREQ(tls_value2, static_cast<char*>(p));
258 }
259}