blob: 0e7f2af89e8b3193581543866d35debc4f161dd8 [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
Primiano Tucci2c5488f2019-06-01 03:27:28 +010021#include "perfetto/ext/base/thread_checker.h"
Primiano Tucci919ca1e2019-08-21 20:26:58 +020022#include "test/gtest_and_gmock.h"
Ryan Savitski29885792019-03-14 12:10:13 +000023
24namespace perfetto {
25namespace base {
26namespace {
27
28class ThreadTaskRunnerTest : public ::testing::Test {
29 protected:
30 std::atomic<bool> atomic_flag_{false};
31};
32
33TEST_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
42TEST_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
63TEST_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.
98class DestructorThreadChecker {
99 public:
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
106 ~DestructorThreadChecker() { EXPECT_TRUE(checker_.CalledOnValidThread()); }
107
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.
116TEST_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