xref: /aosp_15_r20/external/perfetto/src/base/thread_task_runner_unittest.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
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