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

Unified Diff: mojo/public/rust/src/bindings/run_loop.rs

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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « mojo/public/rust/src/bindings/mojom.rs ('k') | mojo/public/rust/src/bindings/util.rs » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: mojo/public/rust/src/bindings/run_loop.rs
diff --git a/mojo/public/rust/src/bindings/run_loop.rs b/mojo/public/rust/src/bindings/run_loop.rs
deleted file mode 100644
index 754694ed3d2d4d94def3b4a39c1bc255375ef53e..0000000000000000000000000000000000000000
--- a/mojo/public/rust/src/bindings/run_loop.rs
+++ /dev/null
@@ -1,672 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-//! This module contains a thread-local run-loop.
-//!
-//! The run-loop may have handles and handlers pre-registers
-//! (and in fact, must) in order to keep running. The run-loop
-//! executes until it has no more handles or handlers on itself,
-//! or until it is told to quit via stop().
-//!
-//! The run-loop waits until some signals on some handle is satisfied,
-//! at which point it wakes up and executes the appropriate handler
-//! method. This handler method may then be used to further populate
-//! or de-populate the run-loop.
-//!
-//! As of yet, the run-loop is NOT thread-safe. Although it is useful
-//! to be able to register tasks or handles from one thread onto
-//! another thread's run-loop, this is as-of-yet unsupported, and
-//! Rust should complain loudly when you try to do any threading here.
-
-use std::cell::RefCell;
-use std::cmp::{PartialEq, Eq, PartialOrd, Ord, Ordering};
-use std::collections::BinaryHeap;
-use std::collections::HashMap;
-use std::i64;
-use std::u32;
-use std::vec::Vec;
-
-use system;
-use system::{Handle, MOJO_INDEFINITE, MojoResult};
-use system::core;
-use system::wait_set;
-
-/// Define the equivalent of MOJO_INDEFINITE for absolute deadlines
-const MOJO_INDEFINITE_ABSOLUTE: system::MojoTimeTicks = 0;
-
-// TODO(mknyszek): The numbers below are arbitrary and come from the C++ bindings,
-// and should probably be changed at some point
-
-/// Initial size of the result buffer.
-const INITIAL_WAIT_SET_NUM_RESULTS: usize = 16;
-
-/// Maximum size of the result buffer.
-const MAXIMUM_WAIT_SET_NUM_RESULTS: usize = 256;
-
-/// Thread-local data structure for keeping track of handles to wait on.
-thread_local!(static TL_RUN_LOOP: RefCell<RunLoop<'static, 'static>> = RefCell::new(RunLoop::new()));
-
-/// Token representing handle/callback to wait on for this thread only. This
-/// token only has meaning on the thread in which the handle was registered.
-#[derive(Clone, Debug, PartialEq, Eq, Hash)]
-pub struct Token(u64);
-
-impl Token {
- /// Get the wait token's "cookie" form, suitable for use in a wait set.
- fn as_cookie(&self) -> u64 {
- self.0
- }
-}
-
-/// Represents the possible error cases that may occur when waiting
-/// on a handle in a RunLoop.
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub enum WaitError {
- /// The handle has been closed or is otherwise no longer valid.
- HandleClosed,
-
- /// The handle is currently busy in some transaction.
- HandleBusy,
-
- /// It has been determined that the signals provided will never
- /// be satisfied for this handle.
- Unsatisfiable,
-}
-
-/// A trait which defines an interface to be a handler usable by
-/// a RunLoop.
-pub trait Handler {
- /// Called after a successful wait.
- fn on_ready(&mut self, runloop: &mut RunLoop, token: Token);
-
- /// Called after the given deadline expires.
- fn on_timeout(&mut self, runloop: &mut RunLoop, token: Token);
-
- /// Called when an unexpected error occurs.
- fn on_error(&mut self, runloop: &mut RunLoop, token: Token, error: WaitError);
-}
-
-/// A wrapper struct for carrying the handler as well as various information
-/// about it.
-struct HandlerInfo<'h> {
- /// The handle for which we are waiting.
- ///
- /// We keep this handle around so that we may easily re-register.
- handle: system::MojoHandle,
-
- /// The handler, boxed up.
- ///
- /// The handler is in an Option type because if it is currently being
- /// used in a callback, we must take ownership to avoid mutability
- /// cycles. The easiest way to do this is to take() from the Option then
- /// put it back.
- handler: Option<Box<Handler + 'h>>,
-
- /// An absolute deadline in terms of time ticks.
- ///
- /// This is the most recently updated deadline that
- /// we should be watching out for. All others for this
- /// token may be considered "stale".
- deadline: system::MojoTimeTicks,
-}
-
-impl<'h> HandlerInfo<'h> {
- /// Take the handler out of its Option type.
- pub fn take(&mut self) -> Option<Box<Handler + 'h>> {
- self.handler.take()
- }
-
- /// Put a new handler into the Option type.
- pub fn give(&mut self, handler: Box<Handler + 'h>) {
- self.handler = Some(handler);
- }
-
- /// Getter for the system::MojoHandle held inside.
- pub fn handle(&self) -> system::MojoHandle {
- self.handle
- }
-
- /// Getter for the current absolute deadline held inside.
- pub fn deadline(&self) -> system::MojoTimeTicks {
- self.deadline
- }
-
- /// Setter to update the current absolute deadline.
- pub fn set_deadline(&mut self, deadline: system::MojoTimeTicks) {
- self.deadline = deadline
- }
-}
-
-/// A wrapper struct for carrying the task as well as some information about
-/// it.
-struct TaskInfo<'t> {
- /// The task, boxed up.
- closure: Box<FnMut(&mut RunLoop) + 't>,
-
- /// An absolute deadline in terms of time ticks.
- ///
- /// This is the most recently updated deadline that
- /// we should be watching out for. All others for this
- /// token may be considered "stale".
- deadline: system::MojoTimeTicks,
-}
-
-impl<'t> TaskInfo<'t> {
- /// Executes the task within the info object, consuming it
- /// in the process.
- pub fn execute_task(mut self, run_loop: &mut RunLoop) {
- (*self.closure)(run_loop);
- }
-
- /// Getter for the current absolute deadline held inside.
- pub fn deadline(&self) -> system::MojoTimeTicks {
- self.deadline
- }
-}
-
-impl<'t> PartialEq for TaskInfo<'t> {
- /// Equality for TaskInfo in terms of its deadline
- fn eq(&self, other: &TaskInfo) -> bool {
- self.deadline == other.deadline
- }
-}
-
-impl<'t> Eq for TaskInfo<'t> {}
-
-impl<'t> PartialOrd for TaskInfo<'t> {
- /// Partial comparison for TaskInfo in terms of its deadline
- ///
- /// Reverses the comparison because the Rust std library only
- /// offers a max-heap, and we need a min-heap.
- fn partial_cmp(&self, other: &TaskInfo) -> Option<Ordering> {
- other.deadline.partial_cmp(&self.deadline)
- }
-}
-
-impl<'t> Ord for TaskInfo<'t> {
- /// Implement comparisons for Task Info.
- ///
- /// Reverses the comparison because the Rust std library only
- /// offers a max-heap, and we need a min-heap.
- fn cmp(&self, other: &Self) -> Ordering {
- other.deadline.cmp(&self.deadline)
- }
-}
-
-/// Wrapper struct intended to be used in a priority queue
-/// for efficiently retrieving the next closest deadline.
-#[derive(Clone)]
-struct DeadlineInfo {
- /// The ID of the associated Handler struct in the RunLoop's
- /// hash map.
- token: Token,
-
- /// An absolute deadline in terms of time ticks.
- deadline: system::MojoTimeTicks,
-}
-
-impl DeadlineInfo {
- /// Getter for an immutable borrow for the token inside.
- pub fn token(&self) -> &Token {
- &self.token
- }
-
- /// Getter for the absolute deadline inside.
- pub fn deadline(&self) -> system::MojoTimeTicks {
- self.deadline
- }
-}
-
-impl PartialEq for DeadlineInfo {
- /// Equality for DeadlineInfo in terms of its deadline
- fn eq(&self, other: &DeadlineInfo) -> bool {
- self.deadline == other.deadline
- }
-}
-
-impl Eq for DeadlineInfo {}
-
-impl PartialOrd for DeadlineInfo {
- /// Partial comparison for DeadlineInfo in terms of its deadline
- ///
- /// Reverses the comparison because the Rust std library only
- /// offers a max-heap, and we need a min-heap.
- fn partial_cmp(&self, other: &DeadlineInfo) -> Option<Ordering> {
- other.deadline.partial_cmp(&self.deadline)
- }
-}
-
-impl Ord for DeadlineInfo {
- /// Implement comparisons for Deadline Info.
- ///
- /// Reverses the comparison because the Rust std library only
- /// offers a max-heap, and we need a min-heap.
- fn cmp(&self, other: &Self) -> Ordering {
- other.deadline.cmp(&self.deadline)
- }
-}
-
-/// Convert a mojo deadline (which is a relative deadline to "now") to
-/// an absolute deadline based on time ticks.
-fn absolute_deadline(deadline: system::MojoDeadline) -> system::MojoTimeTicks {
- if deadline == MOJO_INDEFINITE {
- return MOJO_INDEFINITE_ABSOLUTE;
- }
- let mut converted = MOJO_INDEFINITE_ABSOLUTE;
- let max_time_ticks = i64::MAX as system::MojoDeadline;
- if deadline <= max_time_ticks {
- let now = core::get_time_ticks_now();
- if deadline <= (max_time_ticks - (now as u64)) {
- converted = (deadline as system::MojoTimeTicks) + now
- }
- }
- converted
-}
-
-/// Convert an absolute deadline to a mojo deadline which is relative to some
-/// notion of "now".
-///
-/// If the deadline is earlier than "now", this routine rounds up to "now".
-fn relative_deadline(deadline: system::MojoTimeTicks,
- now: system::MojoTimeTicks)
- -> system::MojoDeadline {
- if deadline == MOJO_INDEFINITE_ABSOLUTE {
- MOJO_INDEFINITE
- } else if now >= deadline {
- 0
- } else {
- (deadline - now) as system::MojoDeadline
- }
-}
-
-/// This structure contains all information necessary to wait on handles
-/// asynchronously.
-///
-/// Ultimately, it should only be a singleton living in
-/// thread-local storage.
-pub struct RunLoop<'h, 't> {
- /// Running count of the next available token slot.
- token_count: u64,
-
- /// A map of handlers.
- ///
- /// TODO(mknyszek): Try out a Slab allocator instead of a hashmap.
- handlers: HashMap<Token, HandlerInfo<'h>>,
-
- /// A min-heap of delayed tasks in order to pull the soonest task to
- /// execute efficiently.
- tasks: BinaryHeap<TaskInfo<'t>>,
-
- /// A min-heap containing deadlines in order to pull out the soonest
- /// deadline efficiently.
- ///
- /// Warning: may contain "stale" deadlines which are not kept in the
- /// map!
- deadlines: BinaryHeap<DeadlineInfo>,
-
- /// The Mojo structure keeping track of handles and signals.
- ///
- /// This structure must be kept in sync with handlers.
- handle_set: wait_set::WaitSet,
-
- /// A flag that tells the RunLoop whether it should quit.
- should_quit: bool,
-
- /// A flag that indicates whether the RunLoop is running or now
- running: bool,
-}
-
-impl<'h, 't> RunLoop<'h, 't> {
- /// Create a new RunLoop.
- pub fn new() -> RunLoop<'h, 't> {
- RunLoop {
- token_count: 0,
- handlers: HashMap::new(),
- tasks: BinaryHeap::new(),
- deadlines: BinaryHeap::new(),
- handle_set: wait_set::WaitSet::new(wsflags!(Create::None)).unwrap(),
- should_quit: false,
- running: false,
- }
- }
-
- /// Generate a new Token for this RunLoop
- fn generate_token(&mut self) -> Token {
- self.token_count = self.token_count.wrapping_add(1);
- Token(self.token_count)
- }
-
- /// Adds a new entry to the runloop queue.
- pub fn register<H>(&mut self,
- handle: &Handle,
- signals: system::HandleSignals,
- deadline: system::MojoDeadline,
- handler: H)
- -> Token
- where H: Handler + 'h
- {
-
- let token = self.generate_token();
- let abs_deadline = absolute_deadline(deadline);
- self.handle_set.add(handle, signals, token.as_cookie(), wsflags!(Add::None));
- self.deadlines.push(DeadlineInfo {
- token: token.clone(),
- deadline: abs_deadline,
- });
- debug_assert!(!self.handlers.contains_key(&token));
- self.handlers.insert(token.clone(),
- HandlerInfo {
- handle: handle.get_native_handle(),
- handler: Some(Box::new(handler)),
- deadline: abs_deadline,
- });
- token
- }
-
- /// Updates the signals and deadline of an existing handler in the
- /// runloop via token. The token remains unchanged and valid.
- ///
- /// Returns true on a successful update and false if the token was not
- /// found.
- pub fn reregister(&mut self,
- token: &Token,
- signals: system::HandleSignals,
- deadline: system::MojoDeadline)
- -> bool {
-
- match self.handlers.get_mut(&token) {
- Some(info) => {
- let _result = self.handle_set.remove(token.as_cookie());
- debug_assert_eq!(_result, MojoResult::Okay);
- let abs_deadline = absolute_deadline(deadline);
- // Update what deadline we should be looking for, rendering
- // all previously set deadlines "stale".
- info.set_deadline(abs_deadline);
- // Add a new deadline
- self.deadlines.push(DeadlineInfo {
- token: token.clone(),
- deadline: abs_deadline,
- });
- // Acquire the raw handle held by the HandlerInfo in order to
- // call the wait_set's add method. Invalidate it immediately after
- // in order to prevent the handle from being closed.
- //
- // It's perfectly okay for the handle to be invalid, so although this
- // is all unsafe, the whole system should just call the handler with an
- // error.
- let mut dummy = unsafe { system::acquire(info.handle()) };
- self.handle_set.add(&dummy, signals, token.as_cookie(), wsflags!(Add::None));
- unsafe {
- dummy.invalidate();
- }
- true
- }
- None => false,
- }
- }
-
- /// Removes an entry from the runloop.
- ///
- /// Since we cannot remove from the deadlines heap, we just leave the deadline
- /// in there as "stale", and we handle those when trying to find the next closest
- /// deadline.
- pub fn deregister(&mut self, token: Token) -> bool {
- match self.handlers.remove(&token) {
- Some(_) => {
- let _result = self.handle_set.remove(token.as_cookie());
- debug_assert_eq!(_result, MojoResult::Okay);
- true
- }
- None => false,
- }
- }
-
- /// Adds a task to be run by the runloop after some delay.
- ///
- /// Returns a token if the delay is valid, otherwise returns None.
- pub fn post_task<F>(&mut self, task: F, delay: system::MojoTimeTicks) -> Result<(), ()>
- where F: FnMut(&mut RunLoop) + 't
- {
- let now = core::get_time_ticks_now();
- if delay > i64::MAX - now {
- return Err(());
- }
- let deadline = now + delay;
- self.tasks.push(TaskInfo {
- closure: Box::new(task),
- deadline: deadline,
- });
- Ok(())
- }
-
- /// Uses the binary heaps to get the next closest deadline.
- ///
- /// Removes stale deadline entries as they are found, but
- /// does not otherwise modify the heap of deadlines.
- fn get_next_deadline(&mut self) -> system::MojoTimeTicks {
- debug_assert!(!self.handlers.is_empty());
- let top_task_deadline = match self.tasks.peek() {
- Some(info) => info.deadline(),
- None => MOJO_INDEFINITE_ABSOLUTE,
- };
- let mut top = match self.deadlines.peek() {
- Some(info) => info.clone(),
- None => return MOJO_INDEFINITE_ABSOLUTE,
- };
- while !self.handlers.contains_key(top.token()) {
- self.deadlines.pop();
- top = match self.deadlines.peek() {
- Some(info) => info.clone(),
- None => return MOJO_INDEFINITE_ABSOLUTE,
- }
- }
- if top_task_deadline != MOJO_INDEFINITE_ABSOLUTE &&
- top_task_deadline < top.deadline() {
- top_task_deadline
- } else {
- top.deadline()
- }
- }
-
- /// Gets a handler by token to be manipulated in a consistent environment.
- ///
- /// This method provides a method of accessing a handler in order to manipulate
- /// it in a manner that avoids a borrow cycle, that is, it take()s the handler
- /// out of the HashMap, and returns it when manipulation has completed.
- fn get_handler_with<F>(&mut self, token: &Token, invoker: F)
- where F: FnOnce(&mut Self,
- &mut Box<Handler + 'h>,
- Token,
- system::MojoTimeTicks)
- {
- // Logic for pulling out the handler as well as its current deadline.
- //
- // Unfortunately, pulling out the handler value here and "onto the stack"
- // (it probably won't actually end up on the stack thanks to optimizations)
- // is necessary since otherwise the borrow checker complains that we pass
- // a mutable reference to the RunLoop and the handler (as &mut self) to
- // the callbacks at the same time. This is understandably unsafe since
- // modifying the hashmap with register and deregister can invalidate the
- // reference to self in the callback. In the C++ bindings and in other Rust
- // event loops this is exactly what happens, but I avoided this. The downside
- // is that we can no longer nest event loop run() calls. Once we put a handler
- // onto the stack here, we can no longer call its callback in a nested manner
- // from the RunLoop. I could just enable nesting with this one restriction, that
- // the handler calling run() will always be ignored, but this is unintuitive.
- let (mut handler, deadline) = match self.handlers.get_mut(&token) {
- Some(ref_info) => {
- (match ref_info.take() {
- Some(handler) => handler,
- None => return,
- },
- ref_info.deadline())
- }
- None => return,
- };
- // Call the closure that will invoke the callbacks.
- invoker(self, &mut handler, token.clone(), deadline);
- // Restore the handler to its location in the HashMap
- if let Some(ref_info) = self.handlers.get_mut(&token) {
- ref_info.give(handler);
- }
- }
-
- /// For all the results we received, we notify the respective handle
- /// owners of the results by calling their given callbacks.
- ///
- /// We do NOT dequeue notified handles.
- fn notify_of_results(&mut self, results: &Vec<system::WaitSetResult>) {
- debug_assert!(!self.handlers.is_empty());
- for wsr in results.iter() {
- let token = Token(wsr.cookie());
- self.get_handler_with(&token, move |runloop, boxed_handler, token, _dl| {
- let handler = boxed_handler.as_mut();
- match wsr.result() {
- MojoResult::Okay => handler.on_ready(runloop, token),
- MojoResult::Cancelled => {
- handler.on_error(runloop, token, WaitError::HandleClosed)
- }
- MojoResult::Busy => handler.on_error(runloop, token, WaitError::HandleBusy),
- MojoResult::FailedPrecondition => {
- handler.on_error(runloop, token, WaitError::Unsatisfiable)
- }
- other => panic!("Unexpected result received after waiting: {}", other),
- }
- });
- // In order to quit as soon as possible, we should check to quit after every
- // potential handler call, as any of them could have signaled to quit.
- if self.should_quit {
- break;
- }
- }
- }
-
- /// Since the deadline expired, we notify the relevant handle
- /// owners of the expiration by calling their given callbacks.
- ///
- /// We do NOT dequeue notified handles.
- fn notify_of_expired(&mut self, expired_deadline: system::MojoTimeTicks) {
- debug_assert!(!self.handlers.is_empty());
- let mut top = match self.deadlines.peek() {
- Some(info) => info.clone(),
- None => panic!("Should not be in notify_of_expired without at least one deadline!"),
- };
- while expired_deadline >= top.deadline() {
- let next_deadline = top.deadline();
- self.get_handler_with(top.token(),
- move |runloop, boxed_handler, token, expected_dl| {
- let handler = boxed_handler.as_mut();
- if next_deadline == expected_dl {
- handler.on_timeout(runloop, token);
- }
- });
- // In order to quit as soon as possible, we should check to quit after every
- // potential handler call, as any of them could have signaled to quit.
- if self.should_quit {
- break;
- }
- // Remove the deadline
- self.deadlines.pop();
- // Break if the next deadline has not yet expired.
- top = match self.deadlines.peek() {
- Some(info) => info.clone(),
- None => break,
- };
- }
- }
-
- /// Iterates through all tasks whose deadline has passed and executes
- /// them, consuming their information object.
- ///
- /// These tasks all have access to the RunLoop so that they may reschedule
- /// themselves or manipulate the RunLoop in some other way.
- fn execute_ready_tasks(&mut self) {
- let now = core::get_time_ticks_now();
- let mut deadline = match self.tasks.peek() {
- Some(info) => info.deadline(),
- None => return,
- };
- while deadline < now {
- let top = self.tasks.pop().expect("Sudden change to heap?");
- top.execute_task(self);
- if self.should_quit {
- return;
- }
- deadline = match self.tasks.peek() {
- Some(info) => info.deadline(),
- None => return,
- };
- }
- }
-
- /// Blocks on handle_set.wait_on_set using the information contained
- /// within itself.
- ///
- /// This method blocks for only as long as the shortest deadline among all
- /// handles this thread has registered. This method returns immediately as
- /// soon as any one handle has its signals satisfied, fails to ever have its
- /// signals satisfied, or reaches its deadline.
- fn wait(&mut self, results_buffer: &mut Vec<system::WaitSetResult>) {
- debug_assert!(!self.handlers.is_empty());
- self.execute_ready_tasks();
- // If after executing a task we quit or there are no handles,
- // we have no reason to continue.
- if self.handlers.is_empty() || self.should_quit {
- return;
- }
- let deadline = self.get_next_deadline();
- let until_deadline = relative_deadline(deadline, core::get_time_ticks_now());
- // Perform the wait
- match self.handle_set.wait_on_set(until_deadline, results_buffer) {
- Ok(max_results) => {
- self.notify_of_results(results_buffer);
- // Clear the buffer since we don't need the results anymore.
- // Helps prevent a copy if we resize the buffer.
- results_buffer.clear();
- // Increase the size of the buffer if there are more results
- // we could be holding.
- let capacity = results_buffer.capacity();
- if capacity < MAXIMUM_WAIT_SET_NUM_RESULTS && capacity < (max_results) as usize {
- results_buffer.reserve(capacity);
- }
- }
- Err(result) => {
- assert_eq!(result, MojoResult::DeadlineExceeded);
- self.notify_of_expired(deadline);
- }
- }
- }
-
- /// Loop forever until a callback tells us to quit.
- pub fn run(&mut self) {
- // It's an error it already be running...
- if self.running {
- panic!("RunLoop is already running!");
- }
- self.running = true;
- self.should_quit = false;
- let mut buffer: Vec<system::WaitSetResult> =
- Vec::with_capacity(INITIAL_WAIT_SET_NUM_RESULTS);
- // Loop while we haven't been signaled to quit, and there's something to wait on.
- while !self.should_quit && !self.handlers.is_empty() {
- self.wait(&mut buffer)
- }
- self.running = false;
- }
-
- /// Set a flag to quit at the next available moment.
- pub fn quit(&mut self) {
- self.should_quit = true;
- }
-}
-
-/// Provides a scope to modify the current thread's runloop.
-pub fn with_current<F>(modifier: F)
- where F: FnOnce(&mut RunLoop)
-{
- TL_RUN_LOOP.with(|ref_runloop| {
- let mut runloop = ref_runloop.borrow_mut();
- modifier(&mut *runloop);
- });
-}
« no previous file with comments | « mojo/public/rust/src/bindings/mojom.rs ('k') | mojo/public/rust/src/bindings/util.rs » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698