Home | History | Annotate | Download | only in message_loops
      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