blob: 5dead32c47038a510dbde64a6434abb24d8b80bd [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>
18
19#ifdef __GNUC__
20// Gcc has a bug with -O -fdata-section for the arm target: http://b/22772147.
21// Until that bug is fixed, disable optimization since
22// it is not essential for this test.
23#pragma GCC optimize("-O0")
24#endif
25
26__thread int local_var = 100;
27int shared_var = 200;
28
29static void reset_vars() {
30 local_var = 1000;
31 shared_var = 2000;
32 // local_var should be reset by threads
33}
34
35typedef void* (*MyThread)(void*);
36
37static void* inc_shared_var(void* p) {
38 int *data = reinterpret_cast<int*>(p);
39 shared_var++;
40 *data = shared_var;
41 return nullptr;
42}
43
44static void* inc_local_var(void* p) {
45 int *data = reinterpret_cast<int*>(p);
46 local_var++;
47 *data = local_var;
48 return nullptr;
49}
50
51static int run_one_thread(MyThread foo) {
52 pthread_t t;
53 int data;
54 int error = pthread_create(&t, nullptr, foo, &data);
55 if (!error)
56 error = pthread_join(t, nullptr);
57 return error ? error : data;
58}
59
60TEST(thread_local_storage, shared) {
61 reset_vars();
62 ASSERT_EQ(local_var, 1000);
63 ASSERT_EQ(shared_var, 2000);
64
65 // Update shared_var, local_var remains 1000.
66 ASSERT_EQ(run_one_thread(inc_shared_var), 2001);
67 ASSERT_EQ(local_var, 1000);
68 ASSERT_EQ(shared_var, 2001);
69
70 ASSERT_EQ(run_one_thread(inc_shared_var), 2002);
71 ASSERT_EQ(local_var, 1000);
72 ASSERT_EQ(shared_var, 2002);
73
74 ASSERT_EQ(run_one_thread(inc_shared_var), 2003);
75 ASSERT_EQ(local_var, 1000);
76 ASSERT_EQ(shared_var, 2003);
77}
78
79TEST(thread_local_storage, local) {
80 reset_vars();
81 ASSERT_EQ(local_var, 1000);
82 ASSERT_EQ(shared_var, 2000);
83
84 // When a child thread updates its own TLS variable,
85 // this thread's local_var and shared_var are not changed.
86 // TLS local_var is initialized to 100 in a thread.
87 ASSERT_EQ(run_one_thread(inc_local_var), 101);
88 ASSERT_EQ(local_var, 1000);
89 ASSERT_EQ(shared_var, 2000);
90
91 ASSERT_EQ(run_one_thread(inc_local_var), 101);
92 ASSERT_EQ(local_var, 1000);
93 ASSERT_EQ(shared_var, 2000);
94
95 ASSERT_EQ(run_one_thread(inc_local_var), 101);
96 ASSERT_EQ(local_var, 1000);
97 ASSERT_EQ(shared_var, 2000);
98}
99
100// Test TLS initialization of more complicated type, array of struct.
101struct Point {
102 int x, y;
103};
104
105typedef Point Triangle[3];
106
107__thread Triangle local_triangle = {{10,10}, {20,20}, {30,30}};
108Triangle shared_triangle = {{1,1}, {2,2}, {3,3}};
109
110static void reset_triangle() {
111 static const Triangle t1 = {{3,3}, {4,4}, {5,5}};
112 static const Triangle t2 = {{2,2}, {3,3}, {4,4}};
113 memcpy(local_triangle, t1, sizeof(local_triangle));
114 memcpy(shared_triangle, t2, sizeof(shared_triangle));
115}
116
117static void* move_shared_triangle(void* p) {
118 int *data = reinterpret_cast<int*>(p);
119 shared_triangle[1].y++;
120 *data = shared_triangle[1].y;
121 return nullptr;
122}
123
124static void* move_local_triangle(void* p) {
125 int *data = reinterpret_cast<int*>(p);
126 local_triangle[1].y++;
127 *data = local_triangle[1].y;
128 return nullptr;
129}
130
131TEST(thread_local_storage, shared_triangle) {
132 reset_triangle();
133 ASSERT_EQ(local_triangle[1].y, 4);
134 ASSERT_EQ(shared_triangle[1].y, 3);
135
136 // Update shared_triangle, local_triangle remains 1000.
137 ASSERT_EQ(run_one_thread(move_shared_triangle), 4);
138 ASSERT_EQ(local_triangle[1].y, 4);
139 ASSERT_EQ(shared_triangle[1].y, 4);
140
141 ASSERT_EQ(run_one_thread(move_shared_triangle), 5);
142 ASSERT_EQ(local_triangle[1].y, 4);
143 ASSERT_EQ(shared_triangle[1].y, 5);
144
145 ASSERT_EQ(run_one_thread(move_shared_triangle), 6);
146 ASSERT_EQ(local_triangle[1].y, 4);
147 ASSERT_EQ(shared_triangle[1].y, 6);
148}
149
150TEST(thread_local_storage, local_triangle) {
151 reset_triangle();
152 ASSERT_EQ(local_triangle[1].y, 4);
153 ASSERT_EQ(shared_triangle[1].y, 3);
154
155 // Update local_triangle, parent thread's
156 // shared_triangle and local_triangle are unchanged.
157 ASSERT_EQ(run_one_thread(move_local_triangle), 21);
158 ASSERT_EQ(local_triangle[1].y, 4);
159 ASSERT_EQ(shared_triangle[1].y, 3);
160
161 ASSERT_EQ(run_one_thread(move_local_triangle), 21);
162 ASSERT_EQ(local_triangle[1].y, 4);
163 ASSERT_EQ(shared_triangle[1].y, 3);
164
165 ASSERT_EQ(run_one_thread(move_local_triangle), 21);
166 ASSERT_EQ(local_triangle[1].y, 4);
167 ASSERT_EQ(shared_triangle[1].y, 3);
168}