blob: ae7dacd9b5b616d461ed9374b1bf6bff102fd889 [file] [log] [blame]
Ryan Savitski29885792019-03-14 12:10:13 +00001/*
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
Primiano Tucci2c5488f2019-06-01 03:27:28 +010017#include "perfetto/ext/base/thread_task_runner.h"
Ryan Savitski29885792019-03-14 12:10:13 +000018
19#include <thread>
20
Ryan Savitski89658b32022-08-23 14:42:28 +010021#include "perfetto/ext/base/no_destructor.h"
Primiano Tucci2c5488f2019-06-01 03:27:28 +010022#include "perfetto/ext/base/thread_checker.h"
Primiano Tucci919ca1e2019-08-21 20:26:58 +020023#include "test/gtest_and_gmock.h"
Ryan Savitski29885792019-03-14 12:10:13 +000024
25namespace perfetto {
26namespace base {
27namespace {
28
29class ThreadTaskRunnerTest : public ::testing::Test {
30 protected:
31 std::atomic<bool> atomic_flag_{false};
32};
33
34TEST_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
43TEST_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
64TEST_F(ThreadTaskRunnerTest, MovableOwnership) {
Ryan Savitski89658b32022-08-23 14:42:28 +010065 // Will destroy manually.
66 NoDestructor<ThreadTaskRunner> ttr{ThreadTaskRunner::CreateAndStart()};
67 ThreadTaskRunner& task_runner = ttr.ref();
68
Ryan Savitski29885792019-03-14 12:10:13 +000069 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.
102class DestructorThreadChecker {
103 public:
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
110 ~DestructorThreadChecker() { EXPECT_TRUE(checker_.CalledOnValidThread()); }
111
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.
120TEST_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