From 4dfb65845b7884e8e7e78a29a1020267d44faf05 Mon Sep 17 00:00:00 2001 From: zetanumbers Date: Fri, 25 Dec 2020 20:21:08 +0300 Subject: [PATCH] Docs update --- src/lib.rs | 4 +-- src/runtime.rs | 57 ++++++++++++++++++++++++------------------ src/runtime/runloop.rs | 36 +++++++++++++++----------- 3 files changed, 56 insertions(+), 41 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index c6fd702ae..270667912 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -73,7 +73,7 @@ use std::{ use topo::CallId; /// Applied to impl blocks, this macro defines a new "updater" wrapper type that -/// holds a [`crate::Key`] and forwards all receiver-mutating methods. Useful +/// holds a [`Key`] and forwards all receiver-mutating methods. Useful /// for defining interactions for a stateful component with less boilerplate. /// /// Requires the name of the updater struct to generate in the arguments to the @@ -628,7 +628,7 @@ where /// /// Reads through a commit are not guaranteed to be the latest value visible to /// the runtime. Commits should be shared and used within the context of a -/// single [`crate::runtime::Revision`], being re-loaded from the state variable +/// single [`runtime::Revision`], being re-loaded from the state variable /// each time. /// /// See [`state`] and [`cache_state`] for examples. diff --git a/src/runtime.rs b/src/runtime.rs index 085a5d1de..639f58cca 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -43,9 +43,8 @@ impl std::fmt::Debug for Revision { } } -/// TODO description #[derive(Debug)] -pub struct RevisionControlSystem { +pub(crate) struct RevisionControlSystem { revision: Revision, waker: Waker, pending_changes: AtomicBool, @@ -75,16 +74,18 @@ pub struct RevisionControlSystem { /// /// ## Change notifications /// -/// Each runtime should be provided with a [`std::task::Waker`] that will notify +/// Each runtime should be provided with a [`Waker`] that will notify /// the embedding environment to run the loop again. This is done by calling -/// [`Runtime::set_state_change_waker`]. +/// [`Runtime::set_state_change_waker`] or +/// [`Runtime::poll_once`][Runtime::poll_once] +/// [`(_with)`][Runtime::poll_once_with]. /// /// For scenarios without an obvious "main thread" this can be done for you by -/// binding a root function to a [`RunLoop`] which implements -/// [`std::future::Future`] and can be spawned as a task onto an executor. For -/// more nuanced scenarios it can be necessary to write your own waker to ensure -/// scheduling compatible with the embedding environment. By default a no-op -/// waker is provided. +/// binding a root function to a [`RunLoop`] which implements +/// [`futures::Stream`] and can be spawned as a task onto an executor. +/// For more nuanced scenarios it can be necessary to write your own waker to +/// ensure scheduling compatible with the embedding environment. By default a +/// no-op waker is provided. /// /// The most common way of notifying a runtime of a change is to update a /// state variable. @@ -150,38 +151,41 @@ impl Runtime { rcs: Arc::new(RwLock::new(RevisionControlSystem { revision: Revision(0), waker: noop_waker(), - // require the first revision to be forced + // require the first revision to be forced? pending_changes: AtomicBool::new(false), })), } } - /// The current revision of the runtime, or how many times `run_once` has - /// been invoked. + /// The current revision of the runtime, or how many runs occurred. pub fn revision(&self) -> Revision { self.rcs.read().revision } - /// TODO description - pub fn force(&self) { - self.rcs.read().pending_changes.store(true, std::sync::atomic::Ordering::Relaxed); - } - - /// Runs the root closure once with access to the runtime context, - /// increments the runtime's `Revision`, and drops any cached values - /// which were not marked alive. + /// Runs the root closure once with access to the runtime context and drops + /// any cached values which were not marked alive. `Revision` is + /// incremented at the start of a run. pub fn run_once(&mut self, op: impl FnOnce() -> Out) -> Out { self.execute(op, self.rcs.write()) } - /// TODO description + /// Runs the root closure once with access to the runtime context and drops + /// any cached values which were not marked alive. `Waker` is set for the + /// next `Revision`, which starts after the start of the run. pub fn run_once_with(&mut self, op: impl FnOnce() -> Out, waker: Waker) -> Out { let mut rcs_write = self.rcs.write(); rcs_write.waker = waker; self.execute(op, rcs_write) } - /// TODO description + /// Forces the next `Revision` without any changes. + pub fn force(&self) { + self.rcs.read().pending_changes.store(true, std::sync::atomic::Ordering::Relaxed); + } + + /// If change occured durig the last `Revision` then calls `run_once` + /// else returns `Poll::Pending`. It is required to force your + /// first revision to register your state variables! pub fn poll_once(&mut self, op: impl FnOnce() -> Out) -> Poll { // Avoid write lock let rcs_read = self.rcs.upgradable_read(); @@ -193,7 +197,9 @@ impl Runtime { } } - /// TODO description + /// If change occured durig the last `Revision` then calls `run_once_with` + /// else returns [`Poll::Pending`]. It is required to force your + /// first revision to register your state variables! pub fn poll_once_with(&mut self, op: impl FnOnce() -> Out, waker: Waker) -> Poll { let mut rcs_write = self.rcs.write(); rcs_write.waker = waker; @@ -219,8 +225,9 @@ impl Runtime { ret } - /// Sets the [`std::task::Waker`] which will be called when state variables - /// receive commits. By default the runtime no-ops on a state change, + /// Sets the [`Waker`] which will be called when state variables + /// receive commits or if current `Revision` already has any received + /// commits. By default the runtime no-ops on a state change, /// which is probably the desired behavior if the embedding system will /// call `Runtime::run_once` on a regular interval regardless. pub fn set_state_change_waker(&mut self, waker: Waker) { diff --git a/src/runtime/runloop.rs b/src/runtime/runloop.rs index 24b91d6b0..d4a24fa16 100644 --- a/src/runtime/runloop.rs +++ b/src/runtime/runloop.rs @@ -11,8 +11,8 @@ use std::{ /// A [`Runtime`] that is bound with a particular root function. /// /// If running in a context with an async executor, can be consumed as a -/// [`futures::Stream`] of [`crate::runtime::Revision`]s in order to provide -/// the [`super::Runtime`] with a [`std::task::Waker`]. +/// [`futures::Stream`] in order to provide +/// the [`Runtime`] with a [`Waker`]. pub struct RunLoop { inner: Runtime, root: Root, @@ -25,7 +25,8 @@ impl super::Runtime { where Root: FnMut() -> Out, { - // RunLoop always forces first revision + // RunLoop always forces it's first revision? + // or maybe just check if current revision is 0 self.force(); RunLoop { inner: self, root } } @@ -37,6 +38,7 @@ where { /// Creates a new `Runtime` attached to the provided root function. pub fn new(root: Root) -> RunLoop { + // maybe only there force first revision Runtime::new().looped(root) } @@ -45,8 +47,9 @@ where self.inner.revision() } - /// Sets the [`std::task::Waker`] which will be called when state variables - /// change. + /// Sets the [`Waker`] which will be called when state variables + /// changes or if current `Revision` already has any state variables + /// changed. pub fn set_state_change_waker(&mut self, wk: Waker) { self.inner.set_state_change_waker(wk); } @@ -56,29 +59,34 @@ where self.inner.set_task_executor(sp); } - /// Run the root function once within this runtime's context, returning the - /// result. + /// Runs the root closure once with access to the runtime context, returning + /// the result. `Revision` is incremented at the start of a run. pub fn run_once(&mut self) -> Out { self.inner.run_once(&mut self.root) } - /// Run the root function once within this runtime's context, returning the - /// result. + /// Runs the root closure once with access to the runtime context, returning + /// the result. `Waker` is set for the next `Revision`, which starts after + /// the start of the run. pub fn run_once_with(&mut self, waker: Waker) -> Out { self.inner.run_once_with(&mut self.root, waker) } - /// TODO description + /// Forces the next `Revision` without any changes. pub fn force(&self) { self.inner.force() } - /// TODO description + /// If change occured durig the last `Revision` then calls `run_once` + /// else returns `Poll::Pending`. Note that RunLoop always forces it's first + /// run (for now?) pub fn poll_once(&mut self) -> Poll { self.inner.poll_once(&mut self.root) } - /// TODO description + /// If change occured durig the last `Revision` then calls `run_once_with` + /// else returns [`Poll::Pending`]. Note that RunLoop always forces it's + /// first run (for now?) pub fn poll_once_with(&mut self, waker: Waker) -> Poll { self.inner.poll_once_with(&mut self.root, waker) } @@ -104,8 +112,8 @@ where { type Item = Out; - /// This `Stream` implementation runs a single revision for each call to - /// `poll_next`, always returning `Poll::Ready(Some(...))`. + /// This `Stream` implementation yields until state change occurred or + /// future fully [loads][crate::load]. fn poll_next(self: Pin<&mut Self>, cx: &mut FutContext<'_>) -> Poll> { let this = self.get_mut(); this.poll_once_with(cx.waker().clone()).map(Some)