OLD | NEW |
| (Empty) |
1 // Copyright 2016 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 //! This module contains a thread-local run-loop. | |
6 //! | |
7 //! The run-loop may have handles and handlers pre-registers | |
8 //! (and in fact, must) in order to keep running. The run-loop | |
9 //! executes until it has no more handles or handlers on itself, | |
10 //! or until it is told to quit via stop(). | |
11 //! | |
12 //! The run-loop waits until some signals on some handle is satisfied, | |
13 //! at which point it wakes up and executes the appropriate handler | |
14 //! method. This handler method may then be used to further populate | |
15 //! or de-populate the run-loop. | |
16 //! | |
17 //! As of yet, the run-loop is NOT thread-safe. Although it is useful | |
18 //! to be able to register tasks or handles from one thread onto | |
19 //! another thread's run-loop, this is as-of-yet unsupported, and | |
20 //! Rust should complain loudly when you try to do any threading here. | |
21 | |
22 use std::cell::RefCell; | |
23 use std::cmp::{PartialEq, Eq, PartialOrd, Ord, Ordering}; | |
24 use std::collections::BinaryHeap; | |
25 use std::collections::HashMap; | |
26 use std::i64; | |
27 use std::u32; | |
28 use std::vec::Vec; | |
29 | |
30 use system; | |
31 use system::{Handle, MOJO_INDEFINITE, MojoResult}; | |
32 use system::core; | |
33 use system::wait_set; | |
34 | |
35 /// Define the equivalent of MOJO_INDEFINITE for absolute deadlines | |
36 const MOJO_INDEFINITE_ABSOLUTE: system::MojoTimeTicks = 0; | |
37 | |
38 // TODO(mknyszek): The numbers below are arbitrary and come from the C++ binding
s, | |
39 // and should probably be changed at some point | |
40 | |
41 /// Initial size of the result buffer. | |
42 const INITIAL_WAIT_SET_NUM_RESULTS: usize = 16; | |
43 | |
44 /// Maximum size of the result buffer. | |
45 const MAXIMUM_WAIT_SET_NUM_RESULTS: usize = 256; | |
46 | |
47 /// Thread-local data structure for keeping track of handles to wait on. | |
48 thread_local!(static TL_RUN_LOOP: RefCell<RunLoop<'static, 'static>> = RefCell::
new(RunLoop::new())); | |
49 | |
50 /// Token representing handle/callback to wait on for this thread only. This | |
51 /// token only has meaning on the thread in which the handle was registered. | |
52 #[derive(Clone, Debug, PartialEq, Eq, Hash)] | |
53 pub struct Token(u64); | |
54 | |
55 impl Token { | |
56 /// Get the wait token's "cookie" form, suitable for use in a wait set. | |
57 fn as_cookie(&self) -> u64 { | |
58 self.0 | |
59 } | |
60 } | |
61 | |
62 /// Represents the possible error cases that may occur when waiting | |
63 /// on a handle in a RunLoop. | |
64 #[derive(Clone, Debug, PartialEq, Eq)] | |
65 pub enum WaitError { | |
66 /// The handle has been closed or is otherwise no longer valid. | |
67 HandleClosed, | |
68 | |
69 /// The handle is currently busy in some transaction. | |
70 HandleBusy, | |
71 | |
72 /// It has been determined that the signals provided will never | |
73 /// be satisfied for this handle. | |
74 Unsatisfiable, | |
75 } | |
76 | |
77 /// A trait which defines an interface to be a handler usable by | |
78 /// a RunLoop. | |
79 pub trait Handler { | |
80 /// Called after a successful wait. | |
81 fn on_ready(&mut self, runloop: &mut RunLoop, token: Token); | |
82 | |
83 /// Called after the given deadline expires. | |
84 fn on_timeout(&mut self, runloop: &mut RunLoop, token: Token); | |
85 | |
86 /// Called when an unexpected error occurs. | |
87 fn on_error(&mut self, runloop: &mut RunLoop, token: Token, error: WaitError
); | |
88 } | |
89 | |
90 /// A wrapper struct for carrying the handler as well as various information | |
91 /// about it. | |
92 struct HandlerInfo<'h> { | |
93 /// The handle for which we are waiting. | |
94 /// | |
95 /// We keep this handle around so that we may easily re-register. | |
96 handle: system::MojoHandle, | |
97 | |
98 /// The handler, boxed up. | |
99 /// | |
100 /// The handler is in an Option type because if it is currently being | |
101 /// used in a callback, we must take ownership to avoid mutability | |
102 /// cycles. The easiest way to do this is to take() from the Option then | |
103 /// put it back. | |
104 handler: Option<Box<Handler + 'h>>, | |
105 | |
106 /// An absolute deadline in terms of time ticks. | |
107 /// | |
108 /// This is the most recently updated deadline that | |
109 /// we should be watching out for. All others for this | |
110 /// token may be considered "stale". | |
111 deadline: system::MojoTimeTicks, | |
112 } | |
113 | |
114 impl<'h> HandlerInfo<'h> { | |
115 /// Take the handler out of its Option type. | |
116 pub fn take(&mut self) -> Option<Box<Handler + 'h>> { | |
117 self.handler.take() | |
118 } | |
119 | |
120 /// Put a new handler into the Option type. | |
121 pub fn give(&mut self, handler: Box<Handler + 'h>) { | |
122 self.handler = Some(handler); | |
123 } | |
124 | |
125 /// Getter for the system::MojoHandle held inside. | |
126 pub fn handle(&self) -> system::MojoHandle { | |
127 self.handle | |
128 } | |
129 | |
130 /// Getter for the current absolute deadline held inside. | |
131 pub fn deadline(&self) -> system::MojoTimeTicks { | |
132 self.deadline | |
133 } | |
134 | |
135 /// Setter to update the current absolute deadline. | |
136 pub fn set_deadline(&mut self, deadline: system::MojoTimeTicks) { | |
137 self.deadline = deadline | |
138 } | |
139 } | |
140 | |
141 /// A wrapper struct for carrying the task as well as some information about | |
142 /// it. | |
143 struct TaskInfo<'t> { | |
144 /// The task, boxed up. | |
145 closure: Box<FnMut(&mut RunLoop) + 't>, | |
146 | |
147 /// An absolute deadline in terms of time ticks. | |
148 /// | |
149 /// This is the most recently updated deadline that | |
150 /// we should be watching out for. All others for this | |
151 /// token may be considered "stale". | |
152 deadline: system::MojoTimeTicks, | |
153 } | |
154 | |
155 impl<'t> TaskInfo<'t> { | |
156 /// Executes the task within the info object, consuming it | |
157 /// in the process. | |
158 pub fn execute_task(mut self, run_loop: &mut RunLoop) { | |
159 (*self.closure)(run_loop); | |
160 } | |
161 | |
162 /// Getter for the current absolute deadline held inside. | |
163 pub fn deadline(&self) -> system::MojoTimeTicks { | |
164 self.deadline | |
165 } | |
166 } | |
167 | |
168 impl<'t> PartialEq for TaskInfo<'t> { | |
169 /// Equality for TaskInfo in terms of its deadline | |
170 fn eq(&self, other: &TaskInfo) -> bool { | |
171 self.deadline == other.deadline | |
172 } | |
173 } | |
174 | |
175 impl<'t> Eq for TaskInfo<'t> {} | |
176 | |
177 impl<'t> PartialOrd for TaskInfo<'t> { | |
178 /// Partial comparison for TaskInfo in terms of its deadline | |
179 /// | |
180 /// Reverses the comparison because the Rust std library only | |
181 /// offers a max-heap, and we need a min-heap. | |
182 fn partial_cmp(&self, other: &TaskInfo) -> Option<Ordering> { | |
183 other.deadline.partial_cmp(&self.deadline) | |
184 } | |
185 } | |
186 | |
187 impl<'t> Ord for TaskInfo<'t> { | |
188 /// Implement comparisons for Task Info. | |
189 /// | |
190 /// Reverses the comparison because the Rust std library only | |
191 /// offers a max-heap, and we need a min-heap. | |
192 fn cmp(&self, other: &Self) -> Ordering { | |
193 other.deadline.cmp(&self.deadline) | |
194 } | |
195 } | |
196 | |
197 /// Wrapper struct intended to be used in a priority queue | |
198 /// for efficiently retrieving the next closest deadline. | |
199 #[derive(Clone)] | |
200 struct DeadlineInfo { | |
201 /// The ID of the associated Handler struct in the RunLoop's | |
202 /// hash map. | |
203 token: Token, | |
204 | |
205 /// An absolute deadline in terms of time ticks. | |
206 deadline: system::MojoTimeTicks, | |
207 } | |
208 | |
209 impl DeadlineInfo { | |
210 /// Getter for an immutable borrow for the token inside. | |
211 pub fn token(&self) -> &Token { | |
212 &self.token | |
213 } | |
214 | |
215 /// Getter for the absolute deadline inside. | |
216 pub fn deadline(&self) -> system::MojoTimeTicks { | |
217 self.deadline | |
218 } | |
219 } | |
220 | |
221 impl PartialEq for DeadlineInfo { | |
222 /// Equality for DeadlineInfo in terms of its deadline | |
223 fn eq(&self, other: &DeadlineInfo) -> bool { | |
224 self.deadline == other.deadline | |
225 } | |
226 } | |
227 | |
228 impl Eq for DeadlineInfo {} | |
229 | |
230 impl PartialOrd for DeadlineInfo { | |
231 /// Partial comparison for DeadlineInfo in terms of its deadline | |
232 /// | |
233 /// Reverses the comparison because the Rust std library only | |
234 /// offers a max-heap, and we need a min-heap. | |
235 fn partial_cmp(&self, other: &DeadlineInfo) -> Option<Ordering> { | |
236 other.deadline.partial_cmp(&self.deadline) | |
237 } | |
238 } | |
239 | |
240 impl Ord for DeadlineInfo { | |
241 /// Implement comparisons for Deadline Info. | |
242 /// | |
243 /// Reverses the comparison because the Rust std library only | |
244 /// offers a max-heap, and we need a min-heap. | |
245 fn cmp(&self, other: &Self) -> Ordering { | |
246 other.deadline.cmp(&self.deadline) | |
247 } | |
248 } | |
249 | |
250 /// Convert a mojo deadline (which is a relative deadline to "now") to | |
251 /// an absolute deadline based on time ticks. | |
252 fn absolute_deadline(deadline: system::MojoDeadline) -> system::MojoTimeTicks { | |
253 if deadline == MOJO_INDEFINITE { | |
254 return MOJO_INDEFINITE_ABSOLUTE; | |
255 } | |
256 let mut converted = MOJO_INDEFINITE_ABSOLUTE; | |
257 let max_time_ticks = i64::MAX as system::MojoDeadline; | |
258 if deadline <= max_time_ticks { | |
259 let now = core::get_time_ticks_now(); | |
260 if deadline <= (max_time_ticks - (now as u64)) { | |
261 converted = (deadline as system::MojoTimeTicks) + now | |
262 } | |
263 } | |
264 converted | |
265 } | |
266 | |
267 /// Convert an absolute deadline to a mojo deadline which is relative to some | |
268 /// notion of "now". | |
269 /// | |
270 /// If the deadline is earlier than "now", this routine rounds up to "now". | |
271 fn relative_deadline(deadline: system::MojoTimeTicks, | |
272 now: system::MojoTimeTicks) | |
273 -> system::MojoDeadline { | |
274 if deadline == MOJO_INDEFINITE_ABSOLUTE { | |
275 MOJO_INDEFINITE | |
276 } else if now >= deadline { | |
277 0 | |
278 } else { | |
279 (deadline - now) as system::MojoDeadline | |
280 } | |
281 } | |
282 | |
283 /// This structure contains all information necessary to wait on handles | |
284 /// asynchronously. | |
285 /// | |
286 /// Ultimately, it should only be a singleton living in | |
287 /// thread-local storage. | |
288 pub struct RunLoop<'h, 't> { | |
289 /// Running count of the next available token slot. | |
290 token_count: u64, | |
291 | |
292 /// A map of handlers. | |
293 /// | |
294 /// TODO(mknyszek): Try out a Slab allocator instead of a hashmap. | |
295 handlers: HashMap<Token, HandlerInfo<'h>>, | |
296 | |
297 /// A min-heap of delayed tasks in order to pull the soonest task to | |
298 /// execute efficiently. | |
299 tasks: BinaryHeap<TaskInfo<'t>>, | |
300 | |
301 /// A min-heap containing deadlines in order to pull out the soonest | |
302 /// deadline efficiently. | |
303 /// | |
304 /// Warning: may contain "stale" deadlines which are not kept in the | |
305 /// map! | |
306 deadlines: BinaryHeap<DeadlineInfo>, | |
307 | |
308 /// The Mojo structure keeping track of handles and signals. | |
309 /// | |
310 /// This structure must be kept in sync with handlers. | |
311 handle_set: wait_set::WaitSet, | |
312 | |
313 /// A flag that tells the RunLoop whether it should quit. | |
314 should_quit: bool, | |
315 | |
316 /// A flag that indicates whether the RunLoop is running or now | |
317 running: bool, | |
318 } | |
319 | |
320 impl<'h, 't> RunLoop<'h, 't> { | |
321 /// Create a new RunLoop. | |
322 pub fn new() -> RunLoop<'h, 't> { | |
323 RunLoop { | |
324 token_count: 0, | |
325 handlers: HashMap::new(), | |
326 tasks: BinaryHeap::new(), | |
327 deadlines: BinaryHeap::new(), | |
328 handle_set: wait_set::WaitSet::new(wsflags!(Create::None)).unwrap(), | |
329 should_quit: false, | |
330 running: false, | |
331 } | |
332 } | |
333 | |
334 /// Generate a new Token for this RunLoop | |
335 fn generate_token(&mut self) -> Token { | |
336 self.token_count = self.token_count.wrapping_add(1); | |
337 Token(self.token_count) | |
338 } | |
339 | |
340 /// Adds a new entry to the runloop queue. | |
341 pub fn register<H>(&mut self, | |
342 handle: &Handle, | |
343 signals: system::HandleSignals, | |
344 deadline: system::MojoDeadline, | |
345 handler: H) | |
346 -> Token | |
347 where H: Handler + 'h | |
348 { | |
349 | |
350 let token = self.generate_token(); | |
351 let abs_deadline = absolute_deadline(deadline); | |
352 self.handle_set.add(handle, signals, token.as_cookie(), wsflags!(Add::No
ne)); | |
353 self.deadlines.push(DeadlineInfo { | |
354 token: token.clone(), | |
355 deadline: abs_deadline, | |
356 }); | |
357 debug_assert!(!self.handlers.contains_key(&token)); | |
358 self.handlers.insert(token.clone(), | |
359 HandlerInfo { | |
360 handle: handle.get_native_handle(), | |
361 handler: Some(Box::new(handler)), | |
362 deadline: abs_deadline, | |
363 }); | |
364 token | |
365 } | |
366 | |
367 /// Updates the signals and deadline of an existing handler in the | |
368 /// runloop via token. The token remains unchanged and valid. | |
369 /// | |
370 /// Returns true on a successful update and false if the token was not | |
371 /// found. | |
372 pub fn reregister(&mut self, | |
373 token: &Token, | |
374 signals: system::HandleSignals, | |
375 deadline: system::MojoDeadline) | |
376 -> bool { | |
377 | |
378 match self.handlers.get_mut(&token) { | |
379 Some(info) => { | |
380 let _result = self.handle_set.remove(token.as_cookie()); | |
381 debug_assert_eq!(_result, MojoResult::Okay); | |
382 let abs_deadline = absolute_deadline(deadline); | |
383 // Update what deadline we should be looking for, rendering | |
384 // all previously set deadlines "stale". | |
385 info.set_deadline(abs_deadline); | |
386 // Add a new deadline | |
387 self.deadlines.push(DeadlineInfo { | |
388 token: token.clone(), | |
389 deadline: abs_deadline, | |
390 }); | |
391 // Acquire the raw handle held by the HandlerInfo in order to | |
392 // call the wait_set's add method. Invalidate it immediately aft
er | |
393 // in order to prevent the handle from being closed. | |
394 // | |
395 // It's perfectly okay for the handle to be invalid, so although
this | |
396 // is all unsafe, the whole system should just call the handler
with an | |
397 // error. | |
398 let mut dummy = unsafe { system::acquire(info.handle()) }; | |
399 self.handle_set.add(&dummy, signals, token.as_cookie(), wsflags!
(Add::None)); | |
400 unsafe { | |
401 dummy.invalidate(); | |
402 } | |
403 true | |
404 } | |
405 None => false, | |
406 } | |
407 } | |
408 | |
409 /// Removes an entry from the runloop. | |
410 /// | |
411 /// Since we cannot remove from the deadlines heap, we just leave the deadli
ne | |
412 /// in there as "stale", and we handle those when trying to find the next cl
osest | |
413 /// deadline. | |
414 pub fn deregister(&mut self, token: Token) -> bool { | |
415 match self.handlers.remove(&token) { | |
416 Some(_) => { | |
417 let _result = self.handle_set.remove(token.as_cookie()); | |
418 debug_assert_eq!(_result, MojoResult::Okay); | |
419 true | |
420 } | |
421 None => false, | |
422 } | |
423 } | |
424 | |
425 /// Adds a task to be run by the runloop after some delay. | |
426 /// | |
427 /// Returns a token if the delay is valid, otherwise returns None. | |
428 pub fn post_task<F>(&mut self, task: F, delay: system::MojoTimeTicks) -> Res
ult<(), ()> | |
429 where F: FnMut(&mut RunLoop) + 't | |
430 { | |
431 let now = core::get_time_ticks_now(); | |
432 if delay > i64::MAX - now { | |
433 return Err(()); | |
434 } | |
435 let deadline = now + delay; | |
436 self.tasks.push(TaskInfo { | |
437 closure: Box::new(task), | |
438 deadline: deadline, | |
439 }); | |
440 Ok(()) | |
441 } | |
442 | |
443 /// Uses the binary heaps to get the next closest deadline. | |
444 /// | |
445 /// Removes stale deadline entries as they are found, but | |
446 /// does not otherwise modify the heap of deadlines. | |
447 fn get_next_deadline(&mut self) -> system::MojoTimeTicks { | |
448 debug_assert!(!self.handlers.is_empty()); | |
449 let top_task_deadline = match self.tasks.peek() { | |
450 Some(info) => info.deadline(), | |
451 None => MOJO_INDEFINITE_ABSOLUTE, | |
452 }; | |
453 let mut top = match self.deadlines.peek() { | |
454 Some(info) => info.clone(), | |
455 None => return MOJO_INDEFINITE_ABSOLUTE, | |
456 }; | |
457 while !self.handlers.contains_key(top.token()) { | |
458 self.deadlines.pop(); | |
459 top = match self.deadlines.peek() { | |
460 Some(info) => info.clone(), | |
461 None => return MOJO_INDEFINITE_ABSOLUTE, | |
462 } | |
463 } | |
464 if top_task_deadline != MOJO_INDEFINITE_ABSOLUTE && | |
465 top_task_deadline < top.deadline() { | |
466 top_task_deadline | |
467 } else { | |
468 top.deadline() | |
469 } | |
470 } | |
471 | |
472 /// Gets a handler by token to be manipulated in a consistent environment. | |
473 /// | |
474 /// This method provides a method of accessing a handler in order to manipul
ate | |
475 /// it in a manner that avoids a borrow cycle, that is, it take()s the handl
er | |
476 /// out of the HashMap, and returns it when manipulation has completed. | |
477 fn get_handler_with<F>(&mut self, token: &Token, invoker: F) | |
478 where F: FnOnce(&mut Self, | |
479 &mut Box<Handler + 'h>, | |
480 Token, | |
481 system::MojoTimeTicks) | |
482 { | |
483 // Logic for pulling out the handler as well as its current deadline. | |
484 // | |
485 // Unfortunately, pulling out the handler value here and "onto the stack
" | |
486 // (it probably won't actually end up on the stack thanks to optimizatio
ns) | |
487 // is necessary since otherwise the borrow checker complains that we pas
s | |
488 // a mutable reference to the RunLoop and the handler (as &mut self) to | |
489 // the callbacks at the same time. This is understandably unsafe since | |
490 // modifying the hashmap with register and deregister can invalidate the | |
491 // reference to self in the callback. In the C++ bindings and in other R
ust | |
492 // event loops this is exactly what happens, but I avoided this. The dow
nside | |
493 // is that we can no longer nest event loop run() calls. Once we put a h
andler | |
494 // onto the stack here, we can no longer call its callback in a nested m
anner | |
495 // from the RunLoop. I could just enable nesting with this one restricti
on, that | |
496 // the handler calling run() will always be ignored, but this is unintui
tive. | |
497 let (mut handler, deadline) = match self.handlers.get_mut(&token) { | |
498 Some(ref_info) => { | |
499 (match ref_info.take() { | |
500 Some(handler) => handler, | |
501 None => return, | |
502 }, | |
503 ref_info.deadline()) | |
504 } | |
505 None => return, | |
506 }; | |
507 // Call the closure that will invoke the callbacks. | |
508 invoker(self, &mut handler, token.clone(), deadline); | |
509 // Restore the handler to its location in the HashMap | |
510 if let Some(ref_info) = self.handlers.get_mut(&token) { | |
511 ref_info.give(handler); | |
512 } | |
513 } | |
514 | |
515 /// For all the results we received, we notify the respective handle | |
516 /// owners of the results by calling their given callbacks. | |
517 /// | |
518 /// We do NOT dequeue notified handles. | |
519 fn notify_of_results(&mut self, results: &Vec<system::WaitSetResult>) { | |
520 debug_assert!(!self.handlers.is_empty()); | |
521 for wsr in results.iter() { | |
522 let token = Token(wsr.cookie()); | |
523 self.get_handler_with(&token, move |runloop, boxed_handler, token, _
dl| { | |
524 let handler = boxed_handler.as_mut(); | |
525 match wsr.result() { | |
526 MojoResult::Okay => handler.on_ready(runloop, token), | |
527 MojoResult::Cancelled => { | |
528 handler.on_error(runloop, token, WaitError::HandleClosed
) | |
529 } | |
530 MojoResult::Busy => handler.on_error(runloop, token, WaitErr
or::HandleBusy), | |
531 MojoResult::FailedPrecondition => { | |
532 handler.on_error(runloop, token, WaitError::Unsatisfiabl
e) | |
533 } | |
534 other => panic!("Unexpected result received after waiting: {
}", other), | |
535 } | |
536 }); | |
537 // In order to quit as soon as possible, we should check to quit aft
er every | |
538 // potential handler call, as any of them could have signaled to qui
t. | |
539 if self.should_quit { | |
540 break; | |
541 } | |
542 } | |
543 } | |
544 | |
545 /// Since the deadline expired, we notify the relevant handle | |
546 /// owners of the expiration by calling their given callbacks. | |
547 /// | |
548 /// We do NOT dequeue notified handles. | |
549 fn notify_of_expired(&mut self, expired_deadline: system::MojoTimeTicks) { | |
550 debug_assert!(!self.handlers.is_empty()); | |
551 let mut top = match self.deadlines.peek() { | |
552 Some(info) => info.clone(), | |
553 None => panic!("Should not be in notify_of_expired without at least
one deadline!"), | |
554 }; | |
555 while expired_deadline >= top.deadline() { | |
556 let next_deadline = top.deadline(); | |
557 self.get_handler_with(top.token(), | |
558 move |runloop, boxed_handler, token, expected_
dl| { | |
559 let handler = boxed_handler.as_mut(); | |
560 if next_deadline == expected_dl { | |
561 handler.on_timeout(runloop, token); | |
562 } | |
563 }); | |
564 // In order to quit as soon as possible, we should check to quit aft
er every | |
565 // potential handler call, as any of them could have signaled to qui
t. | |
566 if self.should_quit { | |
567 break; | |
568 } | |
569 // Remove the deadline | |
570 self.deadlines.pop(); | |
571 // Break if the next deadline has not yet expired. | |
572 top = match self.deadlines.peek() { | |
573 Some(info) => info.clone(), | |
574 None => break, | |
575 }; | |
576 } | |
577 } | |
578 | |
579 /// Iterates through all tasks whose deadline has passed and executes | |
580 /// them, consuming their information object. | |
581 /// | |
582 /// These tasks all have access to the RunLoop so that they may reschedule | |
583 /// themselves or manipulate the RunLoop in some other way. | |
584 fn execute_ready_tasks(&mut self) { | |
585 let now = core::get_time_ticks_now(); | |
586 let mut deadline = match self.tasks.peek() { | |
587 Some(info) => info.deadline(), | |
588 None => return, | |
589 }; | |
590 while deadline < now { | |
591 let top = self.tasks.pop().expect("Sudden change to heap?"); | |
592 top.execute_task(self); | |
593 if self.should_quit { | |
594 return; | |
595 } | |
596 deadline = match self.tasks.peek() { | |
597 Some(info) => info.deadline(), | |
598 None => return, | |
599 }; | |
600 } | |
601 } | |
602 | |
603 /// Blocks on handle_set.wait_on_set using the information contained | |
604 /// within itself. | |
605 /// | |
606 /// This method blocks for only as long as the shortest deadline among all | |
607 /// handles this thread has registered. This method returns immediately as | |
608 /// soon as any one handle has its signals satisfied, fails to ever have its | |
609 /// signals satisfied, or reaches its deadline. | |
610 fn wait(&mut self, results_buffer: &mut Vec<system::WaitSetResult>) { | |
611 debug_assert!(!self.handlers.is_empty()); | |
612 self.execute_ready_tasks(); | |
613 // If after executing a task we quit or there are no handles, | |
614 // we have no reason to continue. | |
615 if self.handlers.is_empty() || self.should_quit { | |
616 return; | |
617 } | |
618 let deadline = self.get_next_deadline(); | |
619 let until_deadline = relative_deadline(deadline, core::get_time_ticks_no
w()); | |
620 // Perform the wait | |
621 match self.handle_set.wait_on_set(until_deadline, results_buffer) { | |
622 Ok(max_results) => { | |
623 self.notify_of_results(results_buffer); | |
624 // Clear the buffer since we don't need the results anymore. | |
625 // Helps prevent a copy if we resize the buffer. | |
626 results_buffer.clear(); | |
627 // Increase the size of the buffer if there are more results | |
628 // we could be holding. | |
629 let capacity = results_buffer.capacity(); | |
630 if capacity < MAXIMUM_WAIT_SET_NUM_RESULTS && capacity < (max_re
sults) as usize { | |
631 results_buffer.reserve(capacity); | |
632 } | |
633 } | |
634 Err(result) => { | |
635 assert_eq!(result, MojoResult::DeadlineExceeded); | |
636 self.notify_of_expired(deadline); | |
637 } | |
638 } | |
639 } | |
640 | |
641 /// Loop forever until a callback tells us to quit. | |
642 pub fn run(&mut self) { | |
643 // It's an error it already be running... | |
644 if self.running { | |
645 panic!("RunLoop is already running!"); | |
646 } | |
647 self.running = true; | |
648 self.should_quit = false; | |
649 let mut buffer: Vec<system::WaitSetResult> = | |
650 Vec::with_capacity(INITIAL_WAIT_SET_NUM_RESULTS); | |
651 // Loop while we haven't been signaled to quit, and there's something to
wait on. | |
652 while !self.should_quit && !self.handlers.is_empty() { | |
653 self.wait(&mut buffer) | |
654 } | |
655 self.running = false; | |
656 } | |
657 | |
658 /// Set a flag to quit at the next available moment. | |
659 pub fn quit(&mut self) { | |
660 self.should_quit = true; | |
661 } | |
662 } | |
663 | |
664 /// Provides a scope to modify the current thread's runloop. | |
665 pub fn with_current<F>(modifier: F) | |
666 where F: FnOnce(&mut RunLoop) | |
667 { | |
668 TL_RUN_LOOP.with(|ref_runloop| { | |
669 let mut runloop = ref_runloop.borrow_mut(); | |
670 modifier(&mut *runloop); | |
671 }); | |
672 } | |
OLD | NEW |