1 // Copyright 2015 The Chromium OS 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 <brillo/message_loops/fake_message_loop.h>
6
7 #include <base/logging.h>
8 #include <brillo/location_logging.h>
9
10 namespace brillo {
11
FakeMessageLoop(base::SimpleTestClock * clock)12 FakeMessageLoop::FakeMessageLoop(base::SimpleTestClock* clock)
13 : test_clock_(clock) {
14 }
15
PostDelayedTask(const base::Location & from_here,base::OnceClosure task,base::TimeDelta delay)16 MessageLoop::TaskId FakeMessageLoop::PostDelayedTask(
17 const base::Location& from_here,
18 base::OnceClosure task,
19 base::TimeDelta delay) {
20 // If no SimpleTestClock was provided, we use the last time we fired a
21 // callback. In this way, tasks scheduled from a Closure will have the right
22 // time.
23 if (test_clock_)
24 current_time_ = test_clock_->Now();
25 MessageLoop::TaskId current_id = ++last_id_;
26 // FakeMessageLoop is limited to only 2^64 tasks. That should be enough.
27 CHECK(current_id);
28 tasks_.emplace(current_id, ScheduledTask{from_here, std::move(task)});
29 fire_order_.push(std::make_pair(current_time_ + delay, current_id));
30 VLOG_LOC(from_here, 1) << "Scheduling delayed task_id " << current_id
31 << " to run at " << current_time_ + delay
32 << " (in " << delay << ").";
33 return current_id;
34 }
35
CancelTask(TaskId task_id)36 bool FakeMessageLoop::CancelTask(TaskId task_id) {
37 if (task_id == MessageLoop::kTaskIdNull)
38 return false;
39 bool ret = tasks_.erase(task_id) > 0;
40 VLOG_IF(1, ret) << "Removing task_id " << task_id;
41 return ret;
42 }
43
RunOnce(bool may_block)44 bool FakeMessageLoop::RunOnce(bool may_block) {
45 if (test_clock_)
46 current_time_ = test_clock_->Now();
47 // Try to fire time-based callbacks afterwards.
48 while (!fire_order_.empty() &&
49 (may_block || fire_order_.top().first <= current_time_)) {
50 const auto task_ref = fire_order_.top();
51 fire_order_.pop();
52 // We need to skip tasks in the priority_queue not in the |tasks_| map.
53 // This is normal if the task was canceled, as there is no efficient way
54 // to remove a task from the priority_queue.
55 const auto scheduled_task_ref = tasks_.find(task_ref.second);
56 if (scheduled_task_ref == tasks_.end())
57 continue;
58 // Advance the clock to the task firing time, if needed.
59 if (current_time_ < task_ref.first) {
60 current_time_ = task_ref.first;
61 if (test_clock_)
62 test_clock_->SetNow(current_time_);
63 }
64 // Move the Closure out of the map before delete it. We need to delete the
65 // entry from the map before we call the callback, since calling CancelTask
66 // for the task you are running now should fail and return false.
67 base::OnceClosure callback = std::move(scheduled_task_ref->second.callback);
68 VLOG_LOC(scheduled_task_ref->second.location, 1)
69 << "Running task_id " << task_ref.second
70 << " at time " << current_time_ << " from this location.";
71 tasks_.erase(scheduled_task_ref);
72
73 std::move(callback).Run();
74 return true;
75 }
76 return false;
77 }
78
PendingTasks()79 bool FakeMessageLoop::PendingTasks() {
80 for (const auto& task : tasks_) {
81 VLOG_LOC(task.second.location, 1)
82 << "Pending task_id " << task.first << " scheduled from here.";
83 }
84 return !tasks_.empty();
85 }
86
87 } // namespace brillo
88