Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 //! This module contains a thread-local run-loop. | 5 //! This module contains a thread-local run-loop. |
| 6 //! | 6 //! |
| 7 //! The run-loop may have handles and handlers pre-registers | 7 //! The run-loop may have handles and handlers pre-registers |
| 8 //! (and in fact, must) in order to keep running. The run-loop | 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, | 9 //! executes until it has no more handles or handlers on itself, |
| 10 //! or until it is told to quit via stop(). | 10 //! or until it is told to quit via stop(). |
| 11 //! | 11 //! |
| 12 //! The run-loop waits until some signals on some handle is satisfied, | 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 | 13 //! at which point it wakes up and executes the appropriate handler |
| 14 //! method. This handler method may then be used to further populate | 14 //! method. This handler method may then be used to further populate |
| 15 //! or de-populate the run-loop. | 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. | |
| 16 | 21 |
| 17 use std::cell::RefCell; | 22 use std::cell::RefCell; |
| 18 use std::cmp::{PartialEq, Eq, PartialOrd, Ord, Ordering}; | 23 use std::cmp::{PartialEq, Eq, PartialOrd, Ord, Ordering}; |
| 19 use std::collections::BinaryHeap; | 24 use std::collections::BinaryHeap; |
| 20 use std::collections::HashMap; | 25 use std::collections::HashMap; |
| 21 use std::i64; | 26 use std::i64; |
| 22 use std::u32; | 27 use std::u32; |
| 23 use std::vec::Vec; | 28 use std::vec::Vec; |
| 24 | 29 |
| 25 use system; | 30 use system; |
| 26 use system::{Handle, MOJO_INDEFINITE, MojoResult}; | 31 use system::{Handle, MOJO_INDEFINITE, MojoResult}; |
| 27 use system::core; | 32 use system::core; |
| 28 use system::wait_set; | 33 use system::wait_set; |
| 29 | 34 |
| 30 /// Define the equivalent of MOJO_INDEFINITE for absolute deadlines | 35 /// Define the equivalent of MOJO_INDEFINITE for absolute deadlines |
| 31 const MOJO_INDEFINITE_ABSOLUTE: system::MojoTimeTicks = 0; | 36 const MOJO_INDEFINITE_ABSOLUTE: system::MojoTimeTicks = 0; |
| 32 | 37 |
| 33 // TODO(mknyszek): The numbers below are arbitrary and come from the C++ binding s, | 38 // TODO(mknyszek): The numbers below are arbitrary and come from the C++ binding s, |
| 34 // and should probably be changed at some point | 39 // and should probably be changed at some point |
| 35 | 40 |
| 36 /// Initial size of the result buffer. | 41 /// Initial size of the result buffer. |
| 37 const INITIAL_WAIT_SET_NUM_RESULTS: usize = 16; | 42 const INITIAL_WAIT_SET_NUM_RESULTS: usize = 16; |
| 38 | 43 |
| 39 /// Maximum size of the result buffer. | 44 /// Maximum size of the result buffer. |
| 40 const MAXIMUM_WAIT_SET_NUM_RESULTS: usize = 256; | 45 const MAXIMUM_WAIT_SET_NUM_RESULTS: usize = 256; |
| 41 | 46 |
| 42 /// Thread-local data structure for keeping track of handles to wait on. | 47 /// Thread-local data structure for keeping track of handles to wait on. |
| 43 thread_local!(static TL_RUN_LOOP: RefCell<RunLoop<'static>> = RefCell::new(RunLo op::new())); | 48 thread_local!(static TL_RUN_LOOP: RefCell<RunLoop<'static, 'static>> = RefCell:: new(RunLoop::new())); |
| 44 | 49 |
| 45 /// Token representing handle/callback to wait on for this thread only. This | 50 /// Token representing handle/callback to wait on for this thread only. This |
| 46 /// token only has meaning on the thread in which the handle was registered. | 51 /// token only has meaning on the thread in which the handle was registered. |
| 47 #[derive(Clone, Debug, PartialEq, Eq, Hash)] | 52 #[derive(Clone, Debug, PartialEq, Eq, Hash)] |
| 48 pub struct Token(u64); | 53 pub struct Token(u64); |
| 49 | 54 |
| 50 impl Token { | 55 impl Token { |
| 51 /// Get the wait token's "cookie" form, suitable for use in a wait set. | 56 /// Get the wait token's "cookie" form, suitable for use in a wait set. |
| 52 fn as_cookie(&self) -> u64 { | 57 fn as_cookie(&self) -> u64 { |
| 53 self.0 | 58 self.0 |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 126 pub fn deadline(&self) -> system::MojoTimeTicks { | 131 pub fn deadline(&self) -> system::MojoTimeTicks { |
| 127 self.deadline | 132 self.deadline |
| 128 } | 133 } |
| 129 | 134 |
| 130 /// Setter to update the current absolute deadline. | 135 /// Setter to update the current absolute deadline. |
| 131 pub fn set_deadline(&mut self, deadline: system::MojoTimeTicks) { | 136 pub fn set_deadline(&mut self, deadline: system::MojoTimeTicks) { |
| 132 self.deadline = deadline | 137 self.deadline = deadline |
| 133 } | 138 } |
| 134 } | 139 } |
| 135 | 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<Fn(&mut RunLoop) + 't>, | |
|
Eric Holk
2016/08/12 01:36:31
Can this be an FnOnce? A lot of times that gives y
mknyszek
2016/08/12 08:09:31
That's true, but unfortunately Rust doesn't suppor
Eric Holk
2016/08/12 16:35:07
Sounds good!
| |
| 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(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 | |
| 136 /// Wrapper struct intended to be used in a priority queue | 197 /// Wrapper struct intended to be used in a priority queue |
| 137 /// for efficiently retrieving the next closest deadline. | 198 /// for efficiently retrieving the next closest deadline. |
| 138 #[derive(Clone)] | 199 #[derive(Clone)] |
| 139 struct DeadlineInfo { | 200 struct DeadlineInfo { |
| 140 /// The ID of the associated Handler struct in the RunLoop's | 201 /// The ID of the associated Handler struct in the RunLoop's |
| 141 /// hash map. | 202 /// hash map. |
| 142 token: Token, | 203 token: Token, |
| 143 | 204 |
| 144 /// An absolute deadline in terms of time ticks. | 205 /// An absolute deadline in terms of time ticks. |
| 145 deadline: system::MojoTimeTicks, | 206 deadline: system::MojoTimeTicks, |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 217 } else { | 278 } else { |
| 218 (deadline - now) as system::MojoDeadline | 279 (deadline - now) as system::MojoDeadline |
| 219 } | 280 } |
| 220 } | 281 } |
| 221 | 282 |
| 222 /// This structure contains all information necessary to wait on handles | 283 /// This structure contains all information necessary to wait on handles |
| 223 /// asynchronously. | 284 /// asynchronously. |
| 224 /// | 285 /// |
| 225 /// Ultimately, it should only be a singleton living in | 286 /// Ultimately, it should only be a singleton living in |
| 226 /// thread-local storage. | 287 /// thread-local storage. |
| 227 pub struct RunLoop<'h> { | 288 pub struct RunLoop<'h, 't> { |
| 228 /// Running count of the next available token slot. | 289 /// Running count of the next available token slot. |
| 229 token_count: u64, | 290 token_count: u64, |
| 230 | 291 |
| 231 /// A map of handlers. | 292 /// A map of handlers. |
| 232 /// | 293 /// |
| 233 /// TODO(mknyszek): Try out a Slab allocator instead of a hashmap. | 294 /// TODO(mknyszek): Try out a Slab allocator instead of a hashmap. |
| 234 handlers: HashMap<Token, HandlerInfo<'h>>, | 295 handlers: HashMap<Token, HandlerInfo<'h>>, |
| 235 | 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 | |
| 236 /// A min-heap containing deadlines in order to pull out the soonest | 301 /// A min-heap containing deadlines in order to pull out the soonest |
| 237 /// deadline efficiently. | 302 /// deadline efficiently. |
| 238 /// | 303 /// |
| 239 /// Warning: may contain "stale" deadlines which are not kept in the | 304 /// Warning: may contain "stale" deadlines which are not kept in the |
| 240 /// map! | 305 /// map! |
| 241 deadlines: BinaryHeap<DeadlineInfo>, | 306 deadlines: BinaryHeap<DeadlineInfo>, |
| 242 | 307 |
| 243 /// The Mojo structure keeping track of handles and signals. | 308 /// The Mojo structure keeping track of handles and signals. |
| 244 /// | 309 /// |
| 245 /// This structure must be kept in sync with handlers. | 310 /// This structure must be kept in sync with handlers. |
| 246 handle_set: wait_set::WaitSet, | 311 handle_set: wait_set::WaitSet, |
| 247 | 312 |
| 248 /// A flag that tells the RunLoop whether it should quit. | 313 /// A flag that tells the RunLoop whether it should quit. |
| 249 should_quit: bool, | 314 should_quit: bool, |
| 250 | 315 |
| 251 /// A flag that indicates whether the RunLoop is running or now | 316 /// A flag that indicates whether the RunLoop is running or now |
| 252 running: bool, | 317 running: bool, |
| 253 } | 318 } |
| 254 | 319 |
| 255 impl<'h> RunLoop<'h> { | 320 impl<'h, 't> RunLoop<'h, 't> { |
| 256 /// Create a new RunLoop. | 321 /// Create a new RunLoop. |
| 257 pub fn new() -> RunLoop<'h> { | 322 pub fn new() -> RunLoop<'h, 't> { |
| 258 RunLoop { | 323 RunLoop { |
| 259 token_count: 0, | 324 token_count: 0, |
| 260 handlers: HashMap::new(), | 325 handlers: HashMap::new(), |
| 326 tasks: BinaryHeap::new(), | |
| 261 deadlines: BinaryHeap::new(), | 327 deadlines: BinaryHeap::new(), |
| 262 handle_set: wait_set::WaitSet::new(wsflags!(Create::None)).unwrap(), | 328 handle_set: wait_set::WaitSet::new(wsflags!(Create::None)).unwrap(), |
| 263 should_quit: false, | 329 should_quit: false, |
| 264 running: false, | 330 running: false, |
| 265 } | 331 } |
| 266 } | 332 } |
| 267 | 333 |
| 268 /// Generate a new Token for this RunLoop | 334 /// Generate a new Token for this RunLoop |
| 269 fn generate_token(&mut self) -> Token { | 335 fn generate_token(&mut self) -> Token { |
| 270 self.token_count = self.token_count.wrapping_add(1); | 336 self.token_count = self.token_count.wrapping_add(1); |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 349 match self.handlers.remove(&token) { | 415 match self.handlers.remove(&token) { |
| 350 Some(_) => { | 416 Some(_) => { |
| 351 let _result = self.handle_set.remove(token.as_cookie()); | 417 let _result = self.handle_set.remove(token.as_cookie()); |
| 352 debug_assert_eq!(_result, MojoResult::Okay); | 418 debug_assert_eq!(_result, MojoResult::Okay); |
| 353 true | 419 true |
| 354 } | 420 } |
| 355 None => false, | 421 None => false, |
| 356 } | 422 } |
| 357 } | 423 } |
| 358 | 424 |
| 359 /// Uses the binary heap to get the next closest deadline. | 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: Fn(&mut RunLoop) + 't | |
|
Eric Holk
2016/08/12 01:36:31
If you make the previous one an FnOnce, I think yo
| |
| 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. | |
| 360 /// | 444 /// |
| 361 /// Removes stale deadline entries as they are found, but | 445 /// Removes stale deadline entries as they are found, but |
| 362 /// does not otherwise modify the heap of deadlines. | 446 /// does not otherwise modify the heap of deadlines. |
| 363 fn get_next_deadline(&mut self) -> system::MojoTimeTicks { | 447 fn get_next_deadline(&mut self) -> system::MojoTimeTicks { |
| 364 debug_assert!(!self.handlers.is_empty()); | 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 }; | |
| 365 let mut top = match self.deadlines.peek() { | 453 let mut top = match self.deadlines.peek() { |
| 366 Some(info) => info.clone(), | 454 Some(info) => info.clone(), |
| 367 None => return MOJO_INDEFINITE_ABSOLUTE, | 455 None => return MOJO_INDEFINITE_ABSOLUTE, |
| 368 }; | 456 }; |
| 369 while !self.handlers.contains_key(top.token()) { | 457 while !self.handlers.contains_key(top.token()) { |
| 370 self.deadlines.pop(); | 458 self.deadlines.pop(); |
| 371 top = match self.deadlines.peek() { | 459 top = match self.deadlines.peek() { |
| 372 Some(info) => info.clone(), | 460 Some(info) => info.clone(), |
| 373 None => return MOJO_INDEFINITE_ABSOLUTE, | 461 None => return MOJO_INDEFINITE_ABSOLUTE, |
| 374 } | 462 } |
| 375 } | 463 } |
| 376 top.deadline() | 464 if top_task_deadline != MOJO_INDEFINITE_ABSOLUTE && |
| 465 top_task_deadline < top.deadline() { | |
| 466 top_task_deadline | |
| 467 } else { | |
| 468 top.deadline() | |
| 469 } | |
| 377 } | 470 } |
| 378 | 471 |
| 379 /// Gets a handler by token to be manipulated in a consistent environment. | 472 /// Gets a handler by token to be manipulated in a consistent environment. |
| 380 /// | 473 /// |
| 381 /// This method provides a method of accessing a handler in order to manipul ate | 474 /// This method provides a method of accessing a handler in order to manipul ate |
| 382 /// it in a manner that avoids a borrow cycle, that is, it take()s the handl er | 475 /// it in a manner that avoids a borrow cycle, that is, it take()s the handl er |
| 383 /// out of the HashMap, and returns it when manipulation has completed. | 476 /// out of the HashMap, and returns it when manipulation has completed. |
| 384 fn get_handler_with<F>(&mut self, token: &Token, invoker: F) | 477 fn get_handler_with<F>(&mut self, token: &Token, invoker: F) |
| 385 where F: FnOnce(&mut Self, | 478 where F: FnOnce(&mut Self, |
| 386 &mut Box<Handler + 'h>, | 479 &mut Box<Handler + 'h>, |
| (...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 476 // Remove the deadline | 569 // Remove the deadline |
| 477 self.deadlines.pop(); | 570 self.deadlines.pop(); |
| 478 // Break if the next deadline has not yet expired. | 571 // Break if the next deadline has not yet expired. |
| 479 top = match self.deadlines.peek() { | 572 top = match self.deadlines.peek() { |
| 480 Some(info) => info.clone(), | 573 Some(info) => info.clone(), |
| 481 None => break, | 574 None => break, |
| 482 }; | 575 }; |
| 483 } | 576 } |
| 484 } | 577 } |
| 485 | 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 | |
| 486 /// Blocks on handle_set.wait_on_set using the information contained | 603 /// Blocks on handle_set.wait_on_set using the information contained |
| 487 /// within itself. | 604 /// within itself. |
| 488 /// | 605 /// |
| 489 /// This method blocks for only as long as the shortest deadline among all | 606 /// This method blocks for only as long as the shortest deadline among all |
| 490 /// handles this thread has registered. This method returns immediately as | 607 /// handles this thread has registered. This method returns immediately as |
| 491 /// soon as any one handle has its signals satisfied, fails to ever have its | 608 /// soon as any one handle has its signals satisfied, fails to ever have its |
| 492 /// signals satisfied, or reaches its deadline. | 609 /// signals satisfied, or reaches its deadline. |
| 493 fn wait(&mut self, results_buffer: &mut Vec<system::WaitSetResult>) { | 610 fn wait(&mut self, results_buffer: &mut Vec<system::WaitSetResult>) { |
| 494 debug_assert!(!self.handlers.is_empty()); | 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 } | |
| 495 let deadline = self.get_next_deadline(); | 618 let deadline = self.get_next_deadline(); |
| 496 let until_deadline = relative_deadline(deadline, core::get_time_ticks_no w()); | 619 let until_deadline = relative_deadline(deadline, core::get_time_ticks_no w()); |
| 497 // Perform the wait | 620 // Perform the wait |
| 498 match self.handle_set.wait_on_set(until_deadline, results_buffer) { | 621 match self.handle_set.wait_on_set(until_deadline, results_buffer) { |
| 499 Ok(max_results) => { | 622 Ok(max_results) => { |
| 500 self.notify_of_results(results_buffer); | 623 self.notify_of_results(results_buffer); |
| 501 // Clear the buffer since we don't need the results anymore. | 624 // Clear the buffer since we don't need the results anymore. |
| 502 // Helps prevent a copy if we resize the buffer. | 625 // Helps prevent a copy if we resize the buffer. |
| 503 results_buffer.clear(); | 626 results_buffer.clear(); |
| 504 // Increase the size of the buffer if there are more results | 627 // Increase the size of the buffer if there are more results |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 540 | 663 |
| 541 /// Provides a scope to modify the current thread's runloop. | 664 /// Provides a scope to modify the current thread's runloop. |
| 542 pub fn with_current<F>(modifier: F) | 665 pub fn with_current<F>(modifier: F) |
| 543 where F: FnOnce(&mut RunLoop) | 666 where F: FnOnce(&mut RunLoop) |
| 544 { | 667 { |
| 545 TL_RUN_LOOP.with(|ref_runloop| { | 668 TL_RUN_LOOP.with(|ref_runloop| { |
| 546 let mut runloop = ref_runloop.borrow_mut(); | 669 let mut runloop = ref_runloop.borrow_mut(); |
| 547 modifier(&mut *runloop); | 670 modifier(&mut *runloop); |
| 548 }); | 671 }); |
| 549 } | 672 } |
| OLD | NEW |