Home | History | Annotate | Download | only in lib
      1 // Copyright 2014 The Chromium 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 "mojo/public/cpp/utility/run_loop.h"
      6 
      7 #include <assert.h>
      8 
      9 #include <algorithm>
     10 #include <vector>
     11 
     12 #include "mojo/public/cpp/utility/lib/thread_local.h"
     13 #include "mojo/public/cpp/utility/run_loop_handler.h"
     14 
     15 namespace mojo {
     16 namespace {
     17 
     18 internal::ThreadLocalPointer<RunLoop> current_run_loop;
     19 
     20 const MojoTimeTicks kInvalidTimeTicks = static_cast<MojoTimeTicks>(0);
     21 
     22 }  // namespace
     23 
     24 // State needed for one iteration of WaitMany().
     25 struct RunLoop::WaitState {
     26   WaitState() : deadline(MOJO_DEADLINE_INDEFINITE) {}
     27 
     28   std::vector<Handle> handles;
     29   std::vector<MojoHandleSignals> handle_signals;
     30   MojoDeadline deadline;
     31 };
     32 
     33 struct RunLoop::RunState {
     34   RunState() : should_quit(false) {}
     35 
     36   bool should_quit;
     37 };
     38 
     39 RunLoop::RunLoop()
     40     : run_state_(NULL), next_handler_id_(0), next_sequence_number_(0) {
     41   assert(!current());
     42   current_run_loop.Set(this);
     43 }
     44 
     45 RunLoop::~RunLoop() {
     46   assert(current() == this);
     47   NotifyHandlers(MOJO_RESULT_ABORTED, IGNORE_DEADLINE);
     48   current_run_loop.Set(NULL);
     49 }
     50 
     51 // static
     52 void RunLoop::SetUp() {
     53   current_run_loop.Allocate();
     54 }
     55 
     56 // static
     57 void RunLoop::TearDown() {
     58   assert(!current());
     59   current_run_loop.Free();
     60 }
     61 
     62 // static
     63 RunLoop* RunLoop::current() {
     64   return current_run_loop.Get();
     65 }
     66 
     67 void RunLoop::AddHandler(RunLoopHandler* handler,
     68                          const Handle& handle,
     69                          MojoHandleSignals handle_signals,
     70                          MojoDeadline deadline) {
     71   assert(current() == this);
     72   assert(handler);
     73   assert(handle.is_valid());
     74   // Assume it's an error if someone tries to reregister an existing handle.
     75   assert(0u == handler_data_.count(handle));
     76   HandlerData handler_data;
     77   handler_data.handler = handler;
     78   handler_data.handle_signals = handle_signals;
     79   handler_data.deadline = (deadline == MOJO_DEADLINE_INDEFINITE) ?
     80       kInvalidTimeTicks :
     81       GetTimeTicksNow() + static_cast<MojoTimeTicks>(deadline);
     82   handler_data.id = next_handler_id_++;
     83   handler_data_[handle] = handler_data;
     84 }
     85 
     86 void RunLoop::RemoveHandler(const Handle& handle) {
     87   assert(current() == this);
     88   handler_data_.erase(handle);
     89 }
     90 
     91 bool RunLoop::HasHandler(const Handle& handle) const {
     92   return handler_data_.find(handle) != handler_data_.end();
     93 }
     94 
     95 void RunLoop::Run() {
     96   RunInternal(UNTIL_EMPTY);
     97 }
     98 
     99 void RunLoop::RunUntilIdle() {
    100   RunInternal(UNTIL_IDLE);
    101 }
    102 
    103 void RunLoop::RunInternal(RunMode run_mode) {
    104   assert(current() == this);
    105   RunState* old_state = run_state_;
    106   RunState run_state;
    107   run_state_ = &run_state;
    108   for (;;) {
    109     bool did_work = DoDelayedWork();
    110     if (run_state.should_quit)
    111       break;
    112     did_work |= Wait(run_mode == UNTIL_IDLE);
    113     if (run_state.should_quit)
    114       break;
    115     if (!did_work && run_mode == UNTIL_IDLE)
    116       break;
    117   }
    118   run_state_ = old_state;
    119 }
    120 
    121 bool RunLoop::DoDelayedWork() {
    122   MojoTimeTicks now = GetTimeTicksNow();
    123   if (!delayed_tasks_.empty() && delayed_tasks_.top().run_time <= now) {
    124     PendingTask task = delayed_tasks_.top();
    125     delayed_tasks_.pop();
    126     task.task.Run();
    127     return true;
    128   }
    129   return false;
    130 }
    131 
    132 void RunLoop::Quit() {
    133   assert(current() == this);
    134   if (run_state_)
    135     run_state_->should_quit = true;
    136 }
    137 
    138 void RunLoop::PostDelayedTask(const Closure& task, MojoTimeTicks delay) {
    139   assert(current() == this);
    140   MojoTimeTicks run_time = delay + GetTimeTicksNow();
    141   delayed_tasks_.push(PendingTask(task, run_time, next_sequence_number_++));
    142 }
    143 
    144 bool RunLoop::Wait(bool non_blocking) {
    145   const WaitState wait_state = GetWaitState(non_blocking);
    146   if (wait_state.handles.empty() && delayed_tasks_.empty()) {
    147     Quit();
    148     return false;
    149   }
    150 
    151   const MojoResult result = WaitMany(wait_state.handles,
    152                                      wait_state.handle_signals,
    153                                      wait_state.deadline);
    154   if (result >= 0) {
    155     const size_t index = static_cast<size_t>(result);
    156     assert(handler_data_.find(wait_state.handles[index]) !=
    157            handler_data_.end());
    158     handler_data_[wait_state.handles[index]].handler->OnHandleReady(
    159         wait_state.handles[index]);
    160     return true;
    161   }
    162 
    163   switch (result) {
    164     case MOJO_RESULT_INVALID_ARGUMENT:
    165     case MOJO_RESULT_FAILED_PRECONDITION:
    166       return RemoveFirstInvalidHandle(wait_state);
    167     case MOJO_RESULT_DEADLINE_EXCEEDED:
    168       return NotifyHandlers(MOJO_RESULT_DEADLINE_EXCEEDED, CHECK_DEADLINE);
    169   }
    170 
    171   assert(false);
    172   return false;
    173 }
    174 
    175 bool RunLoop::NotifyHandlers(MojoResult error, CheckDeadline check) {
    176   bool notified = false;
    177 
    178   // Make a copy in case someone tries to add/remove new handlers as part of
    179   // notifying.
    180   const HandleToHandlerData cloned_handlers(handler_data_);
    181   const MojoTimeTicks now(GetTimeTicksNow());
    182   for (HandleToHandlerData::const_iterator i = cloned_handlers.begin();
    183        i != cloned_handlers.end(); ++i) {
    184     // Only check deadline exceeded if that's what we're notifying.
    185     if (check == CHECK_DEADLINE && (i->second.deadline == kInvalidTimeTicks ||
    186                                     i->second.deadline > now)) {
    187       continue;
    188     }
    189 
    190     // Since we're iterating over a clone of the handlers, verify the handler
    191     // is still valid before notifying.
    192     if (handler_data_.find(i->first) == handler_data_.end() ||
    193         handler_data_[i->first].id != i->second.id) {
    194       continue;
    195     }
    196 
    197     RunLoopHandler* handler = i->second.handler;
    198     handler_data_.erase(i->first);
    199     handler->OnHandleError(i->first, error);
    200     notified = true;
    201   }
    202 
    203   return notified;
    204 }
    205 
    206 bool RunLoop::RemoveFirstInvalidHandle(const WaitState& wait_state) {
    207   for (size_t i = 0; i < wait_state.handles.size(); ++i) {
    208     const MojoResult result =
    209         mojo::Wait(wait_state.handles[i], wait_state.handle_signals[i],
    210                    static_cast<MojoDeadline>(0));
    211     if (result == MOJO_RESULT_INVALID_ARGUMENT ||
    212         result == MOJO_RESULT_FAILED_PRECONDITION) {
    213       // Remove the handle first, this way if OnHandleError() tries to remove
    214       // the handle our iterator isn't invalidated.
    215       assert(handler_data_.find(wait_state.handles[i]) != handler_data_.end());
    216       RunLoopHandler* handler =
    217           handler_data_[wait_state.handles[i]].handler;
    218       handler_data_.erase(wait_state.handles[i]);
    219       handler->OnHandleError(wait_state.handles[i], result);
    220       return true;
    221     }
    222     assert(MOJO_RESULT_DEADLINE_EXCEEDED == result);
    223   }
    224   return false;
    225 }
    226 
    227 RunLoop::WaitState RunLoop::GetWaitState(bool non_blocking) const {
    228   WaitState wait_state;
    229   MojoTimeTicks min_time = kInvalidTimeTicks;
    230   for (HandleToHandlerData::const_iterator i = handler_data_.begin();
    231        i != handler_data_.end(); ++i) {
    232     wait_state.handles.push_back(i->first);
    233     wait_state.handle_signals.push_back(i->second.handle_signals);
    234     if (!non_blocking && i->second.deadline != kInvalidTimeTicks &&
    235         (min_time == kInvalidTimeTicks || i->second.deadline < min_time)) {
    236       min_time = i->second.deadline;
    237     }
    238   }
    239   if (!delayed_tasks_.empty()) {
    240     MojoTimeTicks delayed_min_time = delayed_tasks_.top().run_time;
    241     if (min_time == kInvalidTimeTicks)
    242       min_time = delayed_min_time;
    243     else
    244       min_time = std::min(min_time, delayed_min_time);
    245   }
    246   if (non_blocking) {
    247     wait_state.deadline = static_cast<MojoDeadline>(0);
    248   } else if (min_time != kInvalidTimeTicks) {
    249     const MojoTimeTicks now = GetTimeTicksNow();
    250     if (min_time < now)
    251       wait_state.deadline = static_cast<MojoDeadline>(0);
    252     else
    253       wait_state.deadline = static_cast<MojoDeadline>(min_time - now);
    254   }
    255   return wait_state;
    256 }
    257 
    258 RunLoop::PendingTask::PendingTask(const Closure& task,
    259                                   MojoTimeTicks run_time,
    260                                   uint64_t sequence_number)
    261     : task(task), run_time(run_time), sequence_number(sequence_number) {
    262 }
    263 
    264 RunLoop::PendingTask::~PendingTask() {
    265 }
    266 
    267 bool RunLoop::PendingTask::operator<(const RunLoop::PendingTask& other) const {
    268   if (run_time != other.run_time) {
    269     // std::priority_queue<> puts the least element at the end of the queue. We
    270     // want the soonest eligible task to be at the head of the queue, so
    271     // run_times further in the future are considered lesser.
    272     return run_time > other.run_time;
    273   }
    274 
    275   return sequence_number > other.sequence_number;
    276 }
    277 
    278 }  // namespace mojo
    279