Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lightproc Documentation #81

Merged
merged 3 commits into from
Nov 10, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion lightproc/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# LightProc

Lightweight process abstraction for Rust
LightProc is Lightweight Process abstraction for Rust.

Beneath the implementation:
* It uses futures with lifecycle callbacks to implement Erlang like processes.
* Contains basic pid(process id) to identify processes.
* All panics inside futures are propagated to upper layers.
20 changes: 20 additions & 0 deletions lightproc/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
//!
//!
//! LightProc is Lightweight Process abstraction for Rust.
//!
//! Beneath the implementation:
//! * It uses futures with lifecycle callbacks to implement Erlang like processes.
//! * Contains basic pid(process id) to identify processes.
//! * All panics inside futures are propagated to upper layers.
//!
//! The naming convention of this crate comes from [Erlang's Lightweight Processes].
//!
//! [Erlang's Lightweight Processes]: https://en.wikipedia.org/wiki/Light-weight_process
//!

// Force missing implementations
#![warn(missing_docs)]
#![warn(missing_debug_implementations)]
// Discarded lints
#![allow(clippy::cast_ptr_alignment)]

Expand All @@ -15,6 +32,9 @@ pub mod proc_handle;
pub mod proc_stack;
pub mod recoverable_handle;

/// The lightproc prelude.
///
/// The prelude re-exports lightproc structs and handles from this crate.
pub mod prelude {
pub use crate::lightproc::*;
pub use crate::proc_handle::*;
Expand Down
104 changes: 104 additions & 0 deletions lightproc/src/lightproc.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,39 @@
//!
//! Lightweight process implementation which enables users
//! to create either panic recoverable process or
//! ordinary process.
//!
//! Lightweight processes needs a stack to use their lifecycle
//! operations like `before_start`, `after_complete` and more...
//!
//! # Example Usage
//!
//! ```rust
//! use lightproc::prelude::*;
//!
//! // ... future that does work
//! let future = async {
//! println!("Doing some work");
//! };
//!
//! // ... basic schedule function with no waker logic
//! fn schedule_function(proc: LightProc) {;}
//!
//! // ... process stack with a lifecycle callback
//! let proc_stack =
//! ProcStack::default()
//! .with_after_panic(|| {
//! println!("After panic started!");
//! });
//!
//! // ... creating a recoverable process
//! let panic_recoverable = LightProc::recoverable(
//! future,
//! schedule_function,
//! proc_stack
//! );
//! ```

use crate::proc_data::ProcData;
use crate::proc_ext::ProcFutureExt;
use crate::proc_handle::ProcHandle;
Expand All @@ -11,6 +47,7 @@ use std::mem;
use std::panic::AssertUnwindSafe;
use std::ptr::NonNull;

/// Struct to create and operate lightweight processes
pub struct LightProc {
/// A pointer to the heap-allocated proc.
pub(crate) raw_proc: NonNull<()>,
Expand All @@ -20,6 +57,36 @@ unsafe impl Send for LightProc {}
unsafe impl Sync for LightProc {}

impl LightProc {
///
/// Creates a recoverable process which will signal occurred
/// panic back to the poller.
///
/// # Example
/// ```rust
/// # use lightproc::prelude::*;
/// #
/// # // ... future that does work
/// # let future = async {
/// # println!("Doing some work");
/// # };
/// #
/// # // ... basic schedule function with no waker logic
/// # fn schedule_function(proc: LightProc) {;}
/// #
/// # // ... process stack with a lifecycle callback
/// # let proc_stack =
/// # ProcStack::default()
/// # .with_after_panic(|| {
/// # println!("After panic started!");
/// # });
/// #
/// // ... creating a recoverable process
/// let panic_recoverable = LightProc::recoverable(
/// future,
/// schedule_function,
/// proc_stack
/// );
/// ```
pub fn recoverable<F, R, S>(
future: F,
schedule: S,
Expand All @@ -35,6 +102,35 @@ impl LightProc {
(proc, RecoverableHandle(handle))
}

///
/// Creates a standard process which will stop it's execution on occurrence of panic.
///
/// # Example
/// ```rust
/// # use lightproc::prelude::*;
/// #
/// # // ... future that does work
/// # let future = async {
/// # println!("Doing some work");
/// # };
/// #
/// # // ... basic schedule function with no waker logic
/// # fn schedule_function(proc: LightProc) {;}
/// #
/// # // ... process stack with a lifecycle callback
/// # let proc_stack =
/// # ProcStack::default()
/// # .with_after_panic(|| {
/// # println!("After panic started!");
/// # });
/// #
/// // ... creating a standard process
/// let standard = LightProc::build(
/// future,
/// schedule_function,
/// proc_stack
/// );
/// ```
pub fn build<F, R, S>(future: F, schedule: S, stack: ProcStack) -> (LightProc, ProcHandle<R>)
where
F: Future<Output = R> + Send + 'static,
Expand All @@ -50,6 +146,8 @@ impl LightProc {
(proc, handle)
}

///
/// Schedule the lightweight process with passed `schedule` function at the build time.
pub fn schedule(self) {
let ptr = self.raw_proc.as_ptr();
let pdata = ptr as *const ProcData;
Expand All @@ -60,6 +158,8 @@ impl LightProc {
}
}

///
/// Schedule the lightproc for runnning on the thread.
pub fn run(self) {
let ptr = self.raw_proc.as_ptr();
let pdata = ptr as *const ProcData;
Expand All @@ -70,6 +170,8 @@ impl LightProc {
}
}

///
/// Cancel polling the lightproc's inner future, thus cancelling th proc itself.
pub fn cancel(&self) {
let ptr = self.raw_proc.as_ptr();
let pdata = ptr as *const ProcData;
Expand All @@ -79,6 +181,8 @@ impl LightProc {
}
}

///
/// Gives a reference to given [ProcStack] when building the light proc.
pub fn stack(&self) -> &ProcStack {
let offset = ProcData::offset_stack();
let ptr = self.raw_proc.as_ptr();
Expand Down
3 changes: 3 additions & 0 deletions lightproc/src/proc_handle.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
//!
//! Handle for tasks which don't need to unwind panics inside
//! the given futures.
use crate::proc_data::ProcData;
use crate::proc_stack::ProcStack;
use crate::state::*;
Expand Down
80 changes: 77 additions & 3 deletions lightproc/src/proc_stack.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,76 @@
//! Stack abstraction for lightweight processes
//!
//! This abstraction allows us to execute lifecycle callbacks when
//! a process transites from one state to another.
//!
//! If we want to make an analogy, stack abstraction is similar to actor lifecycle abstractions
//! in frameworks like Akka, but tailored version for Rust environment.

use std::fmt::{self, Debug, Formatter};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;

/// Stack abstraction for lightweight processes
///
/// # Example
///
/// ```rust
/// use lightproc::proc_stack::ProcStack;
///
/// ProcStack::default()
/// .with_before_start(|| { println!("Before start"); })
/// .with_after_complete(|| { println!("After complete"); })
/// .with_after_panic(|| { println!("After panic"); });
/// ```
#[derive(Default)]
pub struct ProcStack {
/// Process ID for the Lightweight Process
///
/// Can be used to identify specific processes during any executor, reactor implementations.
pub pid: AtomicUsize,

// Before action callback
/// Before start callback
///
/// This callback is called before we start to inner future of the process
pub(crate) before_start: Option<Arc<dyn Fn() + Send + Sync>>,

// After action callback
/// After complete callback
///
/// This callback is called after future resolved to it's output.
/// Mind that, even panic occurs this callback will get executed.
///
/// Eventually all panics are coming from an Error output.
pub(crate) after_complete: Option<Arc<dyn Fn() + Send + Sync>>,

// After panic callback
/// After panic callback
///
/// This callback is only called when a panic has been occurred.
/// Mind that [ProcHandle](proc_handle/struct.ProcHandle.html) is not using this
pub(crate) after_panic: Option<Arc<dyn Fn() + Send + Sync>>,
}

impl ProcStack {
/// Adds pid for the process which is going to take this stack
///
/// ```rust
/// use lightproc::proc_stack::ProcStack;
///
/// ProcStack::default()
/// .with_pid(1);
/// ```
pub fn with_pid(mut self, pid: usize) -> Self {
self.pid = AtomicUsize::new(pid);
self
}

/// Adds a callback that will be executed before polling inner future to the stack
///
/// ```rust
/// use lightproc::proc_stack::ProcStack;
///
/// ProcStack::default()
/// .with_before_start(|| { println!("Before start") });
/// ```
pub fn with_before_start<T>(mut self, callback: T) -> Self
where
T: Fn() + Send + Sync + 'static,
Expand All @@ -30,6 +79,14 @@ impl ProcStack {
self
}

/// Adds a callback that will be executed after inner future resolves to an output to the stack
///
/// ```rust
/// use lightproc::proc_stack::ProcStack;
///
/// ProcStack::default()
/// .with_after_complete(|| { println!("After complete") });
/// ```
pub fn with_after_complete<T>(mut self, callback: T) -> Self
where
T: Fn() + Send + Sync + 'static,
Expand All @@ -38,6 +95,14 @@ impl ProcStack {
self
}

/// Adds a callback that will be executed after inner future panics to the stack
///
/// ```rust
/// use lightproc::proc_stack::ProcStack;
///
/// ProcStack::default()
/// .with_after_panic(|| { println!("After panic") });
/// ```
pub fn with_after_panic<T>(mut self, callback: T) -> Self
where
T: Fn() + Send + Sync + 'static,
Expand All @@ -46,6 +111,15 @@ impl ProcStack {
self
}

/// Utility function to get_pid for the implementation of executors.
///
/// ```rust
/// use lightproc::proc_stack::ProcStack;
///
/// let proc = ProcStack::default().with_pid(123);
///
/// assert_eq!(proc.get_pid(), 123);
/// ```
pub fn get_pid(&self) -> usize {
self.pid.load(Ordering::Acquire)
}
Expand Down
5 changes: 5 additions & 0 deletions lightproc/src/recoverable_handle.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//!
//! Handle for recoverable process
use crate::proc_data::ProcData;
use crate::proc_handle::ProcHandle;
use crate::proc_stack::ProcStack;
Expand All @@ -7,6 +9,9 @@ use std::pin::Pin;
use std::task::{Context, Poll};
use std::thread;

/// Recoverable handle which encapsulates a standard Proc Handle and contain all panics inside.
///
/// Execution of `after_panic` will be immediate on polling the [RecoverableHandle]'s future.
pub struct RecoverableHandle<R>(pub(crate) ProcHandle<thread::Result<R>>);

impl<R> RecoverableHandle<R> {
Expand Down