lightproc/
lightproc.rs

1//!
2//! Lightweight process implementation which enables users
3//! to create either panic recoverable process or
4//! ordinary process.
5//!
6//! Lightweight processes needs a stack to use their lifecycle
7//! operations like `before_start`, `after_complete` and more...
8//!
9//! # Example Usage
10//!
11//! ```rust
12//! use lightproc::prelude::*;
13//!
14//! // ... future that does work
15//! let future = async {
16//!     println!("Doing some work");
17//! };
18//!
19//! // ... basic schedule function with no waker logic
20//! fn schedule_function(proc: LightProc) {;}
21//!
22//! // ... process stack with a lifecycle callback
23//! let proc_stack =
24//!     ProcStack::default()
25//!         .with_after_panic(|s: &mut EmptyProcState| {
26//!             println!("After panic started!");
27//!         });
28//!
29//! // ... creating a recoverable process
30//! let panic_recoverable = LightProc::recoverable(
31//!     future,
32//!     schedule_function,
33//!     proc_stack
34//! );
35//! ```
36
37use crate::proc_data::ProcData;
38use crate::proc_ext::ProcFutureExt;
39use crate::proc_handle::ProcHandle;
40use crate::proc_stack::*;
41use crate::raw_proc::RawProc;
42use crate::recoverable_handle::RecoverableHandle;
43use std::fmt::{self, Debug, Formatter};
44use std::future::Future;
45use std::marker::PhantomData;
46use std::mem;
47use std::panic::AssertUnwindSafe;
48use std::ptr::NonNull;
49
50/// Struct to create and operate lightweight processes
51pub struct LightProc {
52    /// A pointer to the heap-allocated proc.
53    pub(crate) raw_proc: NonNull<()>,
54}
55
56unsafe impl Send for LightProc {}
57unsafe impl Sync for LightProc {}
58
59impl LightProc {
60    ///
61    /// Creates a recoverable process which will signal occurred
62    /// panic back to the poller.
63    ///
64    /// # Example
65    /// ```rust
66    /// # use lightproc::prelude::*;
67    /// #
68    /// # // ... future that does work
69    /// # let future = async {
70    /// #     println!("Doing some work");
71    /// # };
72    /// #
73    /// # // ... basic schedule function with no waker logic
74    /// # fn schedule_function(proc: LightProc) {;}
75    /// #
76    /// # // ... process stack with a lifecycle callback
77    /// # let proc_stack =
78    /// #     ProcStack::default()
79    /// #         .with_after_panic(|s: &mut EmptyProcState| {
80    /// #             println!("After panic started!");
81    /// #         });
82    /// #
83    /// // ... creating a recoverable process
84    /// let panic_recoverable = LightProc::recoverable(
85    ///     future,
86    ///     schedule_function,
87    ///     proc_stack
88    /// );
89    /// ```
90    pub fn recoverable<F, R, S>(
91        future: F,
92        schedule: S,
93        stack: ProcStack,
94    ) -> (LightProc, RecoverableHandle<R>)
95    where
96        F: Future<Output = R> + Send + 'static,
97        R: Send + 'static,
98        S: Fn(LightProc) + Send + Sync + 'static,
99    {
100        let recovery_future = AssertUnwindSafe(future).catch_unwind();
101        let (proc, handle) = Self::build(recovery_future, schedule, stack);
102        (proc, RecoverableHandle(handle))
103    }
104
105    ///
106    /// Creates a standard process which will stop it's execution on occurrence of panic.
107    ///
108    /// # Example
109    /// ```rust
110    /// # use lightproc::prelude::*;
111    /// #
112    /// # // ... future that does work
113    /// # let future = async {
114    /// #     println!("Doing some work");
115    /// # };
116    /// #
117    /// # // ... basic schedule function with no waker logic
118    /// # fn schedule_function(proc: LightProc) {;}
119    /// #
120    /// # // ... process stack with a lifecycle callback
121    /// # let proc_stack =
122    /// #     ProcStack::default()
123    /// #         .with_after_panic(|s: &mut EmptyProcState| {
124    /// #             println!("After panic started!");
125    /// #         });
126    /// #
127    /// // ... creating a standard process
128    /// let standard = LightProc::build(
129    ///     future,
130    ///     schedule_function,
131    ///     proc_stack
132    /// );
133    /// ```
134    pub fn build<F, R, S>(future: F, schedule: S, stack: ProcStack) -> (LightProc, ProcHandle<R>)
135    where
136        F: Future<Output = R> + Send + 'static,
137        R: Send + 'static,
138        S: Fn(LightProc) + Send + Sync + 'static,
139    {
140        let raw_proc = RawProc::allocate(stack, future, schedule);
141        let proc = LightProc { raw_proc };
142        let handle = ProcHandle {
143            raw_proc,
144            _marker: PhantomData,
145        };
146        (proc, handle)
147    }
148
149    ///
150    /// Schedule the lightweight process with passed `schedule` function at the build time.
151    pub fn schedule(self) {
152        let ptr = self.raw_proc.as_ptr();
153        let pdata = ptr as *const ProcData;
154        mem::forget(self);
155
156        unsafe {
157            ((*pdata).vtable.schedule)(ptr);
158        }
159    }
160
161    ///
162    /// Schedule the lightproc for runnning on the thread.
163    pub fn run(self) {
164        let ptr = self.raw_proc.as_ptr();
165        let pdata = ptr as *const ProcData;
166        mem::forget(self);
167
168        unsafe {
169            ((*pdata).vtable.run)(ptr);
170        }
171    }
172
173    ///
174    /// Cancel polling the lightproc's inner future, thus cancelling th proc itself.
175    pub fn cancel(&self) {
176        let ptr = self.raw_proc.as_ptr();
177        let pdata = ptr as *const ProcData;
178
179        unsafe {
180            (*pdata).cancel();
181        }
182    }
183
184    ///
185    /// Gives a reference to given [ProcStack] when building the light proc.
186    pub fn stack(&self) -> &ProcStack {
187        let offset = ProcData::offset_stack();
188        let ptr = self.raw_proc.as_ptr();
189
190        unsafe {
191            let raw = (ptr as *mut u8).add(offset) as *const ProcStack;
192            &*raw
193        }
194    }
195}
196
197impl Debug for LightProc {
198    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
199        let ptr = self.raw_proc.as_ptr();
200        let pdata = ptr as *const ProcData;
201
202        fmt.debug_struct("LightProc")
203            .field("pdata", unsafe { &(*pdata) })
204            .field("stack", self.stack())
205            .finish()
206    }
207}
208
209impl Drop for LightProc {
210    fn drop(&mut self) {
211        let ptr = self.raw_proc.as_ptr();
212        let pdata = ptr as *const ProcData;
213
214        unsafe {
215            // Cancel the proc.
216            (*pdata).cancel();
217
218            // Drop the future.
219            ((*pdata).vtable.drop_future)(ptr);
220
221            // Drop the proc reference.
222            ((*pdata).vtable.decrement)(ptr);
223        }
224    }
225}