Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(509)

Side by Side Diff: mojo/public/cpp/utility/lib/run_loop.cc

Issue 2250183003: Make the fuchsia mojo/public repo the source of truth. (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « mojo/public/cpp/utility/BUILD.gn ('k') | mojo/public/cpp/utility/run_loop.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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 <mojo/macros.h>
9 #include <pthread.h>
10
11 #include <algorithm>
12 #include <limits>
13 #include <utility>
14 #include <vector>
15
16 #include "mojo/public/cpp/system/time.h"
17 #include "mojo/public/cpp/system/wait.h"
18 #include "mojo/public/cpp/utility/run_loop_handler.h"
19
20 namespace mojo {
21 namespace {
22
23 // The initial and maximum number of results that we'll accept from
24 // |WaitSetWait()|. TODO(vtl): I just made up these numbers.
25 constexpr uint32_t kInitialWaitSetNumResults = 16u;
26 constexpr uint32_t kMaximumWaitSetNumResults = 256u;
27
28 pthread_key_t g_current_run_loop_key;
29
30 // Ensures that the "current run loop" functionality is available (i.e., that we
31 // have a TLS slot).
32 void InitializeCurrentRunLoopIfNecessary() {
33 static pthread_once_t current_run_loop_key_once = PTHREAD_ONCE_INIT;
34 int error = pthread_once(&current_run_loop_key_once, []() {
35 int error = pthread_key_create(&g_current_run_loop_key, nullptr);
36 MOJO_ALLOW_UNUSED_LOCAL(error);
37 assert(!error);
38 });
39 MOJO_ALLOW_UNUSED_LOCAL(error);
40 assert(!error);
41 }
42
43 void SetCurrentRunLoop(RunLoop* run_loop) {
44 InitializeCurrentRunLoopIfNecessary();
45
46 int error = pthread_setspecific(g_current_run_loop_key, run_loop);
47 MOJO_ALLOW_UNUSED_LOCAL(error);
48 assert(!error);
49 }
50
51 } // namespace
52
53 RunLoop::DelayedTaskInfo::DelayedTaskInfo(RunLoopHandler::Id id,
54 const Closure& task,
55 MojoTimeTicks absolute_run_time)
56 : id(id), task(task), absolute_run_time(absolute_run_time) {}
57
58 RunLoop::DelayedTaskInfo::~DelayedTaskInfo() {}
59
60 struct RunLoop::RunState {
61 bool should_quit = false;
62 uint32_t results_size = kInitialWaitSetNumResults;
63 std::vector<MojoWaitSetResult> results;
64 };
65
66 // static
67 constexpr MojoTimeTicks RunLoop::kInvalidTimeTicks;
68
69 RunLoop::RunLoop() {
70 MojoResult result = CreateWaitSet(nullptr, &wait_set_);
71 MOJO_ALLOW_UNUSED_LOCAL(result);
72 assert(result == MOJO_RESULT_OK);
73 assert(wait_set_.is_valid());
74
75 assert(!current());
76 SetCurrentRunLoop(this);
77 }
78
79 RunLoop::~RunLoop() {
80 assert(current() == this);
81
82 // Notify all handlers that they've been aborted. Note that handlers could
83 // conceivably call |RemoveHandler()| (which would be a bit shady, admittedly,
84 // even if we handle it correctly). (They could also call |AddHandler()|,
85 // which would be even shadier; we handle this "correctly", but we may still
86 // end up looping infinitely in that case.)
87 while (!handlers_.empty()) {
88 auto it = handlers_.begin();
89 auto handler = it->second.handler;
90 auto id = it->first;
91 handlers_.erase(it);
92 handler->OnHandleError(id, MOJO_RESULT_ABORTED);
93 }
94
95 SetCurrentRunLoop(nullptr);
96 }
97
98 // static
99 RunLoop* RunLoop::current() {
100 InitializeCurrentRunLoopIfNecessary();
101 return static_cast<RunLoop*>(pthread_getspecific(g_current_run_loop_key));
102 }
103
104 RunLoopHandler::Id RunLoop::AddHandler(RunLoopHandler* handler,
105 const Handle& handle,
106 MojoHandleSignals handle_signals,
107 MojoDeadline deadline) {
108 assert(current() == this);
109 assert(handler);
110 assert(handle.is_valid());
111
112 // Generate a |RunLoopHandler::Id|.
113 auto id = next_id_++;
114
115 // Calculate the absolute deadline.
116 auto absolute_deadline = kInvalidTimeTicks; // Default to "forever".
117 static constexpr auto kMaxMojoTimeTicks =
118 std::numeric_limits<MojoTimeTicks>::max();
119 if (deadline <= static_cast<MojoDeadline>(kMaxMojoTimeTicks)) {
120 auto now = GetTimeTicksNow();
121 if (deadline <= static_cast<MojoDeadline>(kMaxMojoTimeTicks - now)) {
122 absolute_deadline = now + static_cast<MojoTimeTicks>(deadline);
123 handler_deadlines_.push(HandlerDeadlineInfo(id, absolute_deadline));
124 }
125 // Else either |deadline| or |now| is so large (hopefully the former) that
126 // |now + deadline| would overflow. We'll take that to mean forever.
127 }
128 // Else |deadline| is either very large (which we may as well take as forever)
129 // or |MOJO_DEADLINE_INDEFINITE| (which is forever).
130
131 // Add an entry to |handlers_|.
132 handlers_.insert(std::make_pair(
133 id, HandlerInfo(handler, handle_signals, absolute_deadline)));
134 // Add an entry to the wait set.
135 MojoResult result =
136 WaitSetAdd(wait_set_.get(), handle, handle_signals, id, nullptr);
137 MOJO_ALLOW_UNUSED_LOCAL(result);
138 assert(result == MOJO_RESULT_OK);
139
140 return id;
141 }
142
143 void RunLoop::RemoveHandler(RunLoopHandler::Id id) {
144 assert(current() == this);
145
146 // Remove the entry from |handlers_|.
147 auto it = handlers_.find(id);
148 if (it == handlers_.end())
149 return;
150 handlers_.erase(it);
151 // Remove the entry from the wait set.
152 MojoResult result = WaitSetRemove(wait_set_.get(), id);
153 MOJO_ALLOW_UNUSED_LOCAL(result);
154 assert(result == MOJO_RESULT_OK);
155 }
156
157 void RunLoop::PostDelayedTask(const Closure& task, MojoTimeTicks delay) {
158 assert(current() == this);
159
160 // Generate a |RunLoopHandler::Id|.
161 auto id = next_id_++;
162
163 // Calculate the absolute run time.
164 auto now = GetTimeTicksNow();
165 assert(delay <= std::numeric_limits<MojoTimeTicks>::max() - now);
166 auto absolute_run_time = now + delay;
167
168 // Add an entry to |delayed_tasks_|.
169 delayed_tasks_.push(DelayedTaskInfo(id, task, absolute_run_time));
170 }
171
172 void RunLoop::Run() {
173 RunInternal(false);
174 }
175
176 void RunLoop::RunUntilIdle() {
177 RunInternal(true);
178 }
179
180 void RunLoop::Quit() {
181 assert(current() == this);
182
183 if (current_run_state_)
184 current_run_state_->should_quit = true;
185 }
186
187 void RunLoop::RunInternal(bool quit_when_idle) {
188 assert(current() == this);
189
190 auto old_run_state = current_run_state_;
191 RunState run_state;
192 current_run_state_ = &run_state;
193
194 while (DoIteration(quit_when_idle))
195 ; // The work is done in |DoIteration()|.
196
197 current_run_state_ = old_run_state;
198 }
199
200 bool RunLoop::DoIteration(bool quit_when_idle) {
201 assert(current_run_state_);
202 RunState& run_state = *current_run_state_;
203 assert(!run_state.should_quit);
204
205 bool should_continue = false;
206
207 auto now = GetTimeTicksNow();
208
209 // First, execute any already-enqueued tasks that are ready.
210
211 // This is a fake task that we use to compare to enqueued tasks (if one were
212 // to post a task now with no delay, it'd look like this). This is convenient
213 // since |DelayedTaskInfo| has an |operator<| (used by the priority queue
214 // |delayed_tasks_|).
215 //
216 // We want to execute tasks that are "greater" than |now_task| (i.e.,
217 // |now_task| is less than them) -- this includes all tasks that are currently
218 // ready, but not any newly-posted tasks (i.e., those that are posted as a
219 // result of executing ready tasks).
220 DelayedTaskInfo now_task(next_id_, Closure(), now);
221
222 while (!delayed_tasks_.empty() && now_task < delayed_tasks_.top()) {
223 // We could just execute the task directly from |delayed_tasks_.top()|,
224 // since no newly-posted task should change the top of the priority queue,
225 // but doing the below is more obviously correct.
226 Closure task = delayed_tasks_.top().task;
227 delayed_tasks_.pop();
228 task.Run();
229 should_continue = true;
230
231 if (run_state.should_quit)
232 return false;
233 }
234
235 // Next, "wait" and deal with handles/handlers.
236
237 if (handlers_.empty())
238 return should_continue;
239
240 // Calculate the deadline for the wait. Don't wait if |quit_when_idle| is
241 // true. Otherwise, the minimum of the earliest delayed task run time and the
242 // earliest handler deadline (or "forever" if there are no delayed tasks and
243 // no handler deadlines). (Warning: |CalculateAbsoluteDeadline()| may return a
244 // deadline earlier than |now|.)
245 bool absolute_deadline_is_for_delayed_task = false;
246 MojoTimeTicks absolute_deadline =
247 quit_when_idle
248 ? now
249 : CalculateAbsoluteDeadline(&absolute_deadline_is_for_delayed_task);
250 MojoDeadline relative_deadline =
251 (absolute_deadline == kInvalidTimeTicks)
252 ? MOJO_DEADLINE_INDEFINITE
253 : static_cast<MojoDeadline>(std::max(now, absolute_deadline) - now);
254
255 run_state.results.resize(run_state.results_size);
256 uint32_t max_results = run_state.results_size;
257 switch (WaitSetWait(wait_set_.get(), relative_deadline,
258 &current_run_state_->results, &max_results)) {
259 case MOJO_RESULT_OK:
260 // If there were more results than we could accept, try increasing the
261 // number we accept (up to our limit).
262 if (max_results > run_state.results_size) {
263 run_state.results_size =
264 std::min(kMaximumWaitSetNumResults, run_state.results_size * 2u);
265 }
266 should_continue |= NotifyResults(run_state.results);
267 break;
268 case MOJO_RESULT_INVALID_ARGUMENT:
269 assert(false); // This shouldn't happen.
270 return false;
271 case MOJO_RESULT_CANCELLED:
272 assert(false); // This shouldn't happen.
273 return false;
274 case MOJO_RESULT_RESOURCE_EXHAUSTED:
275 assert(false); // Sadness.
276 return false;
277 case MOJO_RESULT_BUSY:
278 assert(false); // This shouldn't happen.
279 return false;
280 case MOJO_RESULT_DEADLINE_EXCEEDED:
281 should_continue |= NotifyHandlersDeadlineExceeded(absolute_deadline);
282 // If we timed out due for a delayed task, pretend that we did work since
283 // we're not idle yet (there'll be work to do immediately the next time
284 // through the loop).
285 should_continue |= absolute_deadline_is_for_delayed_task;
286 break;
287 default:
288 assert(false); // This *really* shouldn't happen.
289 return false;
290 }
291
292 if (run_state.should_quit)
293 return false;
294
295 return quit_when_idle ? should_continue : !handlers_.empty();
296 }
297
298 MojoTimeTicks RunLoop::CalculateAbsoluteDeadline(bool* is_delayed_task) {
299 assert(!handlers_.empty());
300
301 // Default to "forever".
302 MojoTimeTicks absolute_deadline = kInvalidTimeTicks;
303 if (delayed_tasks_.empty()) {
304 *is_delayed_task = false;
305 } else {
306 // If there are delayed tasks, our deadline can be no later than the
307 // earliest run time.
308 absolute_deadline = delayed_tasks_.top().absolute_run_time;
309 *is_delayed_task = true;
310 }
311
312 // Find the earliest handler deadline.
313 while (!handler_deadlines_.empty()) {
314 const HandlerDeadlineInfo& info = handler_deadlines_.top();
315 const auto it = handlers_.find(info.id);
316 // We might have a stale entry at the top. If so, remove it and continue.
317 if (it == handlers_.end()) {
318 handler_deadlines_.pop();
319 continue;
320 }
321
322 if (absolute_deadline == kInvalidTimeTicks ||
323 info.absolute_deadline < absolute_deadline) {
324 absolute_deadline = info.absolute_deadline;
325 *is_delayed_task = false;
326 }
327
328 break;
329 }
330
331 return absolute_deadline;
332 }
333
334 bool RunLoop::NotifyResults(const std::vector<MojoWaitSetResult>& results) {
335 assert(!results.empty());
336
337 bool did_work = false;
338 for (const auto& result : results) {
339 auto id = result.cookie;
340 auto it = handlers_.find(id);
341 // Though we should find an entry for the first result, a handler that we
342 // invoke may remove other handlers.
343 if (it == handlers_.end())
344 continue;
345
346 auto handler = it->second.handler;
347 handlers_.erase(it);
348 MojoResult r = WaitSetRemove(wait_set_.get(), id);
349 MOJO_ALLOW_UNUSED_LOCAL(r);
350 assert(r == MOJO_RESULT_OK);
351 if (result.wait_result == MOJO_RESULT_OK)
352 handler->OnHandleReady(id);
353 else
354 handler->OnHandleError(id, result.wait_result);
355 did_work = true;
356
357 if (current_run_state_->should_quit)
358 break;
359 }
360 return did_work;
361 }
362
363 bool RunLoop::NotifyHandlersDeadlineExceeded(MojoTimeTicks absolute_deadline) {
364 assert(!handlers_.empty());
365 assert(absolute_deadline != kInvalidTimeTicks);
366
367 bool did_work = false;
368 while (!handler_deadlines_.empty()) {
369 const HandlerDeadlineInfo& info = handler_deadlines_.top();
370
371 if (info.absolute_deadline > absolute_deadline)
372 break;
373
374 const auto it = handlers_.find(info.id);
375 // Though the top shouldn't be stale, there may be stale entries after it
376 // (with the same deadline). Moreover, previously-run handlers may have
377 // removed yet-to-be-run handlers.
378 if (it == handlers_.end()) {
379 handler_deadlines_.pop();
380 continue;
381 }
382
383 auto handler = it->second.handler;
384 auto id = info.id;
385 handlers_.erase(it); // Invalidates |it|.
386 handler_deadlines_.pop(); // Invalidates |info|.
387 handler->OnHandleError(id, MOJO_RESULT_DEADLINE_EXCEEDED);
388 did_work = true;
389
390 if (current_run_state_->should_quit)
391 break;
392 }
393 return did_work;
394 }
395
396 } // namespace mojo
OLDNEW
« no previous file with comments | « mojo/public/cpp/utility/BUILD.gn ('k') | mojo/public/cpp/utility/run_loop.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698