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 12 FakeMessageLoop::FakeMessageLoop(base::SimpleTestClock* clock) 13 : test_clock_(clock) { 14 } 15 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 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 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 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 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 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