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_checker.h"
6
7 #include <memory>
8
9 #include "base/functional/bind.h"
10 #include "base/functional/callback_helpers.h"
11 #include "base/memory/ref_counted.h"
12 #include "base/sequence_token.h"
13 #include "base/task/single_thread_task_runner.h"
14 #include "base/test/bind.h"
15 #include "base/test/gtest_util.h"
16 #include "base/test/test_simple_task_runner.h"
17 #include "base/threading/simple_thread.h"
18 #include "base/threading/thread_local.h"
19 #include "testing/gtest/include/gtest/gtest.h"
20
21 namespace base::internal {
22 namespace {
23
24 // A thread that runs a callback.
25 class RunCallbackThread : public SimpleThread {
26 public:
RunCallbackThread(OnceClosure callback)27 explicit RunCallbackThread(OnceClosure callback)
28 : SimpleThread("RunCallbackThread"), callback_(std::move(callback)) {}
29
30 RunCallbackThread(const RunCallbackThread&) = delete;
31 RunCallbackThread& operator=(const RunCallbackThread&) = delete;
32
33 private:
34 // SimpleThread:
Run()35 void Run() override { std::move(callback_).Run(); }
36
37 OnceClosure callback_;
38 };
39
40 // Runs a callback on a new thread synchronously.
RunCallbackOnNewThreadSynchronously(OnceClosure callback)41 void RunCallbackOnNewThreadSynchronously(OnceClosure callback) {
42 RunCallbackThread run_callback_thread(std::move(callback));
43 run_callback_thread.Start();
44 run_callback_thread.Join();
45 }
46
ExpectCalledOnValidThread(ThreadCheckerImpl * thread_checker)47 void ExpectCalledOnValidThread(ThreadCheckerImpl* thread_checker) {
48 ASSERT_TRUE(thread_checker);
49
50 // This should bind |thread_checker| to the current thread if it wasn't
51 // already bound to a thread.
52 EXPECT_TRUE(thread_checker->CalledOnValidThread());
53
54 // Since |thread_checker| is now bound to the current thread, another call to
55 // CalledOnValidThread() should return true.
56 EXPECT_TRUE(thread_checker->CalledOnValidThread());
57 }
58
ExpectNotCalledOnValidThread(ThreadCheckerImpl * thread_checker)59 void ExpectNotCalledOnValidThread(ThreadCheckerImpl* thread_checker) {
60 ASSERT_TRUE(thread_checker);
61 EXPECT_FALSE(thread_checker->CalledOnValidThread());
62 }
63
ExpectNotCalledOnValidThreadWithSequenceTokenThreadBound(ThreadCheckerImpl * thread_checker,SequenceToken sequence_token)64 void ExpectNotCalledOnValidThreadWithSequenceTokenThreadBound(
65 ThreadCheckerImpl* thread_checker,
66 SequenceToken sequence_token) {
67 TaskScope task_scope(sequence_token, /* is_thread_bound=*/true);
68 ExpectNotCalledOnValidThread(thread_checker);
69 }
70
71 } // namespace
72
TEST(ThreadCheckerTest,AllowedSameThreadNoSequenceToken)73 TEST(ThreadCheckerTest, AllowedSameThreadNoSequenceToken) {
74 ThreadCheckerImpl thread_checker;
75 EXPECT_TRUE(thread_checker.CalledOnValidThread());
76 }
77
TEST(ThreadCheckerTest,AllowedSameThreadAndSequenceDifferentThreadBoundTasks)78 TEST(ThreadCheckerTest, AllowedSameThreadAndSequenceDifferentThreadBoundTasks) {
79 std::unique_ptr<ThreadCheckerImpl> thread_checker;
80 const SequenceToken sequence_token = SequenceToken::Create();
81
82 {
83 TaskScope task_scope(sequence_token,
84 /* is_thread_bound=*/true);
85 thread_checker = std::make_unique<ThreadCheckerImpl>();
86 }
87
88 {
89 TaskScope task_scope(sequence_token,
90 /* is_thread_bound=*/true);
91 EXPECT_TRUE(thread_checker->CalledOnValidThread());
92 }
93 }
94
TEST(ThreadCheckerTest,AllowedSameThreadSequenceAndTaskNotThreadBound)95 TEST(ThreadCheckerTest, AllowedSameThreadSequenceAndTaskNotThreadBound) {
96 TaskScope task_scope(SequenceToken::Create(),
97 /* is_thread_bound=*/false);
98 ThreadCheckerImpl thread_checker;
99 EXPECT_TRUE(thread_checker.CalledOnValidThread());
100 }
101
TEST(ThreadCheckerTest,DisallowedSameThreadAndSequenceDifferentTasksNotThreadBound)102 TEST(ThreadCheckerTest,
103 DisallowedSameThreadAndSequenceDifferentTasksNotThreadBound) {
104 std::unique_ptr<ThreadCheckerImpl> thread_checker;
105
106 {
107 TaskScope task_scope(SequenceToken::Create(),
108 /* is_thread_bound=*/false);
109 thread_checker = std::make_unique<ThreadCheckerImpl>();
110 }
111
112 {
113 TaskScope task_scope(SequenceToken::Create(),
114 /* is_thread_bound=*/false);
115 EXPECT_FALSE(thread_checker->CalledOnValidThread());
116 }
117 }
118
TEST(ThreadCheckerTest,DisallowedDifferentThreadsNoSequenceToken)119 TEST(ThreadCheckerTest, DisallowedDifferentThreadsNoSequenceToken) {
120 ThreadCheckerImpl thread_checker;
121 RunCallbackOnNewThreadSynchronously(
122 BindOnce(&ExpectNotCalledOnValidThread, Unretained(&thread_checker)));
123 }
124
TEST(ThreadCheckerTest,DisallowedDifferentThreadsSameSequence)125 TEST(ThreadCheckerTest, DisallowedDifferentThreadsSameSequence) {
126 SingleThreadTaskRunner::CurrentDefaultHandle
127 single_thread_task_runner_current_default_handle(
128 MakeRefCounted<TestSimpleTaskRunner>());
129 const SequenceToken sequence_token(SequenceToken::Create());
130
131 TaskScope task_scope(sequence_token,
132 /* is_thread_bound=*/false);
133 ThreadCheckerImpl thread_checker;
134 EXPECT_TRUE(thread_checker.CalledOnValidThread());
135
136 RunCallbackOnNewThreadSynchronously(
137 BindOnce(&ExpectNotCalledOnValidThreadWithSequenceTokenThreadBound,
138 Unretained(&thread_checker), sequence_token));
139 }
140
TEST(ThreadCheckerTest,DisallowedSameThreadDifferentSequence)141 TEST(ThreadCheckerTest, DisallowedSameThreadDifferentSequence) {
142 std::unique_ptr<ThreadCheckerImpl> thread_checker;
143
144 SingleThreadTaskRunner::CurrentDefaultHandle
145 single_thread_task_runner_current_default_handle(
146 MakeRefCounted<TestSimpleTaskRunner>());
147
148 {
149 TaskScope task_scope(SequenceToken::Create(),
150 /* is_thread_bound=*/false);
151 thread_checker = std::make_unique<ThreadCheckerImpl>();
152 }
153
154 {
155 // Different SequenceToken.
156 TaskScope task_scope(SequenceToken::Create(),
157 /* is_thread_bound=*/false);
158 EXPECT_FALSE(thread_checker->CalledOnValidThread());
159 }
160
161 // No SequenceToken.
162 EXPECT_FALSE(thread_checker->CalledOnValidThread());
163 }
164
TEST(ThreadCheckerTest,DetachFromThread)165 TEST(ThreadCheckerTest, DetachFromThread) {
166 ThreadCheckerImpl thread_checker;
167 thread_checker.DetachFromThread();
168
169 // Verify that CalledOnValidThread() returns true when called on a different
170 // thread after a call to DetachFromThread().
171 RunCallbackOnNewThreadSynchronously(
172 BindOnce(&ExpectCalledOnValidThread, Unretained(&thread_checker)));
173
174 EXPECT_FALSE(thread_checker.CalledOnValidThread());
175 }
176
TEST(ThreadCheckerTest,DetachFromThreadWithSequenceToken)177 TEST(ThreadCheckerTest, DetachFromThreadWithSequenceToken) {
178 SingleThreadTaskRunner::CurrentDefaultHandle
179 single_thread_task_runner_current_default_handle(
180 MakeRefCounted<TestSimpleTaskRunner>());
181 TaskScope task_scope(SequenceToken::Create(),
182 /* is_thread_bound=*/false);
183 ThreadCheckerImpl thread_checker;
184 thread_checker.DetachFromThread();
185
186 // Verify that CalledOnValidThread() returns true when called on a different
187 // thread after a call to DetachFromThread().
188 RunCallbackOnNewThreadSynchronously(
189 BindOnce(&ExpectCalledOnValidThread, Unretained(&thread_checker)));
190
191 EXPECT_FALSE(thread_checker.CalledOnValidThread());
192 }
193
194 // Owns a ThreadCheckerImpl and asserts that CalledOnValidThread() is valid
195 // in ~ThreadCheckerOwner.
196 class ThreadCheckerOwner {
197 public:
ThreadCheckerOwner(bool detach_from_thread)198 explicit ThreadCheckerOwner(bool detach_from_thread) {
199 if (detach_from_thread)
200 checker_.DetachFromThread();
201 }
202
203 ThreadCheckerOwner(const ThreadCheckerOwner&) = delete;
204 ThreadCheckerOwner& operator=(const ThreadCheckerOwner&) = delete;
205
~ThreadCheckerOwner()206 ~ThreadCheckerOwner() { EXPECT_TRUE(checker_.CalledOnValidThread()); }
207
208 private:
209 ThreadCheckerImpl checker_;
210 };
211
212 // Verifies ThreadCheckerImpl::CalledOnValidThread() returns true if called
213 // during thread destruction.
TEST(ThreadCheckerTest,CalledOnValidThreadFromThreadDestruction)214 TEST(ThreadCheckerTest, CalledOnValidThreadFromThreadDestruction) {
215 ThreadLocalOwnedPointer<ThreadCheckerOwner> thread_local_owner;
216 RunCallbackOnNewThreadSynchronously(BindLambdaForTesting([&]() {
217 thread_local_owner.Set(std::make_unique<ThreadCheckerOwner>(false));
218 }));
219 }
220
221 // Variant of CalledOnValidThreadFromThreadDestruction that calls
222 // ThreadCheckerImpl::DetachFromThread().
TEST(ThreadCheckerTest,CalledOnValidThreadFromThreadDestructionDetached)223 TEST(ThreadCheckerTest, CalledOnValidThreadFromThreadDestructionDetached) {
224 ThreadLocalOwnedPointer<ThreadCheckerOwner> thread_local_owner;
225 RunCallbackOnNewThreadSynchronously(BindLambdaForTesting([&]() {
226 thread_local_owner.Set(std::make_unique<ThreadCheckerOwner>(true));
227 }));
228 }
229
TEST(ThreadCheckerTest,Move)230 TEST(ThreadCheckerTest, Move) {
231 ThreadCheckerImpl initial;
232 EXPECT_TRUE(initial.CalledOnValidThread());
233
234 ThreadCheckerImpl move_constructed(std::move(initial));
235 EXPECT_TRUE(move_constructed.CalledOnValidThread());
236
237 ThreadCheckerImpl move_assigned;
238 move_assigned = std::move(move_constructed);
239 EXPECT_TRUE(move_assigned.CalledOnValidThread());
240
241 // The two ThreadCheckerImpls moved from should be able to rebind to another
242 // thread.
243 RunCallbackOnNewThreadSynchronously(
244 BindOnce(&ExpectCalledOnValidThread, Unretained(&initial)));
245 RunCallbackOnNewThreadSynchronously(
246 BindOnce(&ExpectCalledOnValidThread, Unretained(&move_constructed)));
247
248 // But the latest one shouldn't be able to run on another thread.
249 RunCallbackOnNewThreadSynchronously(
250 BindOnce(&ExpectNotCalledOnValidThread, Unretained(&move_assigned)));
251
252 EXPECT_TRUE(move_assigned.CalledOnValidThread());
253 }
254
TEST(ThreadCheckerTest,MoveAssignIntoDetached)255 TEST(ThreadCheckerTest, MoveAssignIntoDetached) {
256 ThreadCheckerImpl initial;
257
258 ThreadCheckerImpl move_assigned;
259 move_assigned.DetachFromThread();
260 move_assigned = std::move(initial);
261
262 // |initial| is detached after move.
263 RunCallbackOnNewThreadSynchronously(
264 BindOnce(&ExpectCalledOnValidThread, Unretained(&initial)));
265
266 // |move_assigned| should be associated with the main thread.
267 RunCallbackOnNewThreadSynchronously(
268 BindOnce(&ExpectNotCalledOnValidThread, Unretained(&move_assigned)));
269
270 EXPECT_TRUE(move_assigned.CalledOnValidThread());
271 }
272
TEST(ThreadCheckerTest,MoveFromDetachedRebinds)273 TEST(ThreadCheckerTest, MoveFromDetachedRebinds) {
274 ThreadCheckerImpl initial;
275 initial.DetachFromThread();
276
277 ThreadCheckerImpl moved_into(std::move(initial));
278
279 // |initial| is still detached after move.
280 RunCallbackOnNewThreadSynchronously(
281 BindOnce(&ExpectCalledOnValidThread, Unretained(&initial)));
282
283 // |moved_into| is bound to the current thread as part of the move.
284 RunCallbackOnNewThreadSynchronously(
285 BindOnce(&ExpectNotCalledOnValidThread, Unretained(&moved_into)));
286 EXPECT_TRUE(moved_into.CalledOnValidThread());
287 }
288
TEST(ThreadCheckerTest,MoveOffThreadBanned)289 TEST(ThreadCheckerTest, MoveOffThreadBanned) {
290 GTEST_FLAG_SET(death_test_style, "threadsafe");
291
292 ThreadCheckerImpl other_thread;
293 other_thread.DetachFromThread();
294 RunCallbackOnNewThreadSynchronously(
295 BindOnce(&ExpectCalledOnValidThread, Unretained(&other_thread)));
296
297 EXPECT_DCHECK_DEATH(ThreadCheckerImpl main_thread(std::move(other_thread)));
298 }
299
300 namespace {
301
302 // This fixture is a helper for unit testing the thread checker macros as it is
303 // not possible to inline ExpectDeathOnOtherThread() and
304 // ExpectNoDeathOnOtherThreadAfterDetach() as lambdas since binding
305 // |Unretained(&my_sequence_checker)| wouldn't compile on non-dcheck builds
306 // where it won't be defined.
307 class ThreadCheckerMacroTest : public testing::Test {
308 public:
309 ThreadCheckerMacroTest() = default;
310
311 ThreadCheckerMacroTest(const ThreadCheckerMacroTest&) = delete;
312 ThreadCheckerMacroTest& operator=(const ThreadCheckerMacroTest&) = delete;
313
ExpectDeathOnOtherThread()314 void ExpectDeathOnOtherThread() {
315 #if DCHECK_IS_ON()
316 EXPECT_DCHECK_DEATH({ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); });
317 #else
318 // Happily no-ops on non-dcheck builds.
319 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
320 #endif
321 }
322
ExpectNoDeathOnOtherThreadAfterDetach()323 void ExpectNoDeathOnOtherThreadAfterDetach() {
324 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
325 }
326
327 protected:
328 THREAD_CHECKER(thread_checker_);
329 };
330
331 } // namespace
332
TEST_F(ThreadCheckerMacroTest,Macros)333 TEST_F(ThreadCheckerMacroTest, Macros) {
334 GTEST_FLAG_SET(death_test_style, "threadsafe");
335
336 THREAD_CHECKER(my_thread_checker);
337
338 RunCallbackOnNewThreadSynchronously(BindOnce(
339 &ThreadCheckerMacroTest::ExpectDeathOnOtherThread, Unretained(this)));
340
341 DETACH_FROM_THREAD(thread_checker_);
342
343 RunCallbackOnNewThreadSynchronously(
344 BindOnce(&ThreadCheckerMacroTest::ExpectNoDeathOnOtherThreadAfterDetach,
345 Unretained(this)));
346 }
347
348 } // namespace base::internal
349