blob: 34a33da36d049d72b483e38cbd5879fa066dd69d [file] [log] [blame]
Jordan Baylesb0c191e2019-03-26 15:49:57 -07001// Copyright 2019 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Jordan Baylesa26582d2019-07-10 14:44:58 -07005#include "platform/impl/task_runner.h"
btolsch4051e722019-06-07 16:15:17 -07006
btolschd94fe622019-05-09 14:21:40 -07007#include <atomic>
Jordan Baylesb0c191e2019-03-26 15:49:57 -07008#include <thread> // NOLINT
9
Ryan Keane32c88d02019-07-02 18:46:14 -070010#include "gmock/gmock.h"
Jordan Bayles2d01f172019-06-07 11:11:50 -070011#include "gtest/gtest.h"
Jordan Baylesb0c191e2019-03-26 15:49:57 -070012#include "platform/api/time.h"
Jordan Bayles2d01f172019-06-07 11:11:50 -070013#include "platform/test/fake_clock.h"
Jordan Baylesb0c191e2019-03-26 15:49:57 -070014
btolschd94fe622019-05-09 14:21:40 -070015namespace openscreen {
16namespace platform {
Jordan Baylesb0c191e2019-03-26 15:49:57 -070017namespace {
btolschd94fe622019-05-09 14:21:40 -070018
Ryan Keane32c88d02019-07-02 18:46:14 -070019using namespace ::testing;
Jordan Baylesb0c191e2019-03-26 15:49:57 -070020using std::chrono::milliseconds;
Ryan Keane32c88d02019-07-02 18:46:14 -070021using ::testing::_;
Jordan Baylesb0c191e2019-03-26 15:49:57 -070022
23const auto kTaskRunnerSleepTime = milliseconds(1);
btolschd94fe622019-05-09 14:21:40 -070024constexpr Clock::duration kWaitTimeout = milliseconds(1000);
Jordan Baylesb0c191e2019-03-26 15:49:57 -070025
26void WaitUntilCondition(std::function<bool()> predicate) {
27 while (!predicate()) {
28 std::this_thread::sleep_for(kTaskRunnerSleepTime);
29 }
30}
Jordan Baylesb0c191e2019-03-26 15:49:57 -070031
btolschd94fe622019-05-09 14:21:40 -070032class FakeTaskWaiter final : public TaskRunnerImpl::TaskWaiter {
33 public:
34 explicit FakeTaskWaiter(platform::ClockNowFunctionPtr now_function)
35 : now_function_(now_function) {}
36 ~FakeTaskWaiter() override = default;
37
38 Error WaitForTaskToBePosted(Clock::duration timeout) override {
39 Clock::time_point start = now_function_();
40 waiting_.store(true);
41 while (!has_event_.load() && (now_function_() - start) < timeout) {
42 ;
43 }
44 waiting_.store(false);
45 has_event_.store(false);
46 return Error::None();
47 }
48
49 void OnTaskPosted() override { has_event_.store(true); }
50
51 void WakeUpAndStop() {
52 OnTaskPosted();
Yuri Wiitalac05ada22019-09-24 14:25:19 -070053 task_runner_->RequestStopSoon();
btolschd94fe622019-05-09 14:21:40 -070054 }
55
56 bool IsWaiting() const { return waiting_.load(); }
57
58 void SetTaskRunner(TaskRunnerImpl* task_runner) {
59 task_runner_ = task_runner;
60 }
61
62 private:
63 const platform::ClockNowFunctionPtr now_function_;
64 TaskRunnerImpl* task_runner_;
65 std::atomic<bool> has_event_{false};
66 std::atomic<bool> waiting_{false};
67};
68
69class TaskRunnerWithWaiterFactory {
70 public:
btolsch4051e722019-06-07 16:15:17 -070071 static std::unique_ptr<TaskRunnerImpl> Create(
btolschd94fe622019-05-09 14:21:40 -070072 platform::ClockNowFunctionPtr now_function) {
73 fake_waiter = std::make_unique<FakeTaskWaiter>(now_function);
74 auto runner = std::make_unique<TaskRunnerImpl>(
75 now_function, fake_waiter.get(), std::chrono::hours(1));
76 fake_waiter->SetTaskRunner(runner.get());
77 return runner;
78 }
79
80 static std::unique_ptr<FakeTaskWaiter> fake_waiter;
81};
82
83// static
84std::unique_ptr<FakeTaskWaiter> TaskRunnerWithWaiterFactory::fake_waiter;
85
86} // anonymous namespace
Jordan Baylesb0c191e2019-03-26 15:49:57 -070087
Yuri Wiitalac05ada22019-09-24 14:25:19 -070088TEST(TaskRunnerImplTest, TaskRunnerExecutesTaskAndStops) {
Jordan Bayles5d72bc22019-04-09 13:33:52 -070089 FakeClock fake_clock{platform::Clock::time_point(milliseconds(1337))};
Yuri Wiitalac05ada22019-09-24 14:25:19 -070090 TaskRunnerImpl runner(&fake_clock.now);
Jordan Baylesb0c191e2019-03-26 15:49:57 -070091
92 std::string ran_tasks = "";
Yuri Wiitalac05ada22019-09-24 14:25:19 -070093 runner.PostTask([&ran_tasks] { ran_tasks += "1"; });
94 runner.RequestStopSoon();
Jordan Baylesb0c191e2019-03-26 15:49:57 -070095
Yuri Wiitalac05ada22019-09-24 14:25:19 -070096 runner.RunUntilStopped();
Jordan Baylesb0c191e2019-03-26 15:49:57 -070097 EXPECT_EQ(ran_tasks, "1");
Jordan Baylesb0c191e2019-03-26 15:49:57 -070098}
99
btolsch4051e722019-06-07 16:15:17 -0700100TEST(TaskRunnerImplTest, TaskRunnerRunsDelayedTasksInOrder) {
Jordan Bayles5d72bc22019-04-09 13:33:52 -0700101 FakeClock fake_clock{platform::Clock::time_point(milliseconds(1337))};
btolsch4051e722019-06-07 16:15:17 -0700102 TaskRunnerImpl runner(&fake_clock.now);
Jordan Baylesb0c191e2019-03-26 15:49:57 -0700103
btolsch4051e722019-06-07 16:15:17 -0700104 std::thread t([&runner] { runner.RunUntilStopped(); });
Jordan Baylesb0c191e2019-03-26 15:49:57 -0700105
106 std::string ran_tasks = "";
107
Jordan Bayles5d72bc22019-04-09 13:33:52 -0700108 const auto kDelayTime = milliseconds(5);
Jordan Baylesb0c191e2019-03-26 15:49:57 -0700109 const auto task_one = [&ran_tasks] { ran_tasks += "1"; };
btolsch4051e722019-06-07 16:15:17 -0700110 runner.PostTaskWithDelay(task_one, kDelayTime);
Jordan Baylesb0c191e2019-03-26 15:49:57 -0700111
Jordan Baylesb0c191e2019-03-26 15:49:57 -0700112 const auto task_two = [&ran_tasks] { ran_tasks += "2"; };
btolsch4051e722019-06-07 16:15:17 -0700113 runner.PostTaskWithDelay(task_two, kDelayTime * 2);
Jordan Baylesb0c191e2019-03-26 15:49:57 -0700114
Jordan Bayles5d72bc22019-04-09 13:33:52 -0700115 EXPECT_EQ(ran_tasks, "");
116 fake_clock.Advance(kDelayTime);
Jordan Baylesb0c191e2019-03-26 15:49:57 -0700117 WaitUntilCondition([&ran_tasks] { return ran_tasks == "1"; });
118 EXPECT_EQ(ran_tasks, "1");
119
Jordan Bayles5d72bc22019-04-09 13:33:52 -0700120 fake_clock.Advance(kDelayTime);
Jordan Baylesb0c191e2019-03-26 15:49:57 -0700121 WaitUntilCondition([&ran_tasks] { return ran_tasks == "12"; });
122 EXPECT_EQ(ran_tasks, "12");
123
btolsch4051e722019-06-07 16:15:17 -0700124 runner.RequestStopSoon();
Jordan Baylesb0c191e2019-03-26 15:49:57 -0700125 t.join();
126}
127
btolsch4051e722019-06-07 16:15:17 -0700128TEST(TaskRunnerImplTest, SingleThreadedTaskRunnerRunsSequentially) {
Jordan Bayles5d72bc22019-04-09 13:33:52 -0700129 FakeClock fake_clock{platform::Clock::time_point(milliseconds(1337))};
btolsch4051e722019-06-07 16:15:17 -0700130 TaskRunnerImpl runner(&fake_clock.now);
Jordan Baylesb0c191e2019-03-26 15:49:57 -0700131
132 std::string ran_tasks;
133 const auto task_one = [&ran_tasks] { ran_tasks += "1"; };
134 const auto task_two = [&ran_tasks] { ran_tasks += "2"; };
135 const auto task_three = [&ran_tasks] { ran_tasks += "3"; };
136 const auto task_four = [&ran_tasks] { ran_tasks += "4"; };
137 const auto task_five = [&ran_tasks] { ran_tasks += "5"; };
138
139 runner.PostTask(task_one);
140 runner.PostTask(task_two);
141 runner.PostTask(task_three);
142 runner.PostTask(task_four);
143 runner.PostTask(task_five);
Yuri Wiitalac05ada22019-09-24 14:25:19 -0700144 runner.RequestStopSoon();
Jordan Baylesb0c191e2019-03-26 15:49:57 -0700145 EXPECT_EQ(ran_tasks, "");
146
Yuri Wiitalac05ada22019-09-24 14:25:19 -0700147 runner.RunUntilStopped();
Jordan Baylesb0c191e2019-03-26 15:49:57 -0700148 EXPECT_EQ(ran_tasks, "12345");
149}
150
Yuri Wiitalac05ada22019-09-24 14:25:19 -0700151TEST(TaskRunnerImplTest, RunsAllImmediateTasksBeforeStopping) {
Jordan Bayles5d72bc22019-04-09 13:33:52 -0700152 FakeClock fake_clock{platform::Clock::time_point(milliseconds(1337))};
btolsch4051e722019-06-07 16:15:17 -0700153 TaskRunnerImpl runner(&fake_clock.now);
Jordan Baylesb0c191e2019-03-26 15:49:57 -0700154
Yuri Wiitalac05ada22019-09-24 14:25:19 -0700155 std::string result;
156 runner.PostTask([&] {
157 result += "Alice";
Jordan Baylesb0c191e2019-03-26 15:49:57 -0700158
Yuri Wiitalac05ada22019-09-24 14:25:19 -0700159 // Post a task that runs just before the quit task.
160 runner.PostTask([&] {
161 result += " says goodbye";
Jordan Baylesb0c191e2019-03-26 15:49:57 -0700162
Yuri Wiitalac05ada22019-09-24 14:25:19 -0700163 // These tasks will enter the queue after the quit task *and* after the
164 // main loop breaks. They will be executed by the flushing phase.
165 runner.PostTask([&] {
166 result += " and is not";
167 runner.PostTask([&] { result += " forgotten."; });
168 });
169 });
Jordan Baylesb0c191e2019-03-26 15:49:57 -0700170
Yuri Wiitalac05ada22019-09-24 14:25:19 -0700171 // Post the quit task.
172 runner.RequestStopSoon();
173 });
Jordan Baylesb0c191e2019-03-26 15:49:57 -0700174
Yuri Wiitalac05ada22019-09-24 14:25:19 -0700175 EXPECT_EQ(result, "");
176 runner.RunUntilStopped();
177 // All posted tasks will execute because RequestStopSoon() guarantees all
178 // immediately-runnable tasks will run before exiting, even if new
179 // immediately-runnable tasks are posted in the meantime.
180 EXPECT_EQ(result, "Alice says goodbye and is not forgotten.");
Jordan Baylesb0c191e2019-03-26 15:49:57 -0700181}
182
btolsch4051e722019-06-07 16:15:17 -0700183TEST(TaskRunnerImplTest, TaskRunnerIsStableWithLotsOfTasks) {
Jordan Bayles5d72bc22019-04-09 13:33:52 -0700184 FakeClock fake_clock{platform::Clock::time_point(milliseconds(1337))};
btolsch4051e722019-06-07 16:15:17 -0700185 TaskRunnerImpl runner(&fake_clock.now);
Jordan Baylesb0c191e2019-03-26 15:49:57 -0700186
187 const int kNumberOfTasks = 500;
188 std::string expected_ran_tasks;
189 expected_ran_tasks.append(kNumberOfTasks, '1');
190
191 std::string ran_tasks;
192 for (int i = 0; i < kNumberOfTasks; ++i) {
193 const auto task = [&ran_tasks] { ran_tasks += "1"; };
194 runner.PostTask(task);
195 }
196
Yuri Wiitalac05ada22019-09-24 14:25:19 -0700197 runner.RequestStopSoon();
198 runner.RunUntilStopped();
Jordan Baylesb0c191e2019-03-26 15:49:57 -0700199 EXPECT_EQ(ran_tasks, expected_ran_tasks);
200}
Jordan Baylesa8e96772019-04-08 10:53:54 -0700201
btolsch4051e722019-06-07 16:15:17 -0700202TEST(TaskRunnerImplTest, TaskRunnerDelayedTasksDontBlockImmediateTasks) {
Jordan Baylesa8e96772019-04-08 10:53:54 -0700203 TaskRunnerImpl runner(platform::Clock::now);
204
205 std::string ran_tasks;
206 const auto task = [&ran_tasks] { ran_tasks += "1"; };
207 const auto delayed_task = [&ran_tasks] { ran_tasks += "A"; };
208
209 runner.PostTaskWithDelay(delayed_task, milliseconds(10000));
210 runner.PostTask(task);
211
Yuri Wiitalac05ada22019-09-24 14:25:19 -0700212 runner.RequestStopSoon();
213 runner.RunUntilStopped();
Jordan Baylesa8e96772019-04-08 10:53:54 -0700214 // The immediate task should have run, even though the delayed task
215 // was added first.
216
217 EXPECT_EQ(ran_tasks, "1");
218}
btolschd94fe622019-05-09 14:21:40 -0700219
btolsch4051e722019-06-07 16:15:17 -0700220TEST(TaskRunnerImplTest, TaskRunnerUsesEventWaiter) {
221 std::unique_ptr<TaskRunnerImpl> runner =
btolschd94fe622019-05-09 14:21:40 -0700222 TaskRunnerWithWaiterFactory::Create(Clock::now);
223
btolsch691996a2019-07-30 18:52:43 -0700224 std::atomic<int> x{0};
btolschd94fe622019-05-09 14:21:40 -0700225 std::thread t([&runner, &x] {
btolsch4051e722019-06-07 16:15:17 -0700226 runner.get()->RunUntilStopped();
btolschd94fe622019-05-09 14:21:40 -0700227 x = 1;
228 });
229
230 const Clock::time_point start1 = Clock::now();
231 FakeTaskWaiter* fake_waiter = TaskRunnerWithWaiterFactory::fake_waiter.get();
232 while ((Clock::now() - start1) < kWaitTimeout && !fake_waiter->IsWaiting()) {
233 std::this_thread::sleep_for(kTaskRunnerSleepTime);
234 }
235 ASSERT_TRUE(fake_waiter->IsWaiting());
236
237 fake_waiter->WakeUpAndStop();
238 const Clock::time_point start2 = Clock::now();
239 while ((Clock::now() - start2) < kWaitTimeout && x == 0) {
240 std::this_thread::sleep_for(kTaskRunnerSleepTime);
241 }
242 ASSERT_EQ(x, 1);
243 ASSERT_FALSE(fake_waiter->IsWaiting());
244 t.join();
245}
246
btolsch4051e722019-06-07 16:15:17 -0700247TEST(TaskRunnerImplTest, WakesEventWaiterOnPostTask) {
248 std::unique_ptr<TaskRunnerImpl> runner =
btolschd94fe622019-05-09 14:21:40 -0700249 TaskRunnerWithWaiterFactory::Create(Clock::now);
250
btolsch691996a2019-07-30 18:52:43 -0700251 std::atomic<int> x{0};
btolsch4051e722019-06-07 16:15:17 -0700252 std::thread t([&runner] { runner.get()->RunUntilStopped(); });
btolschd94fe622019-05-09 14:21:40 -0700253
254 const Clock::time_point start1 = Clock::now();
255 FakeTaskWaiter* fake_waiter = TaskRunnerWithWaiterFactory::fake_waiter.get();
256 while ((Clock::now() - start1) < kWaitTimeout && !fake_waiter->IsWaiting()) {
257 std::this_thread::sleep_for(kTaskRunnerSleepTime);
258 }
259 ASSERT_TRUE(fake_waiter->IsWaiting());
260
261 runner->PostTask([&x]() { x = 1; });
262 const Clock::time_point start2 = Clock::now();
263 while ((Clock::now() - start2) < kWaitTimeout && x == 0) {
264 std::this_thread::sleep_for(kTaskRunnerSleepTime);
265 }
266 ASSERT_EQ(x, 1);
267
268 fake_waiter->WakeUpAndStop();
269 t.join();
270}
271
Ryan Keane32c88d02019-07-02 18:46:14 -0700272class RepeatedClass {
273 public:
Ryan Keane32c88d02019-07-02 18:46:14 -0700274 MOCK_METHOD0(Repeat, absl::optional<Clock::duration>());
275
276 absl::optional<Clock::duration> DoCall() {
277 auto result = Repeat();
278 execution_count++;
279 return result;
280 }
281
btolsch691996a2019-07-30 18:52:43 -0700282 std::atomic<int> execution_count{0};
Ryan Keane32c88d02019-07-02 18:46:14 -0700283};
284
285TEST(TaskRunnerImplTest, RepeatingFunctionCalledRepeatedly) {
286 std::unique_ptr<TaskRunnerImpl> runner =
287 TaskRunnerWithWaiterFactory::Create(Clock::now);
288
289 std::thread running_thread([&runner]() { runner.get()->RunUntilStopped(); });
290
291 RepeatedClass c;
292 EXPECT_CALL(c, Repeat())
293 .Times(3)
294 .WillOnce(Return(Clock::duration(0)))
295 .WillOnce(Return(Clock::duration(1)))
296 .WillOnce(Return(absl::nullopt));
297
298 RepeatingFunction::Post(runner.get(), [&c]() { return c.DoCall(); });
299 const Clock::time_point start2 = Clock::now();
300 while ((Clock::now() - start2) < kWaitTimeout && c.execution_count < 3) {
301 std::this_thread::sleep_for(kTaskRunnerSleepTime);
302 }
303 ASSERT_EQ(c.execution_count, 3);
304
305 runner->RequestStopSoon();
306 running_thread.join();
307}
308
Jordan Baylesb0c191e2019-03-26 15:49:57 -0700309} // namespace platform
310} // namespace openscreen