OLD | NEW |
| (Empty) |
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_(nullptr), 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(nullptr); | |
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 = | |
80 (deadline == MOJO_DEADLINE_INDEFINITE) | |
81 ? kInvalidTimeTicks | |
82 : GetTimeTicksNow() + static_cast<MojoTimeTicks>(deadline); | |
83 handler_data.id = next_handler_id_++; | |
84 handler_data_[handle] = handler_data; | |
85 } | |
86 | |
87 void RunLoop::RemoveHandler(const Handle& handle) { | |
88 assert(current() == this); | |
89 handler_data_.erase(handle); | |
90 } | |
91 | |
92 bool RunLoop::HasHandler(const Handle& handle) const { | |
93 return handler_data_.find(handle) != handler_data_.end(); | |
94 } | |
95 | |
96 void RunLoop::Run() { | |
97 RunInternal(UNTIL_EMPTY); | |
98 } | |
99 | |
100 void RunLoop::RunUntilIdle() { | |
101 RunInternal(UNTIL_IDLE); | |
102 } | |
103 | |
104 void RunLoop::RunInternal(RunMode run_mode) { | |
105 assert(current() == this); | |
106 RunState* old_state = run_state_; | |
107 RunState run_state; | |
108 run_state_ = &run_state; | |
109 for (;;) { | |
110 bool did_work = DoDelayedWork(); | |
111 if (run_state.should_quit) | |
112 break; | |
113 did_work |= Wait(run_mode == UNTIL_IDLE); | |
114 if (run_state.should_quit) | |
115 break; | |
116 if (!did_work && run_mode == UNTIL_IDLE) | |
117 break; | |
118 } | |
119 run_state_ = old_state; | |
120 } | |
121 | |
122 bool RunLoop::DoDelayedWork() { | |
123 MojoTimeTicks now = GetTimeTicksNow(); | |
124 if (!delayed_tasks_.empty() && delayed_tasks_.top().run_time <= now) { | |
125 PendingTask task = delayed_tasks_.top(); | |
126 delayed_tasks_.pop(); | |
127 task.task.Run(); | |
128 return true; | |
129 } | |
130 return false; | |
131 } | |
132 | |
133 void RunLoop::Quit() { | |
134 assert(current() == this); | |
135 if (run_state_) | |
136 run_state_->should_quit = true; | |
137 } | |
138 | |
139 void RunLoop::PostDelayedTask(const Closure& task, MojoTimeTicks delay) { | |
140 assert(current() == this); | |
141 MojoTimeTicks run_time = delay + GetTimeTicksNow(); | |
142 delayed_tasks_.push(PendingTask(task, run_time, next_sequence_number_++)); | |
143 } | |
144 | |
145 bool RunLoop::Wait(bool non_blocking) { | |
146 const WaitState wait_state = GetWaitState(non_blocking); | |
147 if (wait_state.handles.empty()) { | |
148 if (delayed_tasks_.empty()) | |
149 Quit(); | |
150 return false; | |
151 } | |
152 | |
153 const WaitManyResult wmr = | |
154 WaitMany(wait_state.handles, wait_state.handle_signals, | |
155 wait_state.deadline, nullptr); | |
156 | |
157 if (!wmr.IsIndexValid()) { | |
158 assert(wmr.result == MOJO_RESULT_DEADLINE_EXCEEDED); | |
159 return NotifyHandlers(MOJO_RESULT_DEADLINE_EXCEEDED, CHECK_DEADLINE); | |
160 } | |
161 | |
162 Handle handle = wait_state.handles[wmr.index]; | |
163 assert(handler_data_.find(handle) != handler_data_.end()); | |
164 RunLoopHandler* handler = handler_data_[handle].handler; | |
165 | |
166 switch (wmr.result) { | |
167 case MOJO_RESULT_OK: | |
168 handler->OnHandleReady(handle); | |
169 return true; | |
170 case MOJO_RESULT_INVALID_ARGUMENT: | |
171 case MOJO_RESULT_FAILED_PRECONDITION: | |
172 // Remove the handle first, this way if OnHandleError() tries to remove | |
173 // the handle our iterator isn't invalidated. | |
174 handler_data_.erase(handle); | |
175 handler->OnHandleError(handle, wmr.result); | |
176 return true; | |
177 default: | |
178 assert(false); | |
179 return false; | |
180 } | |
181 } | |
182 | |
183 bool RunLoop::NotifyHandlers(MojoResult error, CheckDeadline check) { | |
184 bool notified = false; | |
185 | |
186 // Make a copy in case someone tries to add/remove new handlers as part of | |
187 // notifying. | |
188 const HandleToHandlerData cloned_handlers(handler_data_); | |
189 const MojoTimeTicks now(GetTimeTicksNow()); | |
190 for (HandleToHandlerData::const_iterator i = cloned_handlers.begin(); | |
191 i != cloned_handlers.end(); | |
192 ++i) { | |
193 // Only check deadline exceeded if that's what we're notifying. | |
194 if (check == CHECK_DEADLINE && | |
195 (i->second.deadline == kInvalidTimeTicks || i->second.deadline > now)) { | |
196 continue; | |
197 } | |
198 | |
199 // Since we're iterating over a clone of the handlers, verify the handler | |
200 // is still valid before notifying. | |
201 if (handler_data_.find(i->first) == handler_data_.end() || | |
202 handler_data_[i->first].id != i->second.id) { | |
203 continue; | |
204 } | |
205 | |
206 RunLoopHandler* handler = i->second.handler; | |
207 handler_data_.erase(i->first); | |
208 handler->OnHandleError(i->first, error); | |
209 notified = true; | |
210 } | |
211 | |
212 return notified; | |
213 } | |
214 | |
215 RunLoop::WaitState RunLoop::GetWaitState(bool non_blocking) const { | |
216 WaitState wait_state; | |
217 MojoTimeTicks min_time = kInvalidTimeTicks; | |
218 for (HandleToHandlerData::const_iterator i = handler_data_.begin(); | |
219 i != handler_data_.end(); | |
220 ++i) { | |
221 wait_state.handles.push_back(i->first); | |
222 wait_state.handle_signals.push_back(i->second.handle_signals); | |
223 if (!non_blocking && i->second.deadline != kInvalidTimeTicks && | |
224 (min_time == kInvalidTimeTicks || i->second.deadline < min_time)) { | |
225 min_time = i->second.deadline; | |
226 } | |
227 } | |
228 if (!delayed_tasks_.empty()) { | |
229 MojoTimeTicks delayed_min_time = delayed_tasks_.top().run_time; | |
230 if (min_time == kInvalidTimeTicks) | |
231 min_time = delayed_min_time; | |
232 else | |
233 min_time = std::min(min_time, delayed_min_time); | |
234 } | |
235 if (non_blocking) { | |
236 wait_state.deadline = static_cast<MojoDeadline>(0); | |
237 } else if (min_time != kInvalidTimeTicks) { | |
238 const MojoTimeTicks now = GetTimeTicksNow(); | |
239 if (min_time < now) | |
240 wait_state.deadline = static_cast<MojoDeadline>(0); | |
241 else | |
242 wait_state.deadline = static_cast<MojoDeadline>(min_time - now); | |
243 } | |
244 return wait_state; | |
245 } | |
246 | |
247 RunLoop::PendingTask::PendingTask(const Closure& task, | |
248 MojoTimeTicks run_time, | |
249 uint64_t sequence_number) | |
250 : task(task), run_time(run_time), sequence_number(sequence_number) { | |
251 } | |
252 | |
253 RunLoop::PendingTask::~PendingTask() { | |
254 } | |
255 | |
256 bool RunLoop::PendingTask::operator<(const RunLoop::PendingTask& other) const { | |
257 if (run_time != other.run_time) { | |
258 // std::priority_queue<> puts the least element at the end of the queue. We | |
259 // want the soonest eligible task to be at the head of the queue, so | |
260 // run_times further in the future are considered lesser. | |
261 return run_time > other.run_time; | |
262 } | |
263 | |
264 return sequence_number > other.sequence_number; | |
265 } | |
266 | |
267 } // namespace mojo | |
OLD | NEW |