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