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