1 // Copyright 2018 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
5 #include "platform/test/fake_clock.h"
6
7 #include <algorithm>
8
9 #include "platform/test/fake_task_runner.h"
10 #include "util/osp_logging.h"
11
12 namespace openscreen {
13
14 namespace {
15 constexpr Clock::time_point kInvalid = Clock::time_point::min();
16 }
17
FakeClock(Clock::time_point start_time)18 FakeClock::FakeClock(Clock::time_point start_time)
19 : control_thread_id_(std::this_thread::get_id()) {
20 OSP_CHECK_EQ(now_.load(std::memory_order_acquire), kInvalid)
21 << "Only one FakeClock instance allowed!";
22 now_.store(start_time, std::memory_order_release);
23 }
24
~FakeClock()25 FakeClock::~FakeClock() {
26 OSP_CHECK_EQ(std::this_thread::get_id(), control_thread_id_);
27 OSP_CHECK(task_runners_.empty());
28 // Set |now_| to kInvalid to flag that this FakeClock has been destroyed.
29 now_.store(kInvalid, std::memory_order_release);
30 }
31
now()32 Clock::time_point FakeClock::now() noexcept {
33 const Clock::time_point value = now_.load(std::memory_order_acquire);
34 OSP_CHECK_NE(value, kInvalid) << "No FakeClock instance!";
35 return value;
36 }
37
Advance(Clock::duration delta)38 void FakeClock::Advance(Clock::duration delta) {
39 OSP_CHECK_EQ(std::this_thread::get_id(), control_thread_id_);
40
41 const Clock::time_point stop_time = now() + delta;
42
43 for (;;) {
44 // Run tasks at the current time, since this might cause additional delayed
45 // tasks to be posted.
46 for (FakeTaskRunner* task_runner : task_runners_) {
47 task_runner->RunTasksUntilIdle();
48 }
49
50 // Find the next "step-to" time, and advance the clock to that point.
51 Clock::time_point step_to = Clock::time_point::max();
52 for (FakeTaskRunner* task_runner : task_runners_) {
53 step_to = std::min(step_to, task_runner->GetResumeTime());
54 }
55 if (step_to > stop_time) {
56 break; // No tasks are scheduled for the remaining time range.
57 }
58
59 OSP_DCHECK_GT(step_to, now());
60 now_.store(step_to, std::memory_order_release);
61 }
62
63 // Skip over any remaining "dead time."
64 now_.store(stop_time, std::memory_order_release);
65 }
66
SubscribeToTimeChanges(FakeTaskRunner * task_runner)67 void FakeClock::SubscribeToTimeChanges(FakeTaskRunner* task_runner) {
68 OSP_CHECK_EQ(std::this_thread::get_id(), control_thread_id_);
69 OSP_CHECK(std::find(task_runners_.begin(), task_runners_.end(),
70 task_runner) == task_runners_.end());
71 task_runners_.push_back(task_runner);
72 }
73
UnsubscribeFromTimeChanges(FakeTaskRunner * task_runner)74 void FakeClock::UnsubscribeFromTimeChanges(FakeTaskRunner* task_runner) {
75 OSP_CHECK_EQ(std::this_thread::get_id(), control_thread_id_);
76 auto it = std::find(task_runners_.begin(), task_runners_.end(), task_runner);
77 OSP_CHECK(it != task_runners_.end());
78 task_runners_.erase(it);
79 }
80
81 // static
82 std::atomic<Clock::time_point> FakeClock::now_{kInvalid};
83
84 } // namespace openscreen
85