1 /*
2 * Copyright (C) 2019 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 "perfetto/ext/base/thread_task_runner.h"
18
19 #include <thread>
20
21 #include "perfetto/ext/base/no_destructor.h"
22 #include "perfetto/ext/base/thread_checker.h"
23 #include "test/gtest_and_gmock.h"
24
25 namespace perfetto {
26 namespace base {
27 namespace {
28
29 class ThreadTaskRunnerTest : public ::testing::Test {
30 protected:
31 std::atomic<bool> atomic_flag_{false};
32 };
33
TEST_F(ThreadTaskRunnerTest,ConstructedRunning)34 TEST_F(ThreadTaskRunnerTest, ConstructedRunning) {
35 ThreadTaskRunner task_runner = ThreadTaskRunner::CreateAndStart();
36 task_runner.get()->PostTask([this] { atomic_flag_ = true; });
37 // main thread not blocked, wait on the task explicitly
38 while (!atomic_flag_) {
39 std::this_thread::yield();
40 }
41 }
42
TEST_F(ThreadTaskRunnerTest,RunsTasksOnOneDedicatedThread)43 TEST_F(ThreadTaskRunnerTest, RunsTasksOnOneDedicatedThread) {
44 ThreadTaskRunner task_runner = ThreadTaskRunner::CreateAndStart();
45 EXPECT_FALSE(task_runner.get()->RunsTasksOnCurrentThread());
46
47 ThreadChecker thread_checker;
48 task_runner.get()->PostTask([&thread_checker] {
49 // make thread_checker track the task thread
50 thread_checker.DetachFromThread();
51 EXPECT_TRUE(thread_checker.CalledOnValidThread());
52 });
53 task_runner.get()->PostTask([this, &thread_checker] {
54 // called on the same thread
55 EXPECT_TRUE(thread_checker.CalledOnValidThread());
56 atomic_flag_ = true;
57 });
58
59 while (!atomic_flag_) {
60 std::this_thread::yield();
61 }
62 }
63
TEST_F(ThreadTaskRunnerTest,MovableOwnership)64 TEST_F(ThreadTaskRunnerTest, MovableOwnership) {
65 // Will destroy manually.
66 NoDestructor<ThreadTaskRunner> ttr{ThreadTaskRunner::CreateAndStart()};
67 ThreadTaskRunner& task_runner = ttr.ref();
68
69 UnixTaskRunner* runner_ptr = task_runner.get();
70 EXPECT_NE(runner_ptr, nullptr);
71
72 ThreadChecker thread_checker;
73 task_runner.get()->PostTask([&thread_checker] {
74 // make thread_checker track the task thread
75 thread_checker.DetachFromThread();
76 EXPECT_TRUE(thread_checker.CalledOnValidThread());
77 });
78
79 // move ownership and destroy old instance
80 ThreadTaskRunner task_runner2 = std::move(task_runner);
81 EXPECT_EQ(task_runner.get(), nullptr);
82 task_runner.~ThreadTaskRunner();
83
84 // runner pointer is stable, and remains usable
85 EXPECT_EQ(task_runner2.get(), runner_ptr);
86 task_runner2.get()->PostTask([this, &thread_checker] {
87 // task thread remains the same
88 EXPECT_TRUE(thread_checker.CalledOnValidThread());
89 atomic_flag_ = true;
90 });
91
92 while (!atomic_flag_) {
93 std::this_thread::yield();
94 }
95 }
96
97 // Test helper callable that remembers a copy of a given ThreadChecker, and
98 // checks that this class' destructor runs on the remembered thread. Note that
99 // it is copyable so that it can be passed as a task (i.e. std::function) to a
100 // task runner. Also note that all instances of this class will thread-check,
101 // including those that have been moved-from.
102 class DestructorThreadChecker {
103 public:
DestructorThreadChecker(ThreadChecker checker)104 DestructorThreadChecker(ThreadChecker checker) : checker_(checker) {}
105 DestructorThreadChecker(const DestructorThreadChecker&) = default;
106 DestructorThreadChecker& operator=(const DestructorThreadChecker&) = default;
107 DestructorThreadChecker(DestructorThreadChecker&&) = default;
108 DestructorThreadChecker& operator=(DestructorThreadChecker&&) = default;
109
~DestructorThreadChecker()110 ~DestructorThreadChecker() { EXPECT_TRUE(checker_.CalledOnValidThread()); }
111
operator ()()112 void operator()() { GTEST_FAIL() << "shouldn't be called"; }
113
114 ThreadChecker checker_;
115 };
116
117 // Checks that the still-pending tasks (and therefore the UnixTaskRunner itself)
118 // are destructed on the task thread, and not the thread that destroys the
119 // ThreadTaskRunner.
TEST_F(ThreadTaskRunnerTest,EnqueuedTasksDestructedOnTaskThread)120 TEST_F(ThreadTaskRunnerTest, EnqueuedTasksDestructedOnTaskThread) {
121 ThreadChecker thread_checker;
122 ThreadTaskRunner task_runner = ThreadTaskRunner::CreateAndStart();
123
124 task_runner.get()->PostTask([this, &thread_checker, &task_runner] {
125 // make thread_checker track the task thread
126 thread_checker.DetachFromThread();
127 EXPECT_TRUE(thread_checker.CalledOnValidThread());
128 // Post a follow-up delayed task and unblock the main thread, which will
129 // destroy the ThreadTaskRunner while this task is still pending.
130 //
131 // Note: DestructorThreadChecker will thread-check (at least) twice:
132 // * for the temporary that was moved-from to construct the task
133 // std::function. Will pass as we're posting from a task thread.
134 // * for the still-pending task once the ThreadTaskRunner destruction causes
135 // the destruction of UnixTaskRunner.
136 task_runner.get()->PostDelayedTask(DestructorThreadChecker(thread_checker),
137 100 * 1000 /*ms*/);
138 atomic_flag_ = true;
139 });
140
141 while (!atomic_flag_) {
142 std::this_thread::yield();
143 }
144 }
145
146 } // namespace
147 } // namespace base
148 } // namespace perfetto
149