Skip to content

Commit

Permalink
Docs update
Browse files Browse the repository at this point in the history
  • Loading branch information
zetanumbers committed Dec 29, 2020
1 parent b5faa1f commit 4dfb658
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 41 deletions.
4 changes: 2 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down
57 changes: 32 additions & 25 deletions src/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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<Out>(&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<Out>(&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<Out>(&mut self, op: impl FnOnce() -> Out) -> Poll<Out> {
// Avoid write lock
let rcs_read = self.rcs.upgradable_read();
Expand All @@ -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<Out>(&mut self, op: impl FnOnce() -> Out, waker: Waker) -> Poll<Out> {
let mut rcs_write = self.rcs.write();
rcs_write.waker = waker;
Expand All @@ -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) {
Expand Down
36 changes: 22 additions & 14 deletions src/runtime/runloop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Root> {
inner: Runtime,
root: Root,
Expand All @@ -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 }
}
Expand All @@ -37,6 +38,7 @@ where
{
/// Creates a new `Runtime` attached to the provided root function.
pub fn new(root: Root) -> RunLoop<Root> {
// maybe only there force first revision
Runtime::new().looped(root)
}

Expand All @@ -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);
}
Expand All @@ -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<Out> {
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<Out> {
self.inner.poll_once_with(&mut self.root, waker)
}
Expand All @@ -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<Option<Self::Item>> {
let this = self.get_mut();
this.poll_once_with(cx.waker().clone()).map(Some)
Expand Down

0 comments on commit 4dfb658

Please sign in to comment.