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 tracked_objects::Location & from_here,const base::Closure & task,base::TimeDelta delay)16 MessageLoop::TaskId FakeMessageLoop::PostDelayedTask(
17     const tracked_objects::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 tracked_objects::Location & from_here,int fd,WatchMode mode,bool persistent,const base::Closure & task)36 MessageLoop::TaskId FakeMessageLoop::WatchFileDescriptor(
37     const tracked_objects::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