Home | History | Annotate | Download | only in utility
      1 // Copyright 2013 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/utility/run_loop.h"
      6 
      7 #include <assert.h>
      8 
      9 #include <algorithm>
     10 #include <vector>
     11 
     12 #include "mojo/public/utility/run_loop_handler.h"
     13 #include "mojo/public/utility/thread_local.h"
     14 
     15 namespace mojo {
     16 namespace utility {
     17 namespace {
     18 
     19 ThreadLocalPointer<RunLoop>* tls_run_loop = NULL;
     20 
     21 const MojoTimeTicks kInvalidTimeTicks = static_cast<MojoTimeTicks>(0);
     22 
     23 }  // namespace
     24 
     25 // State needed for one iteration of WaitMany().
     26 struct RunLoop::WaitState {
     27   WaitState() : deadline(MOJO_DEADLINE_INDEFINITE) {}
     28 
     29   std::vector<Handle> handles;
     30   std::vector<MojoWaitFlags> wait_flags;
     31   MojoDeadline deadline;
     32 };
     33 
     34 struct RunLoop::RunState {
     35   RunState() : should_quit(false) {}
     36 
     37   bool should_quit;
     38 };
     39 
     40 RunLoop::RunLoop() : run_state_(NULL), next_handler_id_(0) {
     41   assert(tls_run_loop);
     42   assert(!tls_run_loop->Get());
     43   tls_run_loop->Set(this);
     44 }
     45 
     46 RunLoop::~RunLoop() {
     47   assert(tls_run_loop->Get() == this);
     48   tls_run_loop->Set(NULL);
     49 }
     50 
     51 // static
     52 void RunLoop::SetUp() {
     53   assert(!tls_run_loop);
     54   tls_run_loop = new ThreadLocalPointer<RunLoop>;
     55 }
     56 
     57 // static
     58 void RunLoop::TearDown() {
     59   assert(!current());
     60   assert(tls_run_loop);
     61   delete tls_run_loop;
     62   tls_run_loop = NULL;
     63 }
     64 
     65 // static
     66 RunLoop* RunLoop::current() {
     67   assert(tls_run_loop);
     68   return tls_run_loop->Get();
     69 }
     70 
     71 void RunLoop::AddHandler(RunLoopHandler* handler,
     72                          const Handle& handle,
     73                          MojoWaitFlags wait_flags,
     74                          MojoDeadline deadline) {
     75   assert(current() == this);
     76   assert(handler);
     77   assert(handle.is_valid());
     78   // Assume it's an error if someone tries to reregister an existing handle.
     79   assert(0u == handler_data_.count(handle));
     80   HandlerData handler_data;
     81   handler_data.handler = handler;
     82   handler_data.wait_flags = wait_flags;
     83   handler_data.deadline = (deadline == MOJO_DEADLINE_INDEFINITE) ?
     84       kInvalidTimeTicks :
     85       GetTimeTicksNow() + static_cast<MojoTimeTicks>(deadline);
     86   handler_data.id = next_handler_id_++;
     87   handler_data_[handle] = handler_data;
     88 }
     89 
     90 void RunLoop::RemoveHandler(const Handle& handle) {
     91   assert(current() == this);
     92   handler_data_.erase(handle);
     93 }
     94 
     95 void RunLoop::Run() {
     96   assert(current() == this);
     97   // We don't currently support nesting.
     98   assert(!run_state_);
     99   RunState* old_state = run_state_;
    100   RunState run_state;
    101   run_state_ = &run_state;
    102   while (!run_state.should_quit)
    103     Wait();
    104   run_state_ = old_state;
    105 }
    106 
    107 void RunLoop::Quit() {
    108   assert(current() == this);
    109   if (run_state_)
    110     run_state_->should_quit = true;
    111 }
    112 
    113 void RunLoop::Wait() {
    114   const WaitState wait_state = GetWaitState();
    115   if (wait_state.handles.empty()) {
    116     Quit();
    117     return;
    118   }
    119 
    120   const MojoResult result =
    121       WaitMany(wait_state.handles, wait_state.wait_flags, wait_state.deadline);
    122   if (result >= 0) {
    123     const size_t index = static_cast<size_t>(result);
    124     assert(handler_data_.find(wait_state.handles[index]) !=
    125            handler_data_.end());
    126     handler_data_[wait_state.handles[index]].handler->OnHandleReady(
    127         wait_state.handles[index]);
    128   } else {
    129     switch (result) {
    130       case MOJO_RESULT_INVALID_ARGUMENT:
    131       case MOJO_RESULT_FAILED_PRECONDITION:
    132         RemoveFirstInvalidHandle(wait_state);
    133         break;
    134       case MOJO_RESULT_DEADLINE_EXCEEDED:
    135         break;
    136       default:
    137         assert(false);
    138     }
    139   }
    140 
    141   NotifyDeadlineExceeded();
    142 }
    143 
    144 void RunLoop::NotifyDeadlineExceeded() {
    145   // Make a copy in case someone tries to add/remove new handlers as part of
    146   // notifying.
    147   const HandleToHandlerData cloned_handlers(handler_data_);
    148   const MojoTimeTicks now(GetTimeTicksNow());
    149   for (HandleToHandlerData::const_iterator i = cloned_handlers.begin();
    150        i != cloned_handlers.end(); ++i) {
    151     // Since we're iterating over a clone of the handlers, verify the handler is
    152     // still valid before notifying.
    153     if (i->second.deadline != kInvalidTimeTicks &&
    154         i->second.deadline < now &&
    155         handler_data_.find(i->first) != handler_data_.end() &&
    156         handler_data_[i->first].id == i->second.id) {
    157       i->second.handler->OnHandleError(i->first, MOJO_RESULT_DEADLINE_EXCEEDED);
    158     }
    159   }
    160 }
    161 
    162 void RunLoop::RemoveFirstInvalidHandle(const WaitState& wait_state) {
    163   for (size_t i = 0; i < wait_state.handles.size(); ++i) {
    164     const MojoResult result =
    165         mojo::Wait(wait_state.handles[i], wait_state.wait_flags[i],
    166                    static_cast<MojoDeadline>(0));
    167     if (result == MOJO_RESULT_INVALID_ARGUMENT ||
    168         result == MOJO_RESULT_FAILED_PRECONDITION) {
    169       // Remove the handle first, this way if OnHandleError() tries to remove
    170       // the handle our iterator isn't invalidated.
    171       assert(handler_data_.find(wait_state.handles[i]) != handler_data_.end());
    172       RunLoopHandler* handler =
    173           handler_data_[wait_state.handles[i]].handler;
    174       handler_data_.erase(wait_state.handles[i]);
    175       handler->OnHandleError(wait_state.handles[i], result);
    176       return;
    177     } else {
    178       assert(MOJO_RESULT_DEADLINE_EXCEEDED == result);
    179     }
    180   }
    181 }
    182 
    183 RunLoop::WaitState RunLoop::GetWaitState() const {
    184   WaitState wait_state;
    185   MojoTimeTicks min_time = kInvalidTimeTicks;
    186   for (HandleToHandlerData::const_iterator i = handler_data_.begin();
    187        i != handler_data_.end(); ++i) {
    188     wait_state.handles.push_back(i->first);
    189     wait_state.wait_flags.push_back(i->second.wait_flags);
    190     if (i->second.deadline != kInvalidTimeTicks &&
    191         (min_time == kInvalidTimeTicks || i->second.deadline < min_time)) {
    192       min_time = i->second.deadline;
    193     }
    194   }
    195   if (min_time != kInvalidTimeTicks) {
    196     const MojoTimeTicks now = GetTimeTicksNow();
    197     if (min_time < now)
    198       wait_state.deadline = static_cast<MojoDeadline>(0);
    199     else
    200       wait_state.deadline = static_cast<MojoDeadline>(min_time - now);
    201   }
    202   return wait_state;
    203 }
    204 
    205 }  // namespace utility
    206 }  // namespace mojo
    207