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,const base::Closure & task,base::TimeDelta delay)16 MessageLoop::TaskId FakeMessageLoop::PostDelayedTask(
17 const base::Location& from_here,
18 const base::Closure& 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, false, 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
WatchFileDescriptor(const base::Location & from_here,int fd,WatchMode mode,bool persistent,const base::Closure & task)36 MessageLoop::TaskId FakeMessageLoop::WatchFileDescriptor(
37 const base::Location& from_here,
38 int fd,
39 WatchMode mode,
40 bool persistent,
41 const base::Closure& task) {
42 MessageLoop::TaskId current_id = ++last_id_;
43 // FakeMessageLoop is limited to only 2^64 tasks. That should be enough.
44 CHECK(current_id);
45 tasks_.emplace(current_id, ScheduledTask{from_here, persistent, task});
46 fds_watched_.emplace(std::make_pair(fd, mode), current_id);
47 return current_id;
48 }
49
CancelTask(TaskId task_id)50 bool FakeMessageLoop::CancelTask(TaskId task_id) {
51 if (task_id == MessageLoop::kTaskIdNull)
52 return false;
53 bool ret = tasks_.erase(task_id) > 0;
54 VLOG_IF(1, ret) << "Removing task_id " << task_id;
55 return ret;
56 }
57
RunOnce(bool may_block)58 bool FakeMessageLoop::RunOnce(bool may_block) {
59 if (test_clock_)
60 current_time_ = test_clock_->Now();
61 // Try to fire ready file descriptors first.
62 for (const auto& fd_mode : fds_ready_) {
63 const auto& fd_watched = fds_watched_.find(fd_mode);
64 if (fd_watched == fds_watched_.end())
65 continue;
66 // The fd_watched->second task might have been canceled and we never removed
67 // it from the fds_watched_, so we fix that now.
68 const auto& scheduled_task_ref = tasks_.find(fd_watched->second);
69 if (scheduled_task_ref == tasks_.end()) {
70 fds_watched_.erase(fd_watched);
71 continue;
72 }
73 VLOG_LOC(scheduled_task_ref->second.location, 1)
74 << "Running task_id " << fd_watched->second
75 << " for watching file descriptor " << fd_mode.first << " for "
76 << (fd_mode.second == MessageLoop::kWatchRead ? "reading" : "writing")
77 << (scheduled_task_ref->second.persistent ?
78 " persistently" : " just once")
79 << " scheduled from this location.";
80 if (scheduled_task_ref->second.persistent) {
81 scheduled_task_ref->second.callback.Run();
82 } else {
83 base::Closure callback = std::move(scheduled_task_ref->second.callback);
84 tasks_.erase(scheduled_task_ref);
85 fds_watched_.erase(fd_watched);
86 callback.Run();
87 }
88 return true;
89 }
90
91 // Try to fire time-based callbacks afterwards.
92 while (!fire_order_.empty() &&
93 (may_block || fire_order_.top().first <= current_time_)) {
94 const auto task_ref = fire_order_.top();
95 fire_order_.pop();
96 // We need to skip tasks in the priority_queue not in the |tasks_| map.
97 // This is normal if the task was canceled, as there is no efficient way
98 // to remove a task from the priority_queue.
99 const auto scheduled_task_ref = tasks_.find(task_ref.second);
100 if (scheduled_task_ref == tasks_.end())
101 continue;
102 // Advance the clock to the task firing time, if needed.
103 if (current_time_ < task_ref.first) {
104 current_time_ = task_ref.first;
105 if (test_clock_)
106 test_clock_->SetNow(current_time_);
107 }
108 // Move the Closure out of the map before delete it. We need to delete the
109 // entry from the map before we call the callback, since calling CancelTask
110 // for the task you are running now should fail and return false.
111 base::Closure callback = std::move(scheduled_task_ref->second.callback);
112 VLOG_LOC(scheduled_task_ref->second.location, 1)
113 << "Running task_id " << task_ref.second
114 << " at time " << current_time_ << " from this location.";
115 tasks_.erase(scheduled_task_ref);
116
117 callback.Run();
118 return true;
119 }
120 return false;
121 }
122
SetFileDescriptorReadiness(int fd,WatchMode mode,bool ready)123 void FakeMessageLoop::SetFileDescriptorReadiness(int fd,
124 WatchMode mode,
125 bool ready) {
126 if (ready)
127 fds_ready_.emplace(fd, mode);
128 else
129 fds_ready_.erase(std::make_pair(fd, mode));
130 }
131
PendingTasks()132 bool FakeMessageLoop::PendingTasks() {
133 for (const auto& task : tasks_) {
134 VLOG_LOC(task.second.location, 1)
135 << "Pending " << (task.second.persistent ? "persistent " : "")
136 << "task_id " << task.first << " scheduled from here.";
137 }
138 return !tasks_.empty();
139 }
140
141 } // namespace brillo
142