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