// 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 brillo { FakeMessageLoop::FakeMessageLoop(base::SimpleTestClock* clock) : test_clock_(clock) { } MessageLoop::TaskId FakeMessageLoop::PostDelayedTask( const base::Location& from_here, base::OnceClosure 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, std::move(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; } 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 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::OnceClosure 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); std::move(callback).Run(); return true; } return false; } bool FakeMessageLoop::PendingTasks() { for (const auto& task : tasks_) { VLOG_LOC(task.second.location, 1) << "Pending task_id " << task.first << " scheduled from here."; } return !tasks_.empty(); } } // namespace brillo