• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 {
WaitStatemojo::utility::RunLoop::WaitState27   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 {
RunStatemojo::utility::RunLoop::RunState35   RunState() : should_quit(false) {}
36 
37   bool should_quit;
38 };
39 
RunLoop()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 
~RunLoop()46 RunLoop::~RunLoop() {
47   assert(tls_run_loop->Get() == this);
48   tls_run_loop->Set(NULL);
49 }
50 
51 // static
SetUp()52 void RunLoop::SetUp() {
53   assert(!tls_run_loop);
54   tls_run_loop = new ThreadLocalPointer<RunLoop>;
55 }
56 
57 // static
TearDown()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
current()66 RunLoop* RunLoop::current() {
67   assert(tls_run_loop);
68   return tls_run_loop->Get();
69 }
70 
AddHandler(RunLoopHandler * handler,const Handle & handle,MojoWaitFlags wait_flags,MojoDeadline deadline)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 
RemoveHandler(const Handle & handle)90 void RunLoop::RemoveHandler(const Handle& handle) {
91   assert(current() == this);
92   handler_data_.erase(handle);
93 }
94 
Run()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 
Quit()107 void RunLoop::Quit() {
108   assert(current() == this);
109   if (run_state_)
110     run_state_->should_quit = true;
111 }
112 
Wait()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 
NotifyDeadlineExceeded()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 
RemoveFirstInvalidHandle(const WaitState & wait_state)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 
GetWaitState() const183 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