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}