blob: b8d582cbdb3f1fb9a4c3b1bfb0aa1dd4e3cf774e [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();
53 task_runner_->PostTask([this]() { task_runner_->RequestStopSoon(); });
54 }
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
btolsch4051e722019-06-07 16:15:17 -070088TEST(TaskRunnerImplTest, TaskRunnerExecutesTask) {
Jordan Bayles5d72bc22019-04-09 13:33:52 -070089 FakeClock fake_clock{platform::Clock::time_point(milliseconds(1337))};
btolsch4051e722019-06-07 16:15:17 -070090 auto runner = std::make_unique<TaskRunnerImpl>(&fake_clock.now);
Jordan Baylesb0c191e2019-03-26 15:49:57 -070091
btolsch4051e722019-06-07 16:15:17 -070092 std::thread t([&runner] { runner.get()->RunUntilStopped(); });
Jordan Baylesb0c191e2019-03-26 15:49:57 -070093
94 std::string ran_tasks = "";
95 const auto task = [&ran_tasks] { ran_tasks += "1"; };
96 EXPECT_EQ(ran_tasks, "");
97
98 runner->PostTask(task);
99
100 WaitUntilCondition([&ran_tasks] { return ran_tasks == "1"; });
101 EXPECT_EQ(ran_tasks, "1");
102
btolsch4051e722019-06-07 16:15:17 -0700103 runner.get()->RequestStopSoon();
Jordan Baylesb0c191e2019-03-26 15:49:57 -0700104 t.join();
105}
106
btolsch4051e722019-06-07 16:15:17 -0700107TEST(TaskRunnerImplTest, TaskRunnerRunsDelayedTasksInOrder) {
Jordan Bayles5d72bc22019-04-09 13:33:52 -0700108 FakeClock fake_clock{platform::Clock::time_point(milliseconds(1337))};
btolsch4051e722019-06-07 16:15:17 -0700109 TaskRunnerImpl runner(&fake_clock.now);
Jordan Baylesb0c191e2019-03-26 15:49:57 -0700110
btolsch4051e722019-06-07 16:15:17 -0700111 std::thread t([&runner] { runner.RunUntilStopped(); });
Jordan Baylesb0c191e2019-03-26 15:49:57 -0700112
113 std::string ran_tasks = "";
114
Jordan Bayles5d72bc22019-04-09 13:33:52 -0700115 const auto kDelayTime = milliseconds(5);
Jordan Baylesb0c191e2019-03-26 15:49:57 -0700116 const auto task_one = [&ran_tasks] { ran_tasks += "1"; };
btolsch4051e722019-06-07 16:15:17 -0700117 runner.PostTaskWithDelay(task_one, kDelayTime);
Jordan Baylesb0c191e2019-03-26 15:49:57 -0700118
Jordan Baylesb0c191e2019-03-26 15:49:57 -0700119 const auto task_two = [&ran_tasks] { ran_tasks += "2"; };
btolsch4051e722019-06-07 16:15:17 -0700120 runner.PostTaskWithDelay(task_two, kDelayTime * 2);
Jordan Baylesb0c191e2019-03-26 15:49:57 -0700121
Jordan Bayles5d72bc22019-04-09 13:33:52 -0700122 EXPECT_EQ(ran_tasks, "");
123 fake_clock.Advance(kDelayTime);
Jordan Baylesb0c191e2019-03-26 15:49:57 -0700124 WaitUntilCondition([&ran_tasks] { return ran_tasks == "1"; });
125 EXPECT_EQ(ran_tasks, "1");
126
Jordan Bayles5d72bc22019-04-09 13:33:52 -0700127 fake_clock.Advance(kDelayTime);
Jordan Baylesb0c191e2019-03-26 15:49:57 -0700128 WaitUntilCondition([&ran_tasks] { return ran_tasks == "12"; });
129 EXPECT_EQ(ran_tasks, "12");
130
btolsch4051e722019-06-07 16:15:17 -0700131 runner.RequestStopSoon();
Jordan Baylesb0c191e2019-03-26 15:49:57 -0700132 t.join();
133}
134
btolsch4051e722019-06-07 16:15:17 -0700135TEST(TaskRunnerImplTest, SingleThreadedTaskRunnerRunsSequentially) {
Jordan Bayles5d72bc22019-04-09 13:33:52 -0700136 FakeClock fake_clock{platform::Clock::time_point(milliseconds(1337))};
btolsch4051e722019-06-07 16:15:17 -0700137 TaskRunnerImpl runner(&fake_clock.now);
Jordan Baylesb0c191e2019-03-26 15:49:57 -0700138
139 std::string ran_tasks;
140 const auto task_one = [&ran_tasks] { ran_tasks += "1"; };
141 const auto task_two = [&ran_tasks] { ran_tasks += "2"; };
142 const auto task_three = [&ran_tasks] { ran_tasks += "3"; };
143 const auto task_four = [&ran_tasks] { ran_tasks += "4"; };
144 const auto task_five = [&ran_tasks] { ran_tasks += "5"; };
145
146 runner.PostTask(task_one);
147 runner.PostTask(task_two);
148 runner.PostTask(task_three);
149 runner.PostTask(task_four);
150 runner.PostTask(task_five);
151 EXPECT_EQ(ran_tasks, "");
152
153 runner.RunUntilIdleForTesting();
154 EXPECT_EQ(ran_tasks, "12345");
155}
156
btolsch4051e722019-06-07 16:15:17 -0700157TEST(TaskRunnerImplTest, TaskRunnerCanStopRunning) {
Jordan Bayles5d72bc22019-04-09 13:33:52 -0700158 FakeClock fake_clock{platform::Clock::time_point(milliseconds(1337))};
btolsch4051e722019-06-07 16:15:17 -0700159 TaskRunnerImpl runner(&fake_clock.now);
Jordan Baylesb0c191e2019-03-26 15:49:57 -0700160
161 std::string ran_tasks;
162 const auto task_one = [&ran_tasks] { ran_tasks += "1"; };
163 const auto task_two = [&ran_tasks] { ran_tasks += "2"; };
164
165 runner.PostTask(task_one);
166 EXPECT_EQ(ran_tasks, "");
167
168 std::thread start_thread([&runner] { runner.RunUntilStopped(); });
169
170 WaitUntilCondition([&ran_tasks] { return !ran_tasks.empty(); });
171 EXPECT_EQ(ran_tasks, "1");
172
173 // Since Stop is called first, and the single threaded task
174 // runner should honor the queue, we know the task runner is not running
175 // since task two doesn't get ran.
176 runner.RequestStopSoon();
177 runner.PostTask(task_two);
178 EXPECT_EQ(ran_tasks, "1");
179
180 start_thread.join();
181}
182
btolsch4051e722019-06-07 16:15:17 -0700183TEST(TaskRunnerImplTest, StoppingDoesNotDeleteTasks) {
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 std::string ran_tasks;
188 const auto task_one = [&ran_tasks] { ran_tasks += "1"; };
189
190 runner.PostTask(task_one);
191 runner.RequestStopSoon();
192
193 EXPECT_EQ(ran_tasks, "");
194 runner.RunUntilIdleForTesting();
195
196 EXPECT_EQ(ran_tasks, "1");
197}
198
btolsch4051e722019-06-07 16:15:17 -0700199TEST(TaskRunnerImplTest, TaskRunnerIsStableWithLotsOfTasks) {
Jordan Bayles5d72bc22019-04-09 13:33:52 -0700200 FakeClock fake_clock{platform::Clock::time_point(milliseconds(1337))};
btolsch4051e722019-06-07 16:15:17 -0700201 TaskRunnerImpl runner(&fake_clock.now);
Jordan Baylesb0c191e2019-03-26 15:49:57 -0700202
203 const int kNumberOfTasks = 500;
204 std::string expected_ran_tasks;
205 expected_ran_tasks.append(kNumberOfTasks, '1');
206
207 std::string ran_tasks;
208 for (int i = 0; i < kNumberOfTasks; ++i) {
209 const auto task = [&ran_tasks] { ran_tasks += "1"; };
210 runner.PostTask(task);
211 }
212
213 runner.RunUntilIdleForTesting();
214 EXPECT_EQ(ran_tasks, expected_ran_tasks);
215}
Jordan Baylesa8e96772019-04-08 10:53:54 -0700216
btolsch4051e722019-06-07 16:15:17 -0700217TEST(TaskRunnerImplTest, TaskRunnerDelayedTasksDontBlockImmediateTasks) {
Jordan Baylesa8e96772019-04-08 10:53:54 -0700218 TaskRunnerImpl runner(platform::Clock::now);
219
220 std::string ran_tasks;
221 const auto task = [&ran_tasks] { ran_tasks += "1"; };
222 const auto delayed_task = [&ran_tasks] { ran_tasks += "A"; };
223
224 runner.PostTaskWithDelay(delayed_task, milliseconds(10000));
225 runner.PostTask(task);
226
227 runner.RunUntilIdleForTesting();
228 // The immediate task should have run, even though the delayed task
229 // was added first.
230
231 EXPECT_EQ(ran_tasks, "1");
232}
btolschd94fe622019-05-09 14:21:40 -0700233
btolsch4051e722019-06-07 16:15:17 -0700234TEST(TaskRunnerImplTest, TaskRunnerUsesEventWaiter) {
235 std::unique_ptr<TaskRunnerImpl> runner =
btolschd94fe622019-05-09 14:21:40 -0700236 TaskRunnerWithWaiterFactory::Create(Clock::now);
237
btolsch691996a2019-07-30 18:52:43 -0700238 std::atomic<int> x{0};
btolschd94fe622019-05-09 14:21:40 -0700239 std::thread t([&runner, &x] {
btolsch4051e722019-06-07 16:15:17 -0700240 runner.get()->RunUntilStopped();
btolschd94fe622019-05-09 14:21:40 -0700241 x = 1;
242 });
243
244 const Clock::time_point start1 = Clock::now();
245 FakeTaskWaiter* fake_waiter = TaskRunnerWithWaiterFactory::fake_waiter.get();
246 while ((Clock::now() - start1) < kWaitTimeout && !fake_waiter->IsWaiting()) {
247 std::this_thread::sleep_for(kTaskRunnerSleepTime);
248 }
249 ASSERT_TRUE(fake_waiter->IsWaiting());
250
251 fake_waiter->WakeUpAndStop();
252 const Clock::time_point start2 = Clock::now();
253 while ((Clock::now() - start2) < kWaitTimeout && x == 0) {
254 std::this_thread::sleep_for(kTaskRunnerSleepTime);
255 }
256 ASSERT_EQ(x, 1);
257 ASSERT_FALSE(fake_waiter->IsWaiting());
258 t.join();
259}
260
btolsch4051e722019-06-07 16:15:17 -0700261TEST(TaskRunnerImplTest, WakesEventWaiterOnPostTask) {
262 std::unique_ptr<TaskRunnerImpl> runner =
btolschd94fe622019-05-09 14:21:40 -0700263 TaskRunnerWithWaiterFactory::Create(Clock::now);
264
btolsch691996a2019-07-30 18:52:43 -0700265 std::atomic<int> x{0};
btolsch4051e722019-06-07 16:15:17 -0700266 std::thread t([&runner] { runner.get()->RunUntilStopped(); });
btolschd94fe622019-05-09 14:21:40 -0700267
268 const Clock::time_point start1 = Clock::now();
269 FakeTaskWaiter* fake_waiter = TaskRunnerWithWaiterFactory::fake_waiter.get();
270 while ((Clock::now() - start1) < kWaitTimeout && !fake_waiter->IsWaiting()) {
271 std::this_thread::sleep_for(kTaskRunnerSleepTime);
272 }
273 ASSERT_TRUE(fake_waiter->IsWaiting());
274
275 runner->PostTask([&x]() { x = 1; });
276 const Clock::time_point start2 = Clock::now();
277 while ((Clock::now() - start2) < kWaitTimeout && x == 0) {
278 std::this_thread::sleep_for(kTaskRunnerSleepTime);
279 }
280 ASSERT_EQ(x, 1);
281
282 fake_waiter->WakeUpAndStop();
283 t.join();
284}
285
Ryan Keane32c88d02019-07-02 18:46:14 -0700286class RepeatedClass {
287 public:
Ryan Keane32c88d02019-07-02 18:46:14 -0700288 MOCK_METHOD0(Repeat, absl::optional<Clock::duration>());
289
290 absl::optional<Clock::duration> DoCall() {
291 auto result = Repeat();
292 execution_count++;
293 return result;
294 }
295
btolsch691996a2019-07-30 18:52:43 -0700296 std::atomic<int> execution_count{0};
Ryan Keane32c88d02019-07-02 18:46:14 -0700297};
298
299TEST(TaskRunnerImplTest, RepeatingFunctionCalledRepeatedly) {
300 std::unique_ptr<TaskRunnerImpl> runner =
301 TaskRunnerWithWaiterFactory::Create(Clock::now);
302
303 std::thread running_thread([&runner]() { runner.get()->RunUntilStopped(); });
304
305 RepeatedClass c;
306 EXPECT_CALL(c, Repeat())
307 .Times(3)
308 .WillOnce(Return(Clock::duration(0)))
309 .WillOnce(Return(Clock::duration(1)))
310 .WillOnce(Return(absl::nullopt));
311
312 RepeatingFunction::Post(runner.get(), [&c]() { return c.DoCall(); });
313 const Clock::time_point start2 = Clock::now();
314 while ((Clock::now() - start2) < kWaitTimeout && c.execution_count < 3) {
315 std::this_thread::sleep_for(kTaskRunnerSleepTime);
316 }
317 ASSERT_EQ(c.execution_count, 3);
318
319 runner->RequestStopSoon();
320 running_thread.join();
321}
322
Jordan Baylesb0c191e2019-03-26 15:49:57 -0700323} // namespace platform
324} // namespace openscreen