// Copyright 2015 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include #include namespace chromeos { FakeMessageLoop::FakeMessageLoop(base::SimpleTestClock* clock) : test_clock_(clock) { } MessageLoop::TaskId FakeMessageLoop::PostDelayedTask( const tracked_objects::Location& from_here, const base::Closure& task, base::TimeDelta delay) { // If no SimpleTestClock was provided, we use the last time we fired a // callback. In this way, tasks scheduled from a Closure will have the right // time. if (test_clock_) current_time_ = test_clock_->Now(); MessageLoop::TaskId current_id = ++last_id_; // FakeMessageLoop is limited to only 2^64 tasks. That should be enough. CHECK(current_id); tasks_.emplace(current_id, ScheduledTask{from_here, false, task}); fire_order_.push(std::make_pair(current_time_ + delay, current_id)); VLOG_LOC(from_here, 1) << "Scheduling delayed task_id " << current_id << " to run at " << current_time_ + delay << " (in " << delay << ")."; return current_id; } MessageLoop::TaskId FakeMessageLoop::WatchFileDescriptor( const tracked_objects::Location& from_here, int fd, WatchMode mode, bool persistent, const base::Closure& task) { MessageLoop::TaskId current_id = ++last_id_; // FakeMessageLoop is limited to only 2^64 tasks. That should be enough. CHECK(current_id); tasks_.emplace(current_id, ScheduledTask{from_here, persistent, task}); fds_watched_.emplace(std::make_pair(fd, mode), current_id); return current_id; } bool FakeMessageLoop::CancelTask(TaskId task_id) { if (task_id == MessageLoop::kTaskIdNull) return false; bool ret = tasks_.erase(task_id) > 0; VLOG_IF(1, ret) << "Removing task_id " << task_id; return ret; } bool FakeMessageLoop::RunOnce(bool may_block) { if (test_clock_) current_time_ = test_clock_->Now(); // Try to fire ready file descriptors first. for (const auto& fd_mode : fds_ready_) { const auto& fd_watched = fds_watched_.find(fd_mode); if (fd_watched == fds_watched_.end()) continue; // The fd_watched->second task might have been canceled and we never removed // it from the fds_watched_, so we fix that now. const auto& scheduled_task_ref = tasks_.find(fd_watched->second); if (scheduled_task_ref == tasks_.end()) { fds_watched_.erase(fd_watched); continue; } VLOG_LOC(scheduled_task_ref->second.location, 1) << "Running task_id " << fd_watched->second << " for watching file descriptor " << fd_mode.first << " for " << (fd_mode.second == MessageLoop::kWatchRead ? "reading" : "writing") << (scheduled_task_ref->second.persistent ? " persistently" : " just once") << " scheduled from this location."; if (scheduled_task_ref->second.persistent) { scheduled_task_ref->second.callback.Run(); } else { base::Closure callback = std::move(scheduled_task_ref->second.callback); tasks_.erase(scheduled_task_ref); fds_watched_.erase(fd_watched); callback.Run(); } return true; } // Try to fire time-based callbacks afterwards. while (!fire_order_.empty() && (may_block || fire_order_.top().first <= current_time_)) { const auto task_ref = fire_order_.top(); fire_order_.pop(); // We need to skip tasks in the priority_queue not in the |tasks_| map. // This is normal if the task was canceled, as there is no efficient way // to remove a task from the priority_queue. const auto scheduled_task_ref = tasks_.find(task_ref.second); if (scheduled_task_ref == tasks_.end()) continue; // Advance the clock to the task firing time, if needed. if (current_time_ < task_ref.first) { current_time_ = task_ref.first; if (test_clock_) test_clock_->SetNow(current_time_); } // Move the Closure out of the map before delete it. We need to delete the // entry from the map before we call the callback, since calling CancelTask // for the task you are running now should fail and return false. base::Closure callback = std::move(scheduled_task_ref->second.callback); VLOG_LOC(scheduled_task_ref->second.location, 1) << "Running task_id " << task_ref.second << " at time " << current_time_ << " from this location."; tasks_.erase(scheduled_task_ref); callback.Run(); return true; } return false; } void FakeMessageLoop::SetFileDescriptorReadiness(int fd, WatchMode mode, bool ready) { if (ready) fds_ready_.emplace(fd, mode); else fds_ready_.erase(std::make_pair(fd, mode)); } bool FakeMessageLoop::PendingTasks() { for (const auto& task : tasks_) { VLOG_LOC(task.second.location, 1) << "Pending " << (task.second.persistent ? "persistent " : "") << "task_id " << task.first << " scheduled from here."; } return !tasks_.empty(); } } // namespace chromeos