1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/threading/thread_local.h"
6
7 #include <optional>
8
9 #include "base/check_op.h"
10 #include "base/memory/raw_ptr.h"
11 #include "base/synchronization/waitable_event.h"
12 #include "base/test/bind.h"
13 #include "base/test/gtest_util.h"
14 #include "base/threading/simple_thread.h"
15 #include "base/threading/thread.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17
18 namespace base {
19
20 namespace {
21
22 // A simple helper which sets the given boolean to true on destruction.
23 class SetTrueOnDestruction {
24 public:
SetTrueOnDestruction(bool * was_destroyed)25 explicit SetTrueOnDestruction(bool* was_destroyed)
26 : was_destroyed_(was_destroyed) {
27 CHECK_NE(was_destroyed, nullptr);
28 }
29
30 SetTrueOnDestruction(const SetTrueOnDestruction&) = delete;
31 SetTrueOnDestruction& operator=(const SetTrueOnDestruction&) = delete;
32
~SetTrueOnDestruction()33 ~SetTrueOnDestruction() {
34 EXPECT_FALSE(*was_destroyed_);
35 *was_destroyed_ = true;
36 }
37
38 private:
39 const raw_ptr<bool> was_destroyed_;
40 };
41
42 } // namespace
43
TEST(ThreadLocalTest,ThreadLocalOwnedPointerBasic)44 TEST(ThreadLocalTest, ThreadLocalOwnedPointerBasic) {
45 ThreadLocalOwnedPointer<SetTrueOnDestruction> tls_owned_pointer;
46 EXPECT_FALSE(tls_owned_pointer.Get());
47
48 bool was_destroyed1 = false;
49 tls_owned_pointer.Set(
50 std::make_unique<SetTrueOnDestruction>(&was_destroyed1));
51 EXPECT_FALSE(was_destroyed1);
52 EXPECT_TRUE(tls_owned_pointer.Get());
53
54 bool was_destroyed2 = false;
55 tls_owned_pointer.Set(
56 std::make_unique<SetTrueOnDestruction>(&was_destroyed2));
57 EXPECT_TRUE(was_destroyed1);
58 EXPECT_FALSE(was_destroyed2);
59 EXPECT_TRUE(tls_owned_pointer.Get());
60
61 tls_owned_pointer.Set(nullptr);
62 EXPECT_TRUE(was_destroyed1);
63 EXPECT_TRUE(was_destroyed2);
64 EXPECT_FALSE(tls_owned_pointer.Get());
65 }
66
TEST(ThreadLocalTest,ThreadLocalOwnedPointerFreedOnThreadExit)67 TEST(ThreadLocalTest, ThreadLocalOwnedPointerFreedOnThreadExit) {
68 bool tls_was_destroyed = false;
69 ThreadLocalOwnedPointer<SetTrueOnDestruction> tls_owned_pointer;
70
71 Thread thread("TestThread");
72 thread.Start();
73
74 WaitableEvent tls_set;
75
76 thread.task_runner()->PostTask(
77 FROM_HERE, BindLambdaForTesting([&]() {
78 tls_owned_pointer.Set(
79 std::make_unique<SetTrueOnDestruction>(&tls_was_destroyed));
80 tls_set.Signal();
81 }));
82
83 tls_set.Wait();
84 EXPECT_FALSE(tls_was_destroyed);
85
86 thread.Stop();
87 EXPECT_TRUE(tls_was_destroyed);
88 }
89
TEST(ThreadLocalTest,ThreadLocalOwnedPointerCleansUpMainThreadOnDestruction)90 TEST(ThreadLocalTest, ThreadLocalOwnedPointerCleansUpMainThreadOnDestruction) {
91 std::optional<ThreadLocalOwnedPointer<SetTrueOnDestruction>>
92 tls_owned_pointer(std::in_place);
93 bool tls_was_destroyed_other = false;
94
95 Thread thread("TestThread");
96 thread.Start();
97
98 WaitableEvent tls_set;
99
100 thread.task_runner()->PostTask(
101 FROM_HERE, BindLambdaForTesting([&]() {
102 tls_owned_pointer->Set(
103 std::make_unique<SetTrueOnDestruction>(&tls_was_destroyed_other));
104 tls_set.Signal();
105 }));
106
107 tls_set.Wait();
108
109 bool tls_was_destroyed_main = false;
110 tls_owned_pointer->Set(
111 std::make_unique<SetTrueOnDestruction>(&tls_was_destroyed_main));
112 EXPECT_FALSE(tls_was_destroyed_other);
113 EXPECT_FALSE(tls_was_destroyed_main);
114
115 // Stopping the thread relinquishes its TLS (as in
116 // ThreadLocalOwnedPointerFreedOnThreadExit).
117 thread.Stop();
118 EXPECT_TRUE(tls_was_destroyed_other);
119 EXPECT_FALSE(tls_was_destroyed_main);
120
121 // Deleting the ThreadLocalOwnedPointer instance on the main thread is allowed
122 // iff that's the only thread with remaining storage (ref. disallowed use case
123 // in ThreadLocalOwnedPointerDeathIfDestroyedWithActiveThread below). In that
124 // case, the storage on the main thread is freed before releasing the TLS
125 // slot.
126 tls_owned_pointer.reset();
127 EXPECT_TRUE(tls_was_destroyed_main);
128 }
129
TEST(ThreadLocalTest,ThreadLocalOwnedPointerDeathIfDestroyedWithActiveThread)130 TEST(ThreadLocalTest, ThreadLocalOwnedPointerDeathIfDestroyedWithActiveThread) {
131 GTEST_FLAG_SET(death_test_style, "threadsafe");
132
133 std::optional<ThreadLocalOwnedPointer<int>> tls_owned_pointer(std::in_place);
134
135 Thread thread("TestThread");
136 thread.Start();
137
138 WaitableEvent tls_set;
139
140 thread.task_runner()->PostTask(
141 FROM_HERE, BindLambdaForTesting([&]() {
142 tls_owned_pointer->Set(std::make_unique<int>(1));
143 tls_set.Signal();
144 }));
145
146 tls_set.Wait();
147
148 EXPECT_DCHECK_DEATH({ tls_owned_pointer.reset(); });
149 }
150
TEST(ThreadLocalTest,ThreadLocalOwnedPointerMultiThreadedAndStaticStorage)151 TEST(ThreadLocalTest, ThreadLocalOwnedPointerMultiThreadedAndStaticStorage) {
152 constexpr int kNumThreads = 16;
153
154 static ThreadLocalOwnedPointer<SetTrueOnDestruction> tls_owned_pointer;
155
156 std::array<bool, kNumThreads> were_destroyed{};
157
158 std::array<std::unique_ptr<Thread>, kNumThreads> threads;
159
160 for (auto& thread : threads) {
161 thread = std::make_unique<Thread>("TestThread");
162 thread->Start();
163 }
164
165 for (const auto& thread : threads) {
166 // Waiting is unnecessary but enhances the likelihood of data races in the
167 // next steps.
168 thread->WaitUntilThreadStarted();
169 }
170
171 for (const bool was_destroyed : were_destroyed) {
172 EXPECT_FALSE(was_destroyed);
173 }
174
175 for (int i = 0; i < kNumThreads; ++i) {
176 threads[i]->task_runner()->PostTask(
177 FROM_HERE,
178 BindOnce(
179 [](bool* was_destroyed) {
180 tls_owned_pointer.Set(
181 std::make_unique<SetTrueOnDestruction>(was_destroyed));
182 },
183 &were_destroyed[i]));
184 }
185
186 static bool main_thread_was_destroyed = false;
187 // Even when the test is run multiple times in the same process: TLS should
188 // never be destroyed until static uninitialization.
189 EXPECT_FALSE(main_thread_was_destroyed);
190
191 tls_owned_pointer.Set(
192 std::make_unique<SetTrueOnDestruction>(&main_thread_was_destroyed));
193
194 for (const auto& thread : threads) {
195 thread->Stop();
196 }
197
198 for (const bool was_destroyed : were_destroyed) {
199 EXPECT_TRUE(was_destroyed);
200 }
201
202 // The main thread's TLS still wasn't destroyed (let the test unfold naturally
203 // through static uninitialization).
204 EXPECT_FALSE(main_thread_was_destroyed);
205 }
206
207 } // namespace base
208