From a48bec700add1c50997bd1f1fbdc3823212f0a31 Mon Sep 17 00:00:00 2001 From: Mahmut Bulut Date: Sat, 12 Oct 2019 19:50:11 +0200 Subject: [PATCH 01/38] Stub --- lightproc/src/lib.rs | 5 +++++ lightproc/src/lightproc.rs | 4 ++++ lightproc/src/proc_data.rs | 12 ++++++++++++ lightproc/src/proc_handle.rs | 24 ++++++++++++++++++++++++ lightproc/src/proc_vtable.rs | 8 ++++++++ lightproc/src/state.rs | 0 6 files changed, 53 insertions(+) create mode 100644 lightproc/src/proc_data.rs create mode 100644 lightproc/src/proc_handle.rs create mode 100644 lightproc/src/proc_vtable.rs create mode 100644 lightproc/src/state.rs diff --git a/lightproc/src/lib.rs b/lightproc/src/lib.rs index e69de29b..799e5a0f 100644 --- a/lightproc/src/lib.rs +++ b/lightproc/src/lib.rs @@ -0,0 +1,5 @@ +mod lightproc; +mod proc_handle; +mod proc_data; +mod proc_vtable; +mod state; diff --git a/lightproc/src/lightproc.rs b/lightproc/src/lightproc.rs index e69de29b..7f586cd9 100644 --- a/lightproc/src/lightproc.rs +++ b/lightproc/src/lightproc.rs @@ -0,0 +1,4 @@ + +pub struct LightProc { + +} diff --git a/lightproc/src/proc_data.rs b/lightproc/src/proc_data.rs new file mode 100644 index 00000000..e7568e79 --- /dev/null +++ b/lightproc/src/proc_data.rs @@ -0,0 +1,12 @@ +use std::task::Waker; +use std::cell::UnsafeCell; +use proc_vtable::ProcVTable; +use std::sync::atomic::{AtomicUsize, Ordering}; + +pub(crate) struct ProcData { + pub(crate) state: AtomicUsize, + + pub(crate) awaiter: UnsafeCell>, + + pub(crate) vtable: &'static ProcVTable, +} diff --git a/lightproc/src/proc_handle.rs b/lightproc/src/proc_handle.rs new file mode 100644 index 00000000..41c9b42d --- /dev/null +++ b/lightproc/src/proc_handle.rs @@ -0,0 +1,24 @@ +use std::ptr::NonNull; +use std::marker::PhantomData as marker; + +pub struct ProcHandle { + raw_proc: NonNull<()>, + _private: marker<(R, T)> +} + +unsafe impl Send for ProcHandle {} +unsafe impl Sync for ProcHandle {} + +impl Unpin for ProcHandle {} + +impl ProcHandle { + /// Cancels the task. + /// + /// If the task has already completed, calling this method will have no effect. + /// + /// When a task is cancelled, its future cannot be polled again and will be dropped instead. + pub fn cancel(&self) { + let ptr = self.raw_proc.as_ptr(); + unimplemented!() + } +} diff --git a/lightproc/src/proc_vtable.rs b/lightproc/src/proc_vtable.rs new file mode 100644 index 00000000..942d1db7 --- /dev/null +++ b/lightproc/src/proc_vtable.rs @@ -0,0 +1,8 @@ +use std::task::RawWakerVTable; + +pub(crate) struct ProcVTable { + /// The raw waker vtable. + pub(crate) raw_waker: RawWakerVTable, + + pub(crate) schedule: unsafe fn(*const ()), +} diff --git a/lightproc/src/state.rs b/lightproc/src/state.rs new file mode 100644 index 00000000..e69de29b From 2ff76041abfd640d9e17784f729500705b11a5cf Mon Sep 17 00:00:00 2001 From: Mahmut Bulut Date: Tue, 15 Oct 2019 23:13:16 +0200 Subject: [PATCH 02/38] Initial lightproc development --- lightproc/Cargo.toml | 1 + lightproc/examples/task_build.rs | 6 +++++ lightproc/src/layout_helpers.rs | 29 ++++++++++++++++++++ lightproc/src/lib.rs | 19 +++++++++---- lightproc/src/lightproc.rs | 45 +++++++++++++++++++++++++++++-- lightproc/src/proc_data.rs | 2 +- lightproc/src/proc_handle.rs | 5 ---- lightproc/src/proc_layout.rs | 15 +++++++++++ lightproc/src/proc_vtable.rs | 3 +++ lightproc/src/raw_proc.rs | 46 ++++++++++++++++++++++++++++++++ lightproc/src/stack.rs | 9 +++++++ 11 files changed, 167 insertions(+), 13 deletions(-) create mode 100644 lightproc/examples/task_build.rs create mode 100644 lightproc/src/layout_helpers.rs create mode 100644 lightproc/src/proc_layout.rs create mode 100644 lightproc/src/raw_proc.rs create mode 100644 lightproc/src/stack.rs diff --git a/lightproc/Cargo.toml b/lightproc/Cargo.toml index 7be0cc3a..28c989a4 100644 --- a/lightproc/Cargo.toml +++ b/lightproc/Cargo.toml @@ -5,3 +5,4 @@ description = "Lightweight process abstraction for Rust" authors = ["Mahmut Bulut "] keywords = ["fault-tolerant", "runtime", "actor", "system"] categories = [] +edition = "2018" diff --git a/lightproc/examples/task_build.rs b/lightproc/examples/task_build.rs new file mode 100644 index 00000000..19ef5294 --- /dev/null +++ b/lightproc/examples/task_build.rs @@ -0,0 +1,6 @@ +use lightproc::prelude::*; + +fn main() { + LightProc::<()>::new() + .with_future(async move { println!("test"); }); +} diff --git a/lightproc/src/layout_helpers.rs b/lightproc/src/layout_helpers.rs new file mode 100644 index 00000000..c8b839b9 --- /dev/null +++ b/lightproc/src/layout_helpers.rs @@ -0,0 +1,29 @@ +use std::alloc::{Layout, LayoutErr}; +use std::io::{Error, ErrorKind}; + +#[inline] +pub fn extend(layout: Layout, next: Layout) -> (Layout, usize) { + let new_align = std::cmp::max(layout.align(), next.align()); + let pad = padding_needed_for(layout, next.align()); + + let offset = layout.size().checked_add(pad) + .ok_or( + Error::new(ErrorKind::Other, "Padding overflow check failed") + ).unwrap(); + let new_size = offset.checked_add(next.size()) + .ok_or( + Error::new(ErrorKind::Other, "New size can't be computed") + ).unwrap(); + + let layout = + Layout::from_size_align(new_size, new_align).unwrap(); + (layout, offset) +} + +#[inline] +pub fn padding_needed_for(layout: Layout, align: usize) -> usize { + let len = layout.size(); + let len_rounded_up = len.wrapping_add(align).wrapping_sub(1) + & !align.wrapping_sub(1); + len_rounded_up.wrapping_sub(len) +} diff --git a/lightproc/src/lib.rs b/lightproc/src/lib.rs index 799e5a0f..bb49b74a 100644 --- a/lightproc/src/lib.rs +++ b/lightproc/src/lib.rs @@ -1,5 +1,14 @@ -mod lightproc; -mod proc_handle; -mod proc_data; -mod proc_vtable; -mod state; +pub mod lightproc; +pub mod proc_handle; +pub mod proc_data; +pub mod proc_vtable; +pub mod state; +pub mod raw_proc; +pub mod proc_layout; +pub mod stack; +pub mod layout_helpers; + +pub mod prelude { + pub use crate::lightproc::*; + pub use crate::proc_layout::*; +} diff --git a/lightproc/src/lightproc.rs b/lightproc/src/lightproc.rs index 7f586cd9..126f1d41 100644 --- a/lightproc/src/lightproc.rs +++ b/lightproc/src/lightproc.rs @@ -1,4 +1,45 @@ +use std::ptr::NonNull; +use std::marker::PhantomData as marker; +use std::future::Future; +use crate::proc_handle::ProcHandle; +use crate::raw_proc::RawProc; +use crate::stack::ProcStack; +use crate::proc_layout::ProcLayout; +use std::alloc; +use std::alloc::Layout; +use crate::layout_helpers::extend; -pub struct LightProc { - +pub struct LightProc { + pub(crate) raw_proc: NonNull<()>, + + pub(crate) proc_layout: ProcLayout, + pub(crate) _private: marker +} + +unsafe impl Send for LightProc {} +unsafe impl Sync for LightProc {} + +impl LightProc { + pub fn new() -> LightProc { + let proc_layout = ProcLayout::default(); + + unsafe { + LightProc { + raw_proc: NonNull::dangling(), + proc_layout, + _private: marker + } + } + } + + pub fn with_future(mut self, f: F) + where + F: Future + Send + 'static + { + let fut_mem = Layout::new::(); + let (new_layout, offset_t) = + extend(self.proc_layout.layout, fut_mem); + self.proc_layout.layout = new_layout; + + } } diff --git a/lightproc/src/proc_data.rs b/lightproc/src/proc_data.rs index e7568e79..477cd5b7 100644 --- a/lightproc/src/proc_data.rs +++ b/lightproc/src/proc_data.rs @@ -1,6 +1,6 @@ use std::task::Waker; use std::cell::UnsafeCell; -use proc_vtable::ProcVTable; +use crate::proc_vtable::ProcVTable; use std::sync::atomic::{AtomicUsize, Ordering}; pub(crate) struct ProcData { diff --git a/lightproc/src/proc_handle.rs b/lightproc/src/proc_handle.rs index 41c9b42d..ffcda982 100644 --- a/lightproc/src/proc_handle.rs +++ b/lightproc/src/proc_handle.rs @@ -12,11 +12,6 @@ unsafe impl Sync for ProcHandle {} impl Unpin for ProcHandle {} impl ProcHandle { - /// Cancels the task. - /// - /// If the task has already completed, calling this method will have no effect. - /// - /// When a task is cancelled, its future cannot be polled again and will be dropped instead. pub fn cancel(&self) { let ptr = self.raw_proc.as_ptr(); unimplemented!() diff --git a/lightproc/src/proc_layout.rs b/lightproc/src/proc_layout.rs new file mode 100644 index 00000000..a67d2bf0 --- /dev/null +++ b/lightproc/src/proc_layout.rs @@ -0,0 +1,15 @@ +use std::alloc::Layout; +use crate::stack::ProcStack; + +#[derive(Clone, Copy)] +pub struct ProcLayout { + pub layout: Layout, +} + +impl Default for ProcLayout { + fn default() -> Self { + ProcLayout { + layout: Layout::new::() + } + } +} diff --git a/lightproc/src/proc_vtable.rs b/lightproc/src/proc_vtable.rs index 942d1db7..f167d83a 100644 --- a/lightproc/src/proc_vtable.rs +++ b/lightproc/src/proc_vtable.rs @@ -5,4 +5,7 @@ pub(crate) struct ProcVTable { pub(crate) raw_waker: RawWakerVTable, pub(crate) schedule: unsafe fn(*const ()), + +// // Callbacks +// pub(crate) callbacks: ProcCallbacks } diff --git a/lightproc/src/raw_proc.rs b/lightproc/src/raw_proc.rs new file mode 100644 index 00000000..9e6bc8a0 --- /dev/null +++ b/lightproc/src/raw_proc.rs @@ -0,0 +1,46 @@ +use crate::proc_data::ProcData; +use std::future::Future; +use crate::lightproc::LightProc; +use std::ptr::NonNull; +use std::sync::atomic::AtomicUsize; +use std::cell::UnsafeCell; +use std::task::RawWakerVTable; +use std::alloc::{Layout, alloc}; +use crate::proc_layout::ProcLayout; + +/// Raw pointers to the fields of a task. +pub struct RawProc { + pub(crate) pdata: *const ProcData, + + pub(crate) schedule: *const S, + + pub(crate) stack: *mut T, + + pub(crate) future: *mut F, + + pub(crate) output: *mut R, +} + +impl Copy for RawProc {} + +impl Clone for RawProc { + fn clone(&self) -> Self { + Self { + pdata: self.pdata, + schedule: self.schedule, + stack: self.stack, + future: self.future, + output: self.output, + } + } +} + +impl RawProc + where + F: Future + Send + 'static, + R: Send + 'static, + S: Fn(LightProc) + Send + Sync + 'static, + T: Send + 'static, +{ + +} diff --git a/lightproc/src/stack.rs b/lightproc/src/stack.rs new file mode 100644 index 00000000..0577c217 --- /dev/null +++ b/lightproc/src/stack.rs @@ -0,0 +1,9 @@ +use std::sync::atomic::AtomicUsize; + +pub(crate) struct ProcStack { + pub(crate) pid: AtomicUsize, + + pub(crate) after_start: unsafe fn(*const ()), + + pub(crate) after_complete: unsafe fn(*const ()), +} From 206b2af764f1692fff3dd6ad7251f8c44c170f65 Mon Sep 17 00:00:00 2001 From: Mahmut Bulut Date: Wed, 16 Oct 2019 00:29:59 +0200 Subject: [PATCH 03/38] Future layout allocation --- lightproc/Cargo.toml | 3 +++ lightproc/src/lightproc.rs | 5 +++-- lightproc/src/proc_layout.rs | 7 +++++-- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/lightproc/Cargo.toml b/lightproc/Cargo.toml index 28c989a4..586317da 100644 --- a/lightproc/Cargo.toml +++ b/lightproc/Cargo.toml @@ -6,3 +6,6 @@ authors = ["Mahmut Bulut "] keywords = ["fault-tolerant", "runtime", "actor", "system"] categories = [] edition = "2018" + +[dependencies] +rustc-hash = "1.0.1" diff --git a/lightproc/src/lightproc.rs b/lightproc/src/lightproc.rs index 126f1d41..cd8ae210 100644 --- a/lightproc/src/lightproc.rs +++ b/lightproc/src/lightproc.rs @@ -37,9 +37,10 @@ impl LightProc { F: Future + Send + 'static { let fut_mem = Layout::new::(); - let (new_layout, offset_t) = + let (new_layout, offset_f) = extend(self.proc_layout.layout, fut_mem); self.proc_layout.layout = new_layout; - + self.proc_layout.offset_table + .insert("future", offset_f); } } diff --git a/lightproc/src/proc_layout.rs b/lightproc/src/proc_layout.rs index a67d2bf0..980f78df 100644 --- a/lightproc/src/proc_layout.rs +++ b/lightproc/src/proc_layout.rs @@ -1,15 +1,18 @@ use std::alloc::Layout; use crate::stack::ProcStack; +use rustc_hash::FxHashMap; -#[derive(Clone, Copy)] +#[derive(Clone)] pub struct ProcLayout { pub layout: Layout, + pub offset_table: FxHashMap<&'static str, usize> } impl Default for ProcLayout { fn default() -> Self { ProcLayout { - layout: Layout::new::() + layout: Layout::new::(), + offset_table: FxHashMap::default() } } } From 45618339e584db5d123bffe376abce13b800e8eb Mon Sep 17 00:00:00 2001 From: Mahmut Bulut Date: Wed, 16 Oct 2019 01:26:20 +0200 Subject: [PATCH 04/38] Realloc at the assignment side --- lightproc/examples/task_build.rs | 5 +++-- lightproc/src/layout_helpers.rs | 28 ++++++++++++----------- lightproc/src/lib.rs | 8 +++---- lightproc/src/lightproc.rs | 38 +++++++++++++++++++------------- lightproc/src/proc_data.rs | 6 ++--- lightproc/src/proc_handle.rs | 6 ++--- lightproc/src/proc_layout.rs | 6 ++--- lightproc/src/proc_vtable.rs | 5 ++--- lightproc/src/raw_proc.rs | 35 ++++++++++++++++++----------- lightproc/src/state.rs | 1 + 10 files changed, 79 insertions(+), 59 deletions(-) diff --git a/lightproc/examples/task_build.rs b/lightproc/examples/task_build.rs index 19ef5294..13a68702 100644 --- a/lightproc/examples/task_build.rs +++ b/lightproc/examples/task_build.rs @@ -1,6 +1,7 @@ use lightproc::prelude::*; fn main() { - LightProc::<()>::new() - .with_future(async move { println!("test"); }); + LightProc::<()>::new().with_future(async move { + println!("test"); + }); } diff --git a/lightproc/src/layout_helpers.rs b/lightproc/src/layout_helpers.rs index c8b839b9..07d668b7 100644 --- a/lightproc/src/layout_helpers.rs +++ b/lightproc/src/layout_helpers.rs @@ -1,4 +1,4 @@ -use std::alloc::{Layout, LayoutErr}; +use std::alloc::Layout; use std::io::{Error, ErrorKind}; #[inline] @@ -6,24 +6,26 @@ pub fn extend(layout: Layout, next: Layout) -> (Layout, usize) { let new_align = std::cmp::max(layout.align(), next.align()); let pad = padding_needed_for(layout, next.align()); - let offset = layout.size().checked_add(pad) - .ok_or( - Error::new(ErrorKind::Other, "Padding overflow check failed") - ).unwrap(); - let new_size = offset.checked_add(next.size()) - .ok_or( - Error::new(ErrorKind::Other, "New size can't be computed") - ).unwrap(); + let offset = layout + .size() + .checked_add(pad) + .ok_or(Error::new( + ErrorKind::Other, + "Padding overflow check failed", + )) + .unwrap(); + let new_size = offset + .checked_add(next.size()) + .ok_or(Error::new(ErrorKind::Other, "New size can't be computed")) + .unwrap(); - let layout = - Layout::from_size_align(new_size, new_align).unwrap(); + let layout = Layout::from_size_align(new_size, new_align).unwrap(); (layout, offset) } #[inline] pub fn padding_needed_for(layout: Layout, align: usize) -> usize { let len = layout.size(); - let len_rounded_up = len.wrapping_add(align).wrapping_sub(1) - & !align.wrapping_sub(1); + let len_rounded_up = len.wrapping_add(align).wrapping_sub(1) & !align.wrapping_sub(1); len_rounded_up.wrapping_sub(len) } diff --git a/lightproc/src/lib.rs b/lightproc/src/lib.rs index bb49b74a..782d626a 100644 --- a/lightproc/src/lib.rs +++ b/lightproc/src/lib.rs @@ -1,12 +1,12 @@ +pub mod layout_helpers; pub mod lightproc; -pub mod proc_handle; pub mod proc_data; +pub mod proc_handle; +pub mod proc_layout; pub mod proc_vtable; -pub mod state; pub mod raw_proc; -pub mod proc_layout; pub mod stack; -pub mod layout_helpers; +pub mod state; pub mod prelude { pub use crate::lightproc::*; diff --git a/lightproc/src/lightproc.rs b/lightproc/src/lightproc.rs index cd8ae210..8510b402 100644 --- a/lightproc/src/lightproc.rs +++ b/lightproc/src/lightproc.rs @@ -1,19 +1,19 @@ -use std::ptr::NonNull; -use std::marker::PhantomData as marker; +use std::alloc; use std::future::Future; -use crate::proc_handle::ProcHandle; -use crate::raw_proc::RawProc; -use crate::stack::ProcStack; +use std::marker::PhantomData as marker; +use std::ptr::NonNull; + use crate::proc_layout::ProcLayout; -use std::alloc; -use std::alloc::Layout; + use crate::layout_helpers::extend; +use crate::raw_proc::RawProc; +use std::alloc::Layout; pub struct LightProc { pub(crate) raw_proc: NonNull<()>, pub(crate) proc_layout: ProcLayout, - pub(crate) _private: marker + pub(crate) _private: marker, } unsafe impl Send for LightProc {} @@ -25,22 +25,30 @@ impl LightProc { unsafe { LightProc { - raw_proc: NonNull::dangling(), + raw_proc: NonNull::new(alloc::alloc(proc_layout.layout) as *mut ()).unwrap(), proc_layout, - _private: marker + _private: marker, } } } pub fn with_future(mut self, f: F) where - F: Future + Send + 'static + F: Future + Send + 'static, { let fut_mem = Layout::new::(); - let (new_layout, offset_f) = - extend(self.proc_layout.layout, fut_mem); + let (new_layout, offset_f) = extend(self.proc_layout.layout, fut_mem); self.proc_layout.layout = new_layout; - self.proc_layout.offset_table - .insert("future", offset_f); + self.proc_layout.offset_table.insert("future", offset_f); + + unsafe { + alloc::realloc( + self.raw_proc.as_ptr() as *mut u8, + self.proc_layout.layout, + self.proc_layout.layout.size(), + ); + } + + // RawProc::from_ptr(self.raw_proc.as_ptr(), self.proc_layout); } } diff --git a/lightproc/src/proc_data.rs b/lightproc/src/proc_data.rs index 477cd5b7..f324e374 100644 --- a/lightproc/src/proc_data.rs +++ b/lightproc/src/proc_data.rs @@ -1,7 +1,7 @@ -use std::task::Waker; -use std::cell::UnsafeCell; use crate::proc_vtable::ProcVTable; -use std::sync::atomic::{AtomicUsize, Ordering}; +use std::cell::UnsafeCell; +use std::sync::atomic::AtomicUsize; +use std::task::Waker; pub(crate) struct ProcData { pub(crate) state: AtomicUsize, diff --git a/lightproc/src/proc_handle.rs b/lightproc/src/proc_handle.rs index ffcda982..a46f3705 100644 --- a/lightproc/src/proc_handle.rs +++ b/lightproc/src/proc_handle.rs @@ -1,9 +1,9 @@ -use std::ptr::NonNull; use std::marker::PhantomData as marker; +use std::ptr::NonNull; pub struct ProcHandle { raw_proc: NonNull<()>, - _private: marker<(R, T)> + _private: marker<(R, T)>, } unsafe impl Send for ProcHandle {} @@ -13,7 +13,7 @@ impl Unpin for ProcHandle {} impl ProcHandle { pub fn cancel(&self) { - let ptr = self.raw_proc.as_ptr(); + let _ptr = self.raw_proc.as_ptr(); unimplemented!() } } diff --git a/lightproc/src/proc_layout.rs b/lightproc/src/proc_layout.rs index 980f78df..9a1f115b 100644 --- a/lightproc/src/proc_layout.rs +++ b/lightproc/src/proc_layout.rs @@ -1,18 +1,18 @@ -use std::alloc::Layout; use crate::stack::ProcStack; use rustc_hash::FxHashMap; +use std::alloc::Layout; #[derive(Clone)] pub struct ProcLayout { pub layout: Layout, - pub offset_table: FxHashMap<&'static str, usize> + pub offset_table: FxHashMap<&'static str, usize>, } impl Default for ProcLayout { fn default() -> Self { ProcLayout { layout: Layout::new::(), - offset_table: FxHashMap::default() + offset_table: FxHashMap::default(), } } } diff --git a/lightproc/src/proc_vtable.rs b/lightproc/src/proc_vtable.rs index f167d83a..ff3b4b33 100644 --- a/lightproc/src/proc_vtable.rs +++ b/lightproc/src/proc_vtable.rs @@ -5,7 +5,6 @@ pub(crate) struct ProcVTable { pub(crate) raw_waker: RawWakerVTable, pub(crate) schedule: unsafe fn(*const ()), - -// // Callbacks -// pub(crate) callbacks: ProcCallbacks + // // Callbacks + // pub(crate) callbacks: ProcCallbacks } diff --git a/lightproc/src/raw_proc.rs b/lightproc/src/raw_proc.rs index 9e6bc8a0..0c4e4b1f 100644 --- a/lightproc/src/raw_proc.rs +++ b/lightproc/src/raw_proc.rs @@ -1,12 +1,7 @@ -use crate::proc_data::ProcData; -use std::future::Future; use crate::lightproc::LightProc; -use std::ptr::NonNull; -use std::sync::atomic::AtomicUsize; -use std::cell::UnsafeCell; -use std::task::RawWakerVTable; -use std::alloc::{Layout, alloc}; +use crate::proc_data::ProcData; use crate::proc_layout::ProcLayout; +use std::future::Future; /// Raw pointers to the fields of a task. pub struct RawProc { @@ -36,11 +31,25 @@ impl Clone for RawProc { } impl RawProc - where - F: Future + Send + 'static, - R: Send + 'static, - S: Fn(LightProc) + Send + Sync + 'static, - T: Send + 'static, +where + F: Future + Send + 'static, + R: Send + 'static, + S: Fn(LightProc) + Send + Sync + 'static, + T: Send + 'static, { - + #[inline] + pub(crate) fn from_ptr(ptr: *const (), proc_layout: ProcLayout) -> Self { + let p = ptr as *const u8; + + unsafe { + Self { + pdata: p as *const ProcData, + schedule: p.add(proc_layout.offset_table.get("schedule").cloned().unwrap()) + as *const S, + stack: p.add(proc_layout.offset_table.get("stack").cloned().unwrap()) as *mut T, + future: p.add(proc_layout.offset_table.get("future").cloned().unwrap()) as *mut F, + output: p.add(proc_layout.offset_table.get("output").cloned().unwrap()) as *mut R, + } + } + } } diff --git a/lightproc/src/state.rs b/lightproc/src/state.rs index e69de29b..8b137891 100644 --- a/lightproc/src/state.rs +++ b/lightproc/src/state.rs @@ -0,0 +1 @@ + From 5cb0ccc77338c6b0065b0023c3a068672863097a Mon Sep 17 00:00:00 2001 From: Mahmut Bulut Date: Wed, 16 Oct 2019 16:42:45 +0200 Subject: [PATCH 05/38] Fetch relative offsets for data --- lightproc/src/lightproc.rs | 2 +- lightproc/src/raw_proc.rs | 25 ++++++++++++++++++++----- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/lightproc/src/lightproc.rs b/lightproc/src/lightproc.rs index 8510b402..d4beb96c 100644 --- a/lightproc/src/lightproc.rs +++ b/lightproc/src/lightproc.rs @@ -49,6 +49,6 @@ impl LightProc { ); } - // RawProc::from_ptr(self.raw_proc.as_ptr(), self.proc_layout); +// RawProc::from_ptr(self.raw_proc.as_ptr(), self.proc_layout); } } diff --git a/lightproc/src/raw_proc.rs b/lightproc/src/raw_proc.rs index 0c4e4b1f..a449688b 100644 --- a/lightproc/src/raw_proc.rs +++ b/lightproc/src/raw_proc.rs @@ -2,6 +2,7 @@ use crate::lightproc::LightProc; use crate::proc_data::ProcData; use crate::proc_layout::ProcLayout; use std::future::Future; +use std::ptr::NonNull; /// Raw pointers to the fields of a task. pub struct RawProc { @@ -44,12 +45,26 @@ where unsafe { Self { pdata: p as *const ProcData, - schedule: p.add(proc_layout.offset_table.get("schedule").cloned().unwrap()) - as *const S, - stack: p.add(proc_layout.offset_table.get("stack").cloned().unwrap()) as *mut T, - future: p.add(proc_layout.offset_table.get("future").cloned().unwrap()) as *mut F, - output: p.add(proc_layout.offset_table.get("output").cloned().unwrap()) as *mut R, + schedule: p.add( + Self::get_offset(&proc_layout, "schedule") + ) as *const S, + stack: p.add( + Self::get_offset(&proc_layout, "stack") + ) as *mut T, + future: p.add( + Self::get_offset(&proc_layout, "future") + ) as *mut F, + output: p.add( + Self::get_offset(&proc_layout, "output") + ) as *mut R, } } } + + #[inline] + pub(crate) fn get_offset(proc_layout: &ProcLayout, offset_of: &str) -> usize { + if let Some(offset) = proc_layout.offset_table.get(offset_of).cloned() { + offset + } else { 0x00_usize } + } } From 2114610303c472add40dc8a22df0d16a624f7338 Mon Sep 17 00:00:00 2001 From: Mahmut Bulut Date: Wed, 16 Oct 2019 16:43:36 +0200 Subject: [PATCH 06/38] cargo fix --- lightproc/src/lightproc.rs | 2 +- lightproc/src/raw_proc.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lightproc/src/lightproc.rs b/lightproc/src/lightproc.rs index d4beb96c..601ce53c 100644 --- a/lightproc/src/lightproc.rs +++ b/lightproc/src/lightproc.rs @@ -6,7 +6,7 @@ use std::ptr::NonNull; use crate::proc_layout::ProcLayout; use crate::layout_helpers::extend; -use crate::raw_proc::RawProc; + use std::alloc::Layout; pub struct LightProc { diff --git a/lightproc/src/raw_proc.rs b/lightproc/src/raw_proc.rs index a449688b..23b3c841 100644 --- a/lightproc/src/raw_proc.rs +++ b/lightproc/src/raw_proc.rs @@ -2,7 +2,7 @@ use crate::lightproc::LightProc; use crate::proc_data::ProcData; use crate::proc_layout::ProcLayout; use std::future::Future; -use std::ptr::NonNull; + /// Raw pointers to the fields of a task. pub struct RawProc { From 252893cf57afd411f27ec17c8906d840bde93364 Mon Sep 17 00:00:00 2001 From: Mahmut Bulut Date: Wed, 16 Oct 2019 22:19:23 +0200 Subject: [PATCH 07/38] Realloc pointer alignment and Debug impls --- lightproc/examples/task_build.rs | 29 ++++++++++- lightproc/src/lightproc.rs | 87 ++++++++++++++++++++++++++++++-- lightproc/src/proc_handle.rs | 4 +- lightproc/src/proc_layout.rs | 7 +-- lightproc/src/raw_proc.rs | 21 ++++---- lightproc/src/stack.rs | 20 ++++++-- 6 files changed, 142 insertions(+), 26 deletions(-) diff --git a/lightproc/examples/task_build.rs b/lightproc/examples/task_build.rs index 13a68702..ddd29ae9 100644 --- a/lightproc/examples/task_build.rs +++ b/lightproc/examples/task_build.rs @@ -1,7 +1,34 @@ use lightproc::prelude::*; +use lightproc::stack::ProcStack; fn main() { - LightProc::<()>::new().with_future(async move { + let lp = LightProc::<()>::new().with_future(async move { println!("test"); }); + dbg!(lp); + + let lp2 = LightProc::<()>::new() + .with_future(async move { + println!("future"); + }) + .with_schedule(|t| { + println!("scheduler"); + }); + dbg!(lp2); + + let lp3 = LightProc::<()>::new() + .with_schedule(|t| { + println!("scheduler"); + }); + dbg!(lp3); + + let lp4 = LightProc::::new() + .with_future(async move { + println!("future"); + }) + .with_schedule(|t| { + println!("scheduler"); + }) + .with_stack(ProcStack::default()); + dbg!(lp4); } diff --git a/lightproc/src/lightproc.rs b/lightproc/src/lightproc.rs index 601ce53c..54e51025 100644 --- a/lightproc/src/lightproc.rs +++ b/lightproc/src/lightproc.rs @@ -8,7 +8,11 @@ use crate::proc_layout::ProcLayout; use crate::layout_helpers::extend; use std::alloc::Layout; +use crate::raw_proc::RawProc; +use crate::proc_handle::ProcHandle; +use crate::stack::ProcStack; +#[derive(Debug)] pub struct LightProc { pub(crate) raw_proc: NonNull<()>, @@ -32,23 +36,96 @@ impl LightProc { } } - pub fn with_future(mut self, f: F) + pub fn with_future(mut self, f: F) -> Self where F: Future + Send + 'static, + R: Send + 'static, { let fut_mem = Layout::new::(); let (new_layout, offset_f) = extend(self.proc_layout.layout, fut_mem); - self.proc_layout.layout = new_layout; self.proc_layout.offset_table.insert("future", offset_f); + self.reallocate(new_layout); + + let rawp = + RawProc::::from_ptr( + self.raw_proc.as_ptr(), &self.proc_layout); + + unsafe { + rawp.future.write(f); + } + + self + } + + pub fn with_schedule(mut self, s: S) -> Self + where + S: Fn(LightProc) + Send + Sync + 'static, + T: Send + 'static, + { + let sched_mem = Layout::new::(); + let (new_layout, offset_s) = extend(self.proc_layout.layout, sched_mem); + self.proc_layout.offset_table.insert("schedule", offset_s); + + self.reallocate(new_layout); + + let rawp = + RawProc::::from_ptr( + self.raw_proc.as_ptr(), &self.proc_layout); + + unsafe { + (rawp.schedule as *mut S).write(s); + } + + self + } + + pub fn with_stack(mut self, st: T) -> Self + where T: Send + 'static, + { + let stack_mem = Layout::new::(); + let (new_layout, offset_st) = extend(self.proc_layout.layout, stack_mem); + self.proc_layout.offset_table.insert("stack", offset_st); + + println!("=============="); + + self.reallocate(new_layout); + + let rawp = + RawProc::::from_ptr( + self.raw_proc.as_ptr(), &self.proc_layout); + + unsafe { + rawp.stack.write(st); + } + + self + } + + pub fn build(mut self) -> (LightProc, ProcHandle) { + let raw_proc = self.raw_proc; + let proc = LightProc { + raw_proc, + proc_layout: self.proc_layout, + _private: marker, + }; + let handle = ProcHandle { + raw_proc, + _private: marker, + }; + (proc, handle) + } + + fn reallocate(&mut self, added: Layout) { unsafe { - alloc::realloc( + let pointer = alloc::realloc( self.raw_proc.as_ptr() as *mut u8, self.proc_layout.layout, - self.proc_layout.layout.size(), + added.size(), ); + self.raw_proc = NonNull::new(pointer as *mut ()).unwrap() } -// RawProc::from_ptr(self.raw_proc.as_ptr(), self.proc_layout); + self.proc_layout.layout = added; } } diff --git a/lightproc/src/proc_handle.rs b/lightproc/src/proc_handle.rs index a46f3705..676dbf1f 100644 --- a/lightproc/src/proc_handle.rs +++ b/lightproc/src/proc_handle.rs @@ -2,8 +2,8 @@ use std::marker::PhantomData as marker; use std::ptr::NonNull; pub struct ProcHandle { - raw_proc: NonNull<()>, - _private: marker<(R, T)>, + pub(crate) raw_proc: NonNull<()>, + pub(crate) _private: marker<(R, T)>, } unsafe impl Send for ProcHandle {} diff --git a/lightproc/src/proc_layout.rs b/lightproc/src/proc_layout.rs index 9a1f115b..c690c2ce 100644 --- a/lightproc/src/proc_layout.rs +++ b/lightproc/src/proc_layout.rs @@ -1,8 +1,9 @@ -use crate::stack::ProcStack; use rustc_hash::FxHashMap; use std::alloc::Layout; +use crate::proc_data::ProcData; +use crate::stack::ProcStack; -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct ProcLayout { pub layout: Layout, pub offset_table: FxHashMap<&'static str, usize>, @@ -11,7 +12,7 @@ pub struct ProcLayout { impl Default for ProcLayout { fn default() -> Self { ProcLayout { - layout: Layout::new::(), + layout: Layout::new::(), offset_table: FxHashMap::default(), } } diff --git a/lightproc/src/raw_proc.rs b/lightproc/src/raw_proc.rs index 23b3c841..903fc3ac 100644 --- a/lightproc/src/raw_proc.rs +++ b/lightproc/src/raw_proc.rs @@ -32,30 +32,25 @@ impl Clone for RawProc { } impl RawProc -where - F: Future + Send + 'static, - R: Send + 'static, - S: Fn(LightProc) + Send + Sync + 'static, - T: Send + 'static, { #[inline] - pub(crate) fn from_ptr(ptr: *const (), proc_layout: ProcLayout) -> Self { + pub(crate) fn from_ptr(ptr: *const (), proc_layout: &ProcLayout) -> Self { let p = ptr as *const u8; unsafe { Self { pdata: p as *const ProcData, schedule: p.add( - Self::get_offset(&proc_layout, "schedule") + Self::get_offset(proc_layout, "schedule") ) as *const S, stack: p.add( - Self::get_offset(&proc_layout, "stack") + Self::get_offset(proc_layout, "stack") ) as *mut T, future: p.add( - Self::get_offset(&proc_layout, "future") + Self::get_offset(proc_layout, "future") ) as *mut F, output: p.add( - Self::get_offset(&proc_layout, "output") + Self::get_offset(proc_layout, "output") ) as *mut R, } } @@ -64,7 +59,11 @@ where #[inline] pub(crate) fn get_offset(proc_layout: &ProcLayout, offset_of: &str) -> usize { if let Some(offset) = proc_layout.offset_table.get(offset_of).cloned() { + dbg!(offset); offset - } else { 0x00_usize } + } else { + dbg!("OFFSET NOT FOUND"); + 0x00_usize + } } } diff --git a/lightproc/src/stack.rs b/lightproc/src/stack.rs index 0577c217..9b263a42 100644 --- a/lightproc/src/stack.rs +++ b/lightproc/src/stack.rs @@ -1,9 +1,21 @@ use std::sync::atomic::AtomicUsize; +use std::sync::Arc; +use std::fmt::{Formatter, Error}; +use std::fmt; -pub(crate) struct ProcStack { - pub(crate) pid: AtomicUsize, +#[derive(Default)] +pub struct ProcStack { + pub pid: AtomicUsize, - pub(crate) after_start: unsafe fn(*const ()), + pub after_start: Option>, - pub(crate) after_complete: unsafe fn(*const ()), + pub after_complete: Option>, +} + +impl fmt::Debug for ProcStack { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { + f.debug_struct("ProcStack") + .field("pid", &self.pid) + .finish() + } } From a84683434bbe988e3110adc89538161c78ffb5a1 Mon Sep 17 00:00:00 2001 From: Mahmut Bulut Date: Wed, 16 Oct 2019 22:46:15 +0200 Subject: [PATCH 08/38] Remove debug lines --- lightproc/examples/task_build.rs | 11 +++++++++++ lightproc/src/lightproc.rs | 4 +--- lightproc/src/raw_proc.rs | 1 - 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/lightproc/examples/task_build.rs b/lightproc/examples/task_build.rs index ddd29ae9..100d80a8 100644 --- a/lightproc/examples/task_build.rs +++ b/lightproc/examples/task_build.rs @@ -1,5 +1,6 @@ use lightproc::prelude::*; use lightproc::stack::ProcStack; +use lightproc::proc_handle::ProcHandle; fn main() { let lp = LightProc::<()>::new().with_future(async move { @@ -31,4 +32,14 @@ fn main() { }) .with_stack(ProcStack::default()); dbg!(lp4); + + let (proc, handle) = LightProc::::new() + .with_future(async move { + println!("future"); + }) + .with_schedule(|t| { + println!("scheduler"); + }) + .with_stack(ProcStack::default()) + .returning::(); } diff --git a/lightproc/src/lightproc.rs b/lightproc/src/lightproc.rs index 54e51025..3572b83b 100644 --- a/lightproc/src/lightproc.rs +++ b/lightproc/src/lightproc.rs @@ -87,8 +87,6 @@ impl LightProc { let (new_layout, offset_st) = extend(self.proc_layout.layout, stack_mem); self.proc_layout.offset_table.insert("stack", offset_st); - println!("=============="); - self.reallocate(new_layout); let rawp = @@ -102,7 +100,7 @@ impl LightProc { self } - pub fn build(mut self) -> (LightProc, ProcHandle) { + pub fn returning(mut self) -> (LightProc, ProcHandle) { let raw_proc = self.raw_proc; let proc = LightProc { raw_proc, diff --git a/lightproc/src/raw_proc.rs b/lightproc/src/raw_proc.rs index 903fc3ac..ad9b7629 100644 --- a/lightproc/src/raw_proc.rs +++ b/lightproc/src/raw_proc.rs @@ -62,7 +62,6 @@ impl RawProc dbg!(offset); offset } else { - dbg!("OFFSET NOT FOUND"); 0x00_usize } } From 7deedca275710b69ba1a071981768407391a7fae Mon Sep 17 00:00:00 2001 From: Mahmut Bulut Date: Thu, 17 Oct 2019 22:30:31 +0200 Subject: [PATCH 09/38] Test --- lightproc/Cargo.toml | 5 + lightproc/examples/proc_run.rs | 57 +++++++++ lightproc/src/lib.rs | 2 + lightproc/src/lightproc.rs | 83 ++++++++++-- lightproc/src/proc_data.rs | 132 ++++++++++++++++++- lightproc/src/proc_handle.rs | 226 ++++++++++++++++++++++++++++++++- lightproc/src/proc_vtable.rs | 18 ++- lightproc/src/state.rs | 65 ++++++++++ 8 files changed, 569 insertions(+), 19 deletions(-) create mode 100644 lightproc/examples/proc_run.rs diff --git a/lightproc/Cargo.toml b/lightproc/Cargo.toml index 586317da..8d143887 100644 --- a/lightproc/Cargo.toml +++ b/lightproc/Cargo.toml @@ -8,4 +8,9 @@ categories = [] edition = "2018" [dependencies] +crossbeam-utils = "0.6.6" rustc-hash = "1.0.1" + +[dev-dependencies] +crossbeam = "0.7.1" +futures-preview = "0.3.0-alpha.17" diff --git a/lightproc/examples/proc_run.rs b/lightproc/examples/proc_run.rs new file mode 100644 index 00000000..35771876 --- /dev/null +++ b/lightproc/examples/proc_run.rs @@ -0,0 +1,57 @@ +//! A function that runs a future to completion on a dedicated thread. + +use std::future::Future; +use std::sync::Arc; +use std::thread; + +use crossbeam::channel; +use futures::executor; +use lightproc::prelude::*; + +/// Spawns a future on a new dedicated thread. +/// +/// The returned handle can be used to await the output of the future. +fn spawn_on_thread(future: F) -> ProcHandle + where + F: Future + Send + 'static, + R: Send + 'static, +{ + // Create a channel that holds the task when it is scheduled for running. + let (sender, receiver) = channel::unbounded(); + let sender = Arc::new(sender); + let s = Arc::downgrade(&sender); + + // Wrap the future into one that disconnects the channel on completion. + let future = async move { + // When the inner future completes, the sender gets dropped and disconnects the channel. + let _sender = sender; + future.await + }; + + // Create a task that is scheduled by sending itself into the channel. + let schedule = move |t| s.upgrade().unwrap().send(t).unwrap(); + let (proc, handle) = LightProc::<()>::new() + .with_future(future) + .with_schedule(schedule) + .with_stack(ProcStack::default()) + .returning(); + + // Schedule the task by sending it into the channel. + proc.schedule(); + + // Spawn a thread running the task to completion. + thread::spawn(move || { + // Keep taking the task from the channel and running it until completion. + for proc in receiver { + proc.run(); + } + }); + + handle +} + +fn main() { + executor::block_on(spawn_on_thread(async { + println!("Hello, world!"); + })); +} diff --git a/lightproc/src/lib.rs b/lightproc/src/lib.rs index 782d626a..a58839c8 100644 --- a/lightproc/src/lib.rs +++ b/lightproc/src/lib.rs @@ -10,5 +10,7 @@ pub mod state; pub mod prelude { pub use crate::lightproc::*; + pub use crate::stack::*; pub use crate::proc_layout::*; + pub use crate::proc_handle::*; } diff --git a/lightproc/src/lightproc.rs b/lightproc/src/lightproc.rs index 3572b83b..b6b0080b 100644 --- a/lightproc/src/lightproc.rs +++ b/lightproc/src/lightproc.rs @@ -1,4 +1,4 @@ -use std::alloc; +use std::{alloc, mem, ptr}; use std::future::Future; use std::marker::PhantomData as marker; use std::ptr::NonNull; @@ -11,6 +11,7 @@ use std::alloc::Layout; use crate::raw_proc::RawProc; use crate::proc_handle::ProcHandle; use crate::stack::ProcStack; +use crate::proc_data::ProcData; #[derive(Debug)] pub struct LightProc { @@ -80,17 +81,16 @@ impl LightProc { self } - pub fn with_stack(mut self, st: T) -> Self - where T: Send + 'static, + pub fn with_stack(mut self, st: ProcStack) -> Self { - let stack_mem = Layout::new::(); + let stack_mem = Layout::new::(); let (new_layout, offset_st) = extend(self.proc_layout.layout, stack_mem); self.proc_layout.offset_table.insert("stack", offset_st); self.reallocate(new_layout); let rawp = - RawProc::::from_ptr( + RawProc::::from_ptr( self.raw_proc.as_ptr(), &self.proc_layout); unsafe { @@ -104,7 +104,7 @@ impl LightProc { let raw_proc = self.raw_proc; let proc = LightProc { raw_proc, - proc_layout: self.proc_layout, + proc_layout: self.proc_layout.clone(), _private: marker, }; let handle = ProcHandle { @@ -114,16 +114,75 @@ impl LightProc { (proc, handle) } - fn reallocate(&mut self, added: Layout) { + pub fn schedule(self) { + let ptr = self.raw_proc.as_ptr(); + let header = ptr as *const ProcData; + mem::forget(self); + + unsafe { + ((*header).vtable.schedule)(ptr); + } + } + + pub fn run(self) { + let ptr = self.raw_proc.as_ptr(); + let header = ptr as *const ProcData; + mem::forget(self); + + unsafe { + ((*header).vtable.run)(ptr); + } + } + + pub fn cancel(&self) { + let ptr = self.raw_proc.as_ptr(); + let header = ptr as *const ProcData; + + unsafe { + (*header).cancel(); + } + } + + pub fn stack(&self) -> &T { + let offset = ProcData::offset_tag::(); + let ptr = self.raw_proc.as_ptr(); + unsafe { - let pointer = alloc::realloc( - self.raw_proc.as_ptr() as *mut u8, + let raw = (ptr as *mut u8).add(offset) as *const T; + &*raw + } + } + + fn reallocate(&mut self, enlarged: Layout) { + unsafe { + let old = self.raw_proc.as_ptr() as *mut u8; + let bigger = alloc::realloc( + old, self.proc_layout.layout, - added.size(), + enlarged.size(), ); - self.raw_proc = NonNull::new(pointer as *mut ()).unwrap() + ptr::copy(old, bigger, self.proc_layout.layout.size()); + self.raw_proc = NonNull::new(bigger as *mut ()).unwrap() } - self.proc_layout.layout = added; + self.proc_layout.layout = enlarged; + } +} + +impl Drop for LightProc { + fn drop(&mut self) { + let ptr = self.raw_proc.as_ptr(); + let header = ptr as *const ProcData; + + unsafe { + // Cancel the task. + (*header).cancel(); + + // Drop the future. + ((*header).vtable.drop_future)(ptr); + + // Drop the task reference. + ((*header).vtable.decrement)(ptr); + } } } diff --git a/lightproc/src/proc_data.rs b/lightproc/src/proc_data.rs index f324e374..66fb48ca 100644 --- a/lightproc/src/proc_data.rs +++ b/lightproc/src/proc_data.rs @@ -1,12 +1,138 @@ use crate::proc_vtable::ProcVTable; -use std::cell::UnsafeCell; -use std::sync::atomic::AtomicUsize; +use std::cell::Cell; +use std::sync::atomic::{AtomicUsize, Ordering}; use std::task::Waker; +use std::fmt; +use crate::state::*; +use std::alloc::Layout; +use crate::layout_helpers::*; +use crossbeam_utils::Backoff; pub(crate) struct ProcData { pub(crate) state: AtomicUsize, - pub(crate) awaiter: UnsafeCell>, + pub(crate) awaiter: Cell>, pub(crate) vtable: &'static ProcVTable, } + + +impl ProcData { + /// Cancels the task. + /// + /// This method will only mark the task as closed and will notify the awaiter, but it won't + /// reschedule the task if it's not completed. + pub(crate) fn cancel(&self) { + let mut state = self.state.load(Ordering::Acquire); + + loop { + // If the task has been completed or closed, it can't be cancelled. + if state & (COMPLETED | CLOSED) != 0 { + break; + } + + // Mark the task as closed. + match self.state.compare_exchange_weak( + state, + state | CLOSED, + Ordering::AcqRel, + Ordering::Acquire, + ) { + Ok(_) => { + // Notify the awaiter that the task has been closed. + if state & AWAITER != 0 { + self.notify(); + } + + break; + } + Err(s) => state = s, + } + } + } + + /// Notifies the task blocked on the task. + /// + /// If there is a registered waker, it will be removed from the header and woken. + #[inline] + pub(crate) fn notify(&self) { + if let Some(waker) = self.swap_awaiter(None) { + waker.wake(); + } + } + + /// Notifies the task blocked on the task unless its waker matches `current`. + /// + /// If there is a registered waker, it will be removed from the header. + #[inline] + pub(crate) fn notify_unless(&self, current: &Waker) { + if let Some(waker) = self.swap_awaiter(None) { + if !waker.will_wake(current) { + waker.wake(); + } + } + } + + /// Swaps the awaiter and returns the previous value. + #[inline] + pub(crate) fn swap_awaiter(&self, new: Option) -> Option { + let new_is_none = new.is_none(); + + // We're about to try acquiring the lock in a loop. If it's already being held by another + // thread, we'll have to spin for a while so it's best to employ a backoff strategy. + let backoff = Backoff::new(); + loop { + // Acquire the lock. If we're storing an awaiter, then also set the awaiter flag. + let state = if new_is_none { + self.state.fetch_or(LOCKED, Ordering::Acquire) + } else { + self.state.fetch_or(LOCKED | AWAITER, Ordering::Acquire) + }; + + // If the lock was acquired, break from the loop. + if state & LOCKED == 0 { + break; + } + + // Snooze for a little while because the lock is held by another thread. + backoff.snooze(); + } + + // Replace the awaiter. + let old = self.awaiter.replace(new); + + // Release the lock. If we've cleared the awaiter, then also unset the awaiter flag. + if new_is_none { + self.state.fetch_and(!LOCKED & !AWAITER, Ordering::Release); + } else { + self.state.fetch_and(!LOCKED, Ordering::Release); + } + + old + } + + /// Returns the offset at which the tag of type `T` is stored. + #[inline] + pub(crate) fn offset_tag() -> usize { + let layout_proc_data = Layout::new::(); + let layout_t = Layout::new::(); + let (_, offset_t) = extend(layout_proc_data, layout_t); + offset_t + } +} + +impl fmt::Debug for ProcData { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let state = self.state.load(Ordering::SeqCst); + + f.debug_struct("ProcData") + .field("scheduled", &(state & SCHEDULED != 0)) + .field("running", &(state & RUNNING != 0)) + .field("completed", &(state & COMPLETED != 0)) + .field("closed", &(state & CLOSED != 0)) + .field("awaiter", &(state & AWAITER != 0)) + .field("handle", &(state & HANDLE != 0)) + .field("ref_count", &(state / REFERENCE)) + .finish() + } +} diff --git a/lightproc/src/proc_handle.rs b/lightproc/src/proc_handle.rs index 676dbf1f..cec57a77 100644 --- a/lightproc/src/proc_handle.rs +++ b/lightproc/src/proc_handle.rs @@ -1,5 +1,12 @@ use std::marker::PhantomData as marker; use std::ptr::NonNull; +use std::pin::Pin; +use std::task::{Context, Poll}; +use std::future::Future; +use std::sync::atomic::Ordering; +use crate::proc_data::ProcData; +use crate::state::*; +use std::fmt; pub struct ProcHandle { pub(crate) raw_proc: NonNull<()>, @@ -12,8 +19,223 @@ unsafe impl Sync for ProcHandle {} impl Unpin for ProcHandle {} impl ProcHandle { + /// Cancels the task. + /// + /// If the task has already completed, calling this method will have no effect. + /// + /// When a task is cancelled, its future cannot be polled again and will be dropped instead. pub fn cancel(&self) { - let _ptr = self.raw_proc.as_ptr(); - unimplemented!() + let ptr = self.raw_proc.as_ptr(); + let header = ptr as *const ProcData; + + unsafe { + let mut state = (*header).state.load(Ordering::Acquire); + + loop { + // If the task has been completed or closed, it can't be cancelled. + if state & (COMPLETED | CLOSED) != 0 { + break; + } + + // If the task is not scheduled nor running, we'll need to schedule it. + let new = if state & (SCHEDULED | RUNNING) == 0 { + (state | SCHEDULED | CLOSED) + REFERENCE + } else { + state | CLOSED + }; + + // Mark the task as closed. + match (*header).state.compare_exchange_weak( + state, + new, + Ordering::AcqRel, + Ordering::Acquire, + ) { + Ok(_) => { + // If the task is not scheduled nor running, schedule it so that its future + // gets dropped by the executor. + if state & (SCHEDULED | RUNNING) == 0 { + ((*header).vtable.schedule)(ptr); + } + + // Notify the awaiter that the task has been closed. + if state & AWAITER != 0 { + (*header).notify(); + } + + break; + } + Err(s) => state = s, + } + } + } + } + + pub fn stack(&self) -> &T { + let offset = ProcData::offset_tag::(); + let ptr = self.raw_proc.as_ptr(); + + unsafe { + let raw = (ptr as *mut u8).add(offset) as *const T; + &*raw + } + } +} + +impl Drop for ProcHandle { + fn drop(&mut self) { + let ptr = self.raw_proc.as_ptr(); + let header = ptr as *const ProcData; + + // A place where the output will be stored in case it needs to be dropped. + let mut output = None; + + unsafe { + // Optimistically assume the `JoinHandle` is being dropped just after creating the + // task. This is a common case so if the handle is not used, the overhead of it is only + // one compare-exchange operation. + if let Err(mut state) = (*header).state.compare_exchange_weak( + SCHEDULED | HANDLE | REFERENCE, + SCHEDULED | REFERENCE, + Ordering::AcqRel, + Ordering::Acquire, + ) { + loop { + // If the task has been completed but not yet closed, that means its output + // must be dropped. + if state & COMPLETED != 0 && state & CLOSED == 0 { + // Mark the task as closed in order to grab its output. + match (*header).state.compare_exchange_weak( + state, + state | CLOSED, + Ordering::AcqRel, + Ordering::Acquire, + ) { + Ok(_) => { + // Read the output. + output = + Some((((*header).vtable.get_output)(ptr) as *mut R).read()); + + // Update the state variable because we're continuing the loop. + state |= CLOSED; + } + Err(s) => state = s, + } + } else { + // If this is the last reference to the task and it's not closed, then + // close it and schedule one more time so that its future gets dropped by + // the executor. + let new = if state & (!(REFERENCE - 1) | CLOSED) == 0 { + SCHEDULED | CLOSED | REFERENCE + } else { + state & !HANDLE + }; + + // Unset the handle flag. + match (*header).state.compare_exchange_weak( + state, + new, + Ordering::AcqRel, + Ordering::Acquire, + ) { + Ok(_) => { + // If this is the last reference to the task, we need to either + // schedule dropping its future or destroy it. + if state & !(REFERENCE - 1) == 0 { + if state & CLOSED == 0 { + ((*header).vtable.schedule)(ptr); + } else { + ((*header).vtable.destroy)(ptr); + } + } + + break; + } + Err(s) => state = s, + } + } + } + } + } + + // Drop the output if it was taken out of the task. + drop(output); + } +} + +impl Future for ProcHandle { + type Output = Option; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let ptr = self.raw_proc.as_ptr(); + let header = ptr as *const ProcData; + + unsafe { + let mut state = (*header).state.load(Ordering::Acquire); + + loop { + // If the task has been closed, notify the awaiter and return `None`. + if state & CLOSED != 0 { + // Even though the awaiter is most likely the current task, it could also be + // another task. + (*header).notify_unless(cx.waker()); + return Poll::Ready(None); + } + + // If the task is not completed, register the current task. + if state & COMPLETED == 0 { + (*header).swap_awaiter(Some(cx.waker().clone())); + + // Reload the state after registering. It is possible that the task became + // completed or closed just before registration so we need to check for that. + state = (*header).state.load(Ordering::Acquire); + + // If the task has been closed, notify the awaiter and return `None`. + if state & CLOSED != 0 { + // Even though the awaiter is most likely the current task, it could also + // be another task. + (*header).notify_unless(cx.waker()); + return Poll::Ready(None); + } + + // If the task is still not completed, we're blocked on it. + if state & COMPLETED == 0 { + return Poll::Pending; + } + } + + // Since the task is now completed, mark it as closed in order to grab its output. + match (*header).state.compare_exchange( + state, + state | CLOSED, + Ordering::AcqRel, + Ordering::Acquire, + ) { + Ok(_) => { + // Notify the awaiter. Even though the awaiter is most likely the current + // task, it could also be another task. + if state & AWAITER != 0 { + (*header).notify_unless(cx.waker()); + } + + // Take the output from the task. + let output = ((*header).vtable.get_output)(ptr) as *mut R; + return Poll::Ready(Some(output.read())); + } + Err(s) => state = s, + } + } + } + } +} + +impl fmt::Debug for ProcHandle { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let ptr = self.raw_proc.as_ptr(); + let header = ptr as *const ProcData; + + f.debug_struct("JoinHandle") + .field("procdata", unsafe { &(*header) }) + .finish() } } diff --git a/lightproc/src/proc_vtable.rs b/lightproc/src/proc_vtable.rs index ff3b4b33..0e818a1e 100644 --- a/lightproc/src/proc_vtable.rs +++ b/lightproc/src/proc_vtable.rs @@ -4,7 +4,21 @@ pub(crate) struct ProcVTable { /// The raw waker vtable. pub(crate) raw_waker: RawWakerVTable, + /// Schedules the task. pub(crate) schedule: unsafe fn(*const ()), - // // Callbacks - // pub(crate) callbacks: ProcCallbacks + + /// Drops the future inside the task. + pub(crate) drop_future: unsafe fn(*const ()), + + /// Returns a pointer to the output stored after completion. + pub(crate) get_output: unsafe fn(*const ()) -> *const (), + + /// Drops a waker or a task. + pub(crate) decrement: unsafe fn(ptr: *const ()), + + /// Destroys the task. + pub(crate) destroy: unsafe fn(*const ()), + + /// Runs the task. + pub(crate) run: unsafe fn(*const ()), } diff --git a/lightproc/src/state.rs b/lightproc/src/state.rs index 8b137891..b1899efc 100644 --- a/lightproc/src/state.rs +++ b/lightproc/src/state.rs @@ -1 +1,66 @@ +/// Set if the task is scheduled for running. +/// +/// A task is considered to be scheduled whenever its `Task` reference exists. It is in scheduled +/// state at the moment of creation and when it gets unapused either by its `JoinHandle` or woken +/// by a `Waker`. +/// +/// This flag can't be set when the task is completed. However, it can be set while the task is +/// running, in which case it will be rescheduled as soon as polling finishes. +pub(crate) const SCHEDULED: usize = 1 << 0; + +/// Set if the task is running. +/// +/// A task is running state while its future is being polled. +/// +/// This flag can't be set when the task is completed. However, it can be in scheduled state while +/// it is running, in which case it will be rescheduled when it stops being polled. +pub(crate) const RUNNING: usize = 1 << 1; + +/// Set if the task has been completed. +/// +/// This flag is set when polling returns `Poll::Ready`. The output of the future is then stored +/// inside the task until it becomes stopped. In fact, `JoinHandle` picks the output up by marking +/// the task as stopped. +/// +/// This flag can't be set when the task is scheduled or completed. +pub(crate) const COMPLETED: usize = 1 << 2; + +/// Set if the task is closed. +/// +/// If a task is closed, that means its either cancelled or its output has been consumed by the +/// `JoinHandle`. A task becomes closed when: +/// +/// 1. It gets cancelled by `Task::cancel()` or `JoinHandle::cancel()`. +/// 2. Its output is awaited by the `JoinHandle`. +/// 3. It panics while polling the future. +/// 4. It is completed and the `JoinHandle` is dropped. +pub(crate) const CLOSED: usize = 1 << 3; + +/// Set if the `JoinHandle` still exists. +/// +/// The `JoinHandle` is a special case in that it is only tracked by this flag, while all other +/// task references (`Task` and `Waker`s) are tracked by the reference count. +pub(crate) const HANDLE: usize = 1 << 4; + +/// Set if the `JoinHandle` is awaiting the output. +/// +/// This flag is set while there is a registered awaiter of type `Waker` inside the task. When the +/// task gets closed or completed, we need to wake the awaiter. This flag can be used as a fast +/// check that tells us if we need to wake anyone without acquiring the lock inside the task. +pub(crate) const AWAITER: usize = 1 << 5; + +/// Set if the awaiter is locked. +/// +/// This lock is acquired before a new awaiter is registered or the existing one is woken. +pub(crate) const LOCKED: usize = 1 << 6; + +/// A single reference. +/// +/// The lower bits in the state contain various flags representing the task state, while the upper +/// bits contain the reference count. The value of `REFERENCE` represents a single reference in the +/// total reference count. +/// +/// Note that the reference counter only tracks the `Task` and `Waker`s. The `JoinHandle` is +/// tracked separately by the `HANDLE` flag. +pub(crate) const REFERENCE: usize = 1 << 7; From e0767929dad99bf5a2414dbabb2bbc1f016d02aa Mon Sep 17 00:00:00 2001 From: Mahmut Bulut Date: Sun, 20 Oct 2019 21:41:20 +0200 Subject: [PATCH 10/38] Offset access --- lightproc/examples/proc_run.rs | 26 +- lightproc/src/align_proc.rs | 57 ++++ lightproc/src/layout_helpers.rs | 5 +- lightproc/src/lib.rs | 3 + lightproc/src/lightproc.rs | 128 +------ lightproc/src/proc_data.rs | 46 ++- lightproc/src/proc_handle.rs | 61 ++-- lightproc/src/raw_proc.rs | 571 ++++++++++++++++++++++++++++++-- lightproc/src/stack.rs | 5 +- lightproc/src/state.rs | 4 + lightproc/src/utils.rs | 21 ++ 11 files changed, 749 insertions(+), 178 deletions(-) create mode 100644 lightproc/src/align_proc.rs create mode 100644 lightproc/src/utils.rs diff --git a/lightproc/examples/proc_run.rs b/lightproc/examples/proc_run.rs index 35771876..f51249ab 100644 --- a/lightproc/examples/proc_run.rs +++ b/lightproc/examples/proc_run.rs @@ -6,12 +6,16 @@ use std::thread; use crossbeam::channel; use futures::executor; -use lightproc::prelude::*; +use std::sync::atomic::AtomicUsize; +use lightproc::proc_handle::ProcHandle; +use lightproc::stack::ProcStack; +use lightproc::lightproc::LightProc; +use std::time::Duration; /// Spawns a future on a new dedicated thread. /// /// The returned handle can be used to await the output of the future. -fn spawn_on_thread(future: F) -> ProcHandle +fn spawn_on_thread(fut: F) -> ProcHandle where F: Future + Send + 'static, R: Send + 'static, @@ -24,17 +28,19 @@ fn spawn_on_thread(future: F) -> ProcHandle // Wrap the future into one that disconnects the channel on completion. let future = async move { // When the inner future completes, the sender gets dropped and disconnects the channel. + println!("exec"); let _sender = sender; - future.await + println!("exec1"); + fut.await }; // Create a task that is scheduled by sending itself into the channel. let schedule = move |t| s.upgrade().unwrap().send(t).unwrap(); - let (proc, handle) = LightProc::<()>::new() - .with_future(future) - .with_schedule(schedule) - .with_stack(ProcStack::default()) - .returning(); + let (proc, handle) = LightProc::build( + future, + schedule, + ProcStack::default() + ); // Schedule the task by sending it into the channel. proc.schedule(); @@ -43,7 +49,9 @@ fn spawn_on_thread(future: F) -> ProcHandle thread::spawn(move || { // Keep taking the task from the channel and running it until completion. for proc in receiver { + println!("ad"); proc.run(); + println!("ad2"); } }); @@ -54,4 +62,6 @@ fn main() { executor::block_on(spawn_on_thread(async { println!("Hello, world!"); })); + + thread::sleep(Duration::new(5, 0)); } diff --git a/lightproc/src/align_proc.rs b/lightproc/src/align_proc.rs new file mode 100644 index 00000000..212257d8 --- /dev/null +++ b/lightproc/src/align_proc.rs @@ -0,0 +1,57 @@ +use crate::proc_layout::ProcLayout; +use std::ptr::NonNull; +use crate::proc_data::ProcData; +use std::sync::atomic::AtomicUsize; +use crate::state::*; +use std::cell::Cell; +use crate::proc_vtable::ProcVTable; +use std::task::RawWakerVTable; + +pub struct AlignProc { + pub(crate) pdata: *const ProcData, + + pub(crate) schedule: *const u8, + + pub(crate) stack: *mut u8, + + pub(crate) future: *mut u8, + + pub(crate) output: *mut u8, +} + +impl AlignProc { + #[inline] + pub(crate) fn from_layout(ptr: *const (), proc_layout: &ProcLayout) -> Self { + let p = ptr as *const u8; + + unsafe { + Self { + pdata: p as *const ProcData, + schedule: p.add( + Self::get_offset(proc_layout, "schedule") + ) as *const u8, + stack: p.add( + Self::get_offset(proc_layout, "stack") + ) as *mut u8, + future: p.add( + Self::get_offset(proc_layout, "future") + ) as *mut u8, + output: p.add( + Self::get_offset(proc_layout, "output") + ) as *mut u8, + } + } + } + + #[inline] + pub(crate) fn get_offset(proc_layout: &ProcLayout, offset_of: &str) -> usize { + if let Some(offset) = proc_layout.offset_table.get(offset_of).cloned() { + dbg!(offset_of); + dbg!(offset); + offset + } else { + dbg!("align_proc::not_found", offset_of); + 0x00_usize + } + } +} diff --git a/lightproc/src/layout_helpers.rs b/lightproc/src/layout_helpers.rs index 07d668b7..86dd9407 100644 --- a/lightproc/src/layout_helpers.rs +++ b/lightproc/src/layout_helpers.rs @@ -16,7 +16,10 @@ pub fn extend(layout: Layout, next: Layout) -> (Layout, usize) { .unwrap(); let new_size = offset .checked_add(next.size()) - .ok_or(Error::new(ErrorKind::Other, "New size can't be computed")) + .ok_or(Error::new( + ErrorKind::Other, + "New size can't be computed", + )) .unwrap(); let layout = Layout::from_size_align(new_size, new_align).unwrap(); diff --git a/lightproc/src/lib.rs b/lightproc/src/lib.rs index a58839c8..49fe353b 100644 --- a/lightproc/src/lib.rs +++ b/lightproc/src/lib.rs @@ -7,10 +7,13 @@ pub mod proc_vtable; pub mod raw_proc; pub mod stack; pub mod state; +pub mod align_proc; +pub mod utils; pub mod prelude { pub use crate::lightproc::*; pub use crate::stack::*; pub use crate::proc_layout::*; pub use crate::proc_handle::*; + pub use crate::align_proc::*; } diff --git a/lightproc/src/lightproc.rs b/lightproc/src/lightproc.rs index b6b0080b..7094f225 100644 --- a/lightproc/src/lightproc.rs +++ b/lightproc/src/lightproc.rs @@ -12,113 +12,36 @@ use crate::raw_proc::RawProc; use crate::proc_handle::ProcHandle; use crate::stack::ProcStack; use crate::proc_data::ProcData; +use crate::align_proc::AlignProc; +use std::sync::atomic::Ordering; + #[derive(Debug)] -pub struct LightProc { +pub struct LightProc { pub(crate) raw_proc: NonNull<()>, - - pub(crate) proc_layout: ProcLayout, - pub(crate) _private: marker, } -unsafe impl Send for LightProc {} -unsafe impl Sync for LightProc {} - -impl LightProc { - pub fn new() -> LightProc { - let proc_layout = ProcLayout::default(); - - unsafe { - LightProc { - raw_proc: NonNull::new(alloc::alloc(proc_layout.layout) as *mut ()).unwrap(), - proc_layout, - _private: marker, - } - } - } +unsafe impl Send for LightProc {} +unsafe impl Sync for LightProc {} - pub fn with_future(mut self, f: F) -> Self +impl LightProc { + pub fn build(future: F, schedule: S, stack: ProcStack) -> (LightProc, ProcHandle) where F: Future + Send + 'static, R: Send + 'static, + S: Fn(LightProc) + Send + Sync + 'static { - let fut_mem = Layout::new::(); - let (new_layout, offset_f) = extend(self.proc_layout.layout, fut_mem); - self.proc_layout.offset_table.insert("future", offset_f); - - self.reallocate(new_layout); - - let rawp = - RawProc::::from_ptr( - self.raw_proc.as_ptr(), &self.proc_layout); - - unsafe { - rawp.future.write(f); - } - - self - } - - pub fn with_schedule(mut self, s: S) -> Self - where - S: Fn(LightProc) + Send + Sync + 'static, - T: Send + 'static, - { - let sched_mem = Layout::new::(); - let (new_layout, offset_s) = extend(self.proc_layout.layout, sched_mem); - self.proc_layout.offset_table.insert("schedule", offset_s); - - self.reallocate(new_layout); - - let rawp = - RawProc::::from_ptr( - self.raw_proc.as_ptr(), &self.proc_layout); - - unsafe { - (rawp.schedule as *mut S).write(s); - } - - self - } - - pub fn with_stack(mut self, st: ProcStack) -> Self - { - let stack_mem = Layout::new::(); - let (new_layout, offset_st) = extend(self.proc_layout.layout, stack_mem); - self.proc_layout.offset_table.insert("stack", offset_st); - - self.reallocate(new_layout); - - let rawp = - RawProc::::from_ptr( - self.raw_proc.as_ptr(), &self.proc_layout); - - unsafe { - rawp.stack.write(st); - } - - self - } - - pub fn returning(mut self) -> (LightProc, ProcHandle) { - let raw_proc = self.raw_proc; - let proc = LightProc { - raw_proc, - proc_layout: self.proc_layout.clone(), - _private: marker, - }; - let handle = ProcHandle { - raw_proc, - _private: marker, - }; + let raw_proc = RawProc::::allocate( + future, schedule, stack + ); + let proc = LightProc { raw_proc }; + let handle = ProcHandle { raw_proc, _private: marker }; (proc, handle) } pub fn schedule(self) { let ptr = self.raw_proc.as_ptr(); let header = ptr as *const ProcData; - mem::forget(self); - unsafe { ((*header).vtable.schedule)(ptr); } @@ -143,33 +66,18 @@ impl LightProc { } } - pub fn stack(&self) -> &T { - let offset = ProcData::offset_tag::(); + pub fn stack(&self) -> &ProcStack { + let offset = ProcData::offset_stack(); let ptr = self.raw_proc.as_ptr(); unsafe { - let raw = (ptr as *mut u8).add(offset) as *const T; + let raw = (ptr as *mut u8).add(offset) as *const ProcStack; &*raw } } - - fn reallocate(&mut self, enlarged: Layout) { - unsafe { - let old = self.raw_proc.as_ptr() as *mut u8; - let bigger = alloc::realloc( - old, - self.proc_layout.layout, - enlarged.size(), - ); - ptr::copy(old, bigger, self.proc_layout.layout.size()); - self.raw_proc = NonNull::new(bigger as *mut ()).unwrap() - } - - self.proc_layout.layout = enlarged; - } } -impl Drop for LightProc { +impl Drop for LightProc { fn drop(&mut self) { let ptr = self.raw_proc.as_ptr(); let header = ptr as *const ProcData; diff --git a/lightproc/src/proc_data.rs b/lightproc/src/proc_data.rs index 66fb48ca..af510751 100644 --- a/lightproc/src/proc_data.rs +++ b/lightproc/src/proc_data.rs @@ -1,22 +1,38 @@ -use crate::proc_vtable::ProcVTable; +use std::alloc::Layout; use std::cell::Cell; +use std::fmt; use std::sync::atomic::{AtomicUsize, Ordering}; use std::task::Waker; -use std::fmt; -use crate::state::*; -use std::alloc::Layout; -use crate::layout_helpers::*; + use crossbeam_utils::Backoff; +use crate::state::*; +use crate::utils::{abort_on_panic}; +use crate::stack::ProcStack; +use crate::proc_vtable::ProcVTable; +use crate::layout_helpers::extend; + +/// The header of a task. +/// +/// This header is stored right at the beginning of every heap-allocated task. pub(crate) struct ProcData { + /// Current state of the task. + /// + /// Contains flags representing the current state and the reference count. pub(crate) state: AtomicUsize, + /// The task that is blocked on the `JoinHandle`. + /// + /// This waker needs to be woken once the task completes or is closed. pub(crate) awaiter: Cell>, + /// The virtual table. + /// + /// In addition to the actual waker virtual table, it also contains pointers to several other + /// methods necessary for bookkeeping the heap-allocated task. pub(crate) vtable: &'static ProcVTable, } - impl ProcData { /// Cancels the task. /// @@ -57,7 +73,10 @@ impl ProcData { #[inline] pub(crate) fn notify(&self) { if let Some(waker) = self.swap_awaiter(None) { - waker.wake(); + // We need a safeguard against panics because waking can panic. + abort_on_panic(|| { + waker.wake(); + }); } } @@ -68,7 +87,10 @@ impl ProcData { pub(crate) fn notify_unless(&self, current: &Waker) { if let Some(waker) = self.swap_awaiter(None) { if !waker.will_wake(current) { - waker.wake(); + // We need a safeguard against panics because waking can panic. + abort_on_panic(|| { + waker.wake(); + }); } } } @@ -113,10 +135,10 @@ impl ProcData { /// Returns the offset at which the tag of type `T` is stored. #[inline] - pub(crate) fn offset_tag() -> usize { - let layout_proc_data = Layout::new::(); - let layout_t = Layout::new::(); - let (_, offset_t) = extend(layout_proc_data, layout_t); + pub(crate) fn offset_stack() -> usize { + let layout_header = Layout::new::(); + let layout_t = Layout::new::(); + let (_, offset_t) = extend(layout_header, layout_t); offset_t } } diff --git a/lightproc/src/proc_handle.rs b/lightproc/src/proc_handle.rs index cec57a77..e87c455d 100644 --- a/lightproc/src/proc_handle.rs +++ b/lightproc/src/proc_handle.rs @@ -1,24 +1,36 @@ -use std::marker::PhantomData as marker; -use std::ptr::NonNull; -use std::pin::Pin; -use std::task::{Context, Poll}; +use std::fmt; use std::future::Future; +use std::marker::{PhantomData, Unpin}; +use std::pin::Pin; +use std::ptr::NonNull; use std::sync::atomic::Ordering; -use crate::proc_data::ProcData; +use std::task::{Context, Poll}; + use crate::state::*; -use std::fmt; +use crate::utils::abort_on_panic; +use crate::stack::ProcStack; +use crate::proc_data::ProcData; -pub struct ProcHandle { +/// A handle that awaits the result of a task. +/// +/// This type is a future that resolves to an `Option` where: +/// +/// * `None` indicates the task has panicked or was cancelled +/// * `Some(res)` indicates the task has completed with `res` +pub struct ProcHandle { + /// A raw task pointer. pub(crate) raw_proc: NonNull<()>, - pub(crate) _private: marker<(R, T)>, + + /// A marker capturing the generic type `R`. + pub(crate) _private: PhantomData, } -unsafe impl Send for ProcHandle {} -unsafe impl Sync for ProcHandle {} +unsafe impl Send for ProcHandle {} +unsafe impl Sync for ProcHandle {} -impl Unpin for ProcHandle {} +impl Unpin for ProcHandle {} -impl ProcHandle { +impl ProcHandle { /// Cancels the task. /// /// If the task has already completed, calling this method will have no effect. @@ -71,18 +83,19 @@ impl ProcHandle { } } - pub fn stack(&self) -> &T { - let offset = ProcData::offset_tag::(); + /// Returns a reference to the tag stored inside the task. + pub fn stack(&self) -> &ProcStack { + let offset = ProcData::offset_stack(); let ptr = self.raw_proc.as_ptr(); unsafe { - let raw = (ptr as *mut u8).add(offset) as *const T; + let raw = (ptr as *mut u8).add(offset) as *const ProcStack; &*raw } } } -impl Drop for ProcHandle { +impl Drop for ProcHandle { fn drop(&mut self) { let ptr = self.raw_proc.as_ptr(); let header = ptr as *const ProcData; @@ -91,7 +104,7 @@ impl Drop for ProcHandle { let mut output = None; unsafe { - // Optimistically assume the `JoinHandle` is being dropped just after creating the + // Optimistically assume the `ProcHandle` is being dropped just after creating the // task. This is a common case so if the handle is not used, the overhead of it is only // one compare-exchange operation. if let Err(mut state) = (*header).state.compare_exchange_weak( @@ -163,7 +176,7 @@ impl Drop for ProcHandle { } } -impl Future for ProcHandle { +impl Future for ProcHandle { type Output = Option; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { @@ -184,7 +197,11 @@ impl Future for ProcHandle { // If the task is not completed, register the current task. if state & COMPLETED == 0 { - (*header).swap_awaiter(Some(cx.waker().clone())); + // Replace the waker with one associated with the current task. We need a + // safeguard against panics because dropping the previous waker can panic. + abort_on_panic(|| { + (*header).swap_awaiter(Some(cx.waker().clone())); + }); // Reload the state after registering. It is possible that the task became // completed or closed just before registration so we need to check for that. @@ -229,13 +246,13 @@ impl Future for ProcHandle { } } -impl fmt::Debug for ProcHandle { +impl fmt::Debug for ProcHandle { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let ptr = self.raw_proc.as_ptr(); let header = ptr as *const ProcData; - f.debug_struct("JoinHandle") - .field("procdata", unsafe { &(*header) }) + f.debug_struct("ProcHandle") + .field("header", unsafe { &(*header) }) .finish() } } diff --git a/lightproc/src/raw_proc.rs b/lightproc/src/raw_proc.rs index ad9b7629..626aa6b1 100644 --- a/lightproc/src/raw_proc.rs +++ b/lightproc/src/raw_proc.rs @@ -1,25 +1,44 @@ -use crate::lightproc::LightProc; -use crate::proc_data::ProcData; -use crate::proc_layout::ProcLayout; +use std::alloc::{self, Layout}; +use std::cell::Cell; use std::future::Future; +use std::marker::PhantomData; +use std::mem::{self, ManuallyDrop}; +use std::pin::Pin; +use std::ptr::NonNull; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker}; +use crate::state::*; +use crate::utils::{abort_on_panic}; +use crate::stack::ProcStack; +use crate::layout_helpers::extend; +use crate::proc_layout::ProcLayout; +use rustc_hash::FxHashMap; +use crate::lightproc::LightProc; +use crate::proc_data::ProcData; +use crate::proc_vtable::ProcVTable; /// Raw pointers to the fields of a task. -pub struct RawProc { +pub(crate) struct RawProc { + /// The task header. pub(crate) pdata: *const ProcData, + /// The schedule function. pub(crate) schedule: *const S, - pub(crate) stack: *mut T, + /// The tag inside the task. + pub(crate) stack: *mut ProcStack, + /// The future. pub(crate) future: *mut F, + /// The output of the future. pub(crate) output: *mut R, } -impl Copy for RawProc {} +impl Copy for RawProc {} -impl Clone for RawProc { +impl Clone for RawProc { fn clone(&self) -> Self { Self { pdata: self.pdata, @@ -31,38 +50,548 @@ impl Clone for RawProc { } } -impl RawProc +impl RawProc + where + F: Future + Send + 'static, + R: Send + 'static, + S: Fn(LightProc) + Send + Sync + 'static { + /// Allocates a task with the given `future` and `schedule` function. + /// + /// It is assumed there are initially only the `Task` reference and the `JoinHandle`. + pub(crate) fn allocate(future: F, schedule: S, stack: ProcStack) -> NonNull<()> { + // Compute the layout of the task for allocation. Abort if the computation fails. + let task_layout = abort_on_panic(|| Self::task_layout()); + + unsafe { + // Allocate enough space for the entire task. + let raw_task = match NonNull::new(alloc::alloc(task_layout.layout) as *mut ()) { + None => std::process::abort(), + Some(p) => p, + }; + + let raw = Self::from_ptr(raw_task.as_ptr()); + + // Write the header as the first field of the task. + (raw.pdata as *mut ProcData).write(ProcData { + state: AtomicUsize::new(SCHEDULED | HANDLE | REFERENCE), + awaiter: Cell::new(None), + vtable: &ProcVTable { + raw_waker: RawWakerVTable::new( + Self::clone_waker, + Self::wake, + Self::wake_by_ref, + Self::decrement, + ), + schedule: Self::schedule, + drop_future: Self::drop_future, + get_output: Self::get_output, + decrement: Self::decrement, + destroy: Self::destroy, + run: Self::run, + }, + }); + + // Write the tag as the second field of the task. + (raw.stack as *mut ProcStack).write(stack); + + // Write the schedule function as the third field of the task. + (raw.schedule as *mut S).write(schedule); + + // Write the future as the fourth field of the task. + raw.future.write(future); + + raw_task + } + } + + /// Creates a `RawTask` from a raw task pointer. #[inline] - pub(crate) fn from_ptr(ptr: *const (), proc_layout: &ProcLayout) -> Self { + pub(crate) fn from_ptr(ptr: *const ()) -> Self { + let proc_layout = Self::task_layout(); let p = ptr as *const u8; unsafe { Self { pdata: p as *const ProcData, + stack: p.add( + proc_layout.offset_table.get("stack").cloned().unwrap() + ) as *mut ProcStack, schedule: p.add( - Self::get_offset(proc_layout, "schedule") + proc_layout.offset_table.get("schedule").cloned().unwrap() ) as *const S, - stack: p.add( - Self::get_offset(proc_layout, "stack") - ) as *mut T, future: p.add( - Self::get_offset(proc_layout, "future") + proc_layout.offset_table.get("future").cloned().unwrap() ) as *mut F, output: p.add( - Self::get_offset(proc_layout, "output") + proc_layout.offset_table.get("output").cloned().unwrap() ) as *mut R, } } } + /// Returns the memory layout for a task. + #[inline] + fn task_layout() -> ProcLayout { + // Compute the layouts for `ProcData`, `T`, `S`, `F`, and `R`. + let layout_header = Layout::new::(); + let layout_t = Layout::new::(); + let layout_s = Layout::new::(); + let layout_f = Layout::new::(); + let layout_r = Layout::new::(); + + // Compute the layout for `union { F, R }`. + let size_union = layout_f.size().max(layout_r.size()); + let align_union = layout_f.align().max(layout_r.align()); + let layout_union = unsafe { Layout::from_size_align_unchecked(size_union, align_union) }; + + // Compute the layout for `ProcData` followed by `T`, then `S`, then `union { F, R }`. + let layout = layout_header; + let (layout, offset_t) = extend(layout, layout_t); + let (layout, offset_s) = extend(layout, layout_s); + let (layout, offset_union) = extend(layout, layout_union); + let offset_f = offset_union; + let offset_r = offset_union; + + let mut offset_table + = FxHashMap::default(); + offset_table.insert("future", offset_f); + offset_table.insert("stack", offset_t); + offset_table.insert("output", offset_r); + offset_table.insert("schedule", offset_s); + + ProcLayout { + layout, + offset_table + } + } + + /// Wakes a waker. + unsafe fn wake(ptr: *const ()) { + let raw = Self::from_ptr(ptr); + + let mut state = (*raw.pdata).state.load(Ordering::Acquire); + + loop { + // If the task is completed or closed, it can't be woken. + if state & (COMPLETED | CLOSED) != 0 { + // Drop the waker. + Self::decrement(ptr); + break; + } + + // If the task is already scheduled, we just need to synchronize with the thread that + // will run the task by "publishing" our current view of the memory. + if state & SCHEDULED != 0 { + // Update the state without actually modifying it. + match (*raw.pdata).state.compare_exchange_weak( + state, + state, + Ordering::AcqRel, + Ordering::Acquire, + ) { + Ok(_) => { + // Drop the waker. + Self::decrement(ptr); + break; + } + Err(s) => state = s, + } + } else { + // Mark the task as scheduled. + match (*raw.pdata).state.compare_exchange_weak( + state, + state | SCHEDULED, + Ordering::AcqRel, + Ordering::Acquire, + ) { + Ok(_) => { + // If the task is not yet scheduled and isn't currently running, now is the + // time to schedule it. + if state & (SCHEDULED | RUNNING) == 0 { + // Schedule the task. + let task = LightProc { + raw_proc: NonNull::new_unchecked(ptr as *mut ()) + }; + (*raw.schedule)(task); + } else { + // Drop the waker. + Self::decrement(ptr); + } + + break; + } + Err(s) => state = s, + } + } + } + } + + /// Wakes a waker by reference. + unsafe fn wake_by_ref(ptr: *const ()) { + let raw = Self::from_ptr(ptr); + + let mut state = (*raw.pdata).state.load(Ordering::Acquire); + + loop { + // If the task is completed or closed, it can't be woken. + if state & (COMPLETED | CLOSED) != 0 { + break; + } + + // If the task is already scheduled, we just need to synchronize with the thread that + // will run the task by "publishing" our current view of the memory. + if state & SCHEDULED != 0 { + // Update the state without actually modifying it. + match (*raw.pdata).state.compare_exchange_weak( + state, + state, + Ordering::AcqRel, + Ordering::Acquire, + ) { + Ok(_) => break, + Err(s) => state = s, + } + } else { + // If the task is not scheduled nor running, we'll need to schedule after waking. + let new = if state & (SCHEDULED | RUNNING) == 0 { + (state | SCHEDULED) + REFERENCE + } else { + state | SCHEDULED + }; + + // Mark the task as scheduled. + match (*raw.pdata).state.compare_exchange_weak( + state, + new, + Ordering::AcqRel, + Ordering::Acquire, + ) { + Ok(_) => { + // If the task is not scheduled nor running, now is the time to schedule. + if state & (SCHEDULED | RUNNING) == 0 { + // If the reference count overflowed, abort. + if state > isize::max_value() as usize { + std::process::abort(); + } + + // Schedule the task. + let task = LightProc { + raw_proc: NonNull::new_unchecked(ptr as *mut ()) + }; + (*raw.schedule)(task); + } + + break; + } + Err(s) => state = s, + } + } + } + } + + /// Clones a waker. + unsafe fn clone_waker(ptr: *const ()) -> RawWaker { + let raw = Self::from_ptr(ptr); + let raw_waker = &(*raw.pdata).vtable.raw_waker; + + // Increment the reference count. With any kind of reference-counted data structure, + // relaxed ordering is fine when the reference is being cloned. + let state = (*raw.pdata).state.fetch_add(REFERENCE, Ordering::Relaxed); + + // If the reference count overflowed, abort. + if state > isize::max_value() as usize { + std::process::abort(); + } + + RawWaker::new(ptr, raw_waker) + } + + /// Drops a waker or a task. + /// + /// This function will decrement the reference count. If it drops down to zero and the + /// associated join handle has been dropped too, then the task gets destroyed. #[inline] - pub(crate) fn get_offset(proc_layout: &ProcLayout, offset_of: &str) -> usize { - if let Some(offset) = proc_layout.offset_table.get(offset_of).cloned() { - dbg!(offset); - offset - } else { - 0x00_usize + unsafe fn decrement(ptr: *const ()) { + let raw = Self::from_ptr(ptr); + + // Decrement the reference count. + let k = (*raw.pdata).state.load(Ordering::SeqCst); + + let new = (*raw.pdata) + .state + .fetch_sub(REFERENCE, Ordering::AcqRel) - REFERENCE; + + // If this was the last reference to the task and the `JoinHandle` has been dropped as + // well, then destroy the task. + if new & !(REFERENCE - 1) == 0 && new & HANDLE == 0 { + Self::destroy(ptr); + } + } + + /// Schedules a task for running. + /// + /// This function doesn't modify the state of the task. It only passes the task reference to + /// its schedule function. + unsafe fn schedule(ptr: *const ()) { + let raw = Self::from_ptr(ptr); + + (*raw.schedule)(LightProc { + raw_proc: NonNull::new_unchecked(ptr as *mut ()) + }); + } + + /// Drops the future inside a task. + #[inline] + unsafe fn drop_future(ptr: *const ()) { + let raw = Self::from_ptr(ptr); + + // We need a safeguard against panics because the destructor can panic. + abort_on_panic(|| { + raw.future.drop_in_place(); + }) + } + + /// Returns a pointer to the output inside a task. + unsafe fn get_output(ptr: *const ()) -> *const () { + let raw = Self::from_ptr(ptr); + raw.output as *const () + } + + /// Cleans up task's resources and deallocates it. + /// + /// If the task has not been closed, then its future or the output will be dropped. The + /// schedule function and the tag get dropped too. + #[inline] + unsafe fn destroy(ptr: *const ()) { + let raw = Self::from_ptr(ptr); + let task_layout = Self::task_layout(); + + // We need a safeguard against panics because destructors can panic. + abort_on_panic(|| { + // Drop the schedule function. + (raw.schedule as *mut S).drop_in_place(); + + // Drop the tag. + (raw.stack as *mut ProcStack).drop_in_place(); + }); + + // Finally, deallocate the memory reserved by the task. + alloc::dealloc(ptr as *mut u8, task_layout.layout); + } + + /// Runs a task. + /// + /// If polling its future panics, the task will be closed and the panic propagated into the + /// caller. + unsafe fn run(ptr: *const ()) { + let raw = Self::from_ptr(ptr); + + // Create a context from the raw task pointer and the vtable inside the its header. + let waker = ManuallyDrop::new(Waker::from_raw(RawWaker::new( + ptr, + &(*raw.pdata).vtable.raw_waker, + ))); + let cx = &mut Context::from_waker(&waker); + + let mut state = (*raw.pdata).state.load(Ordering::Acquire); + + // Update the task's state before polling its future. + loop { + // If the task has been closed, drop the task reference and return. + if state & CLOSED != 0 { + // Notify the awaiter that the task has been closed. + if state & AWAITER != 0 { + (*raw.pdata).notify(); + } + + // Drop the future. + Self::drop_future(ptr); + + // Drop the task reference. + Self::decrement(ptr); + + return; + } + + // Mark the task as unscheduled and running. + match (*raw.pdata).state.compare_exchange_weak( + state, + (state & !SCHEDULED) | RUNNING, + Ordering::AcqRel, + Ordering::Acquire, + ) { + Ok(_) => { + // Update the state because we're continuing with polling the future. + state = (state & !SCHEDULED) | RUNNING; + break; + } + Err(s) => state = s, + } + } + + // Poll the inner future, but surround it with a guard that closes the task in case polling + // panics. + let guard = Guard(raw); + let poll = ::poll(Pin::new_unchecked(&mut *raw.future), cx); + mem::forget(guard); + + match poll { + Poll::Ready(out) => { + // Replace the future with its output. + Self::drop_future(ptr); + raw.output.write(out); + + // A place where the output will be stored in case it needs to be dropped. + let mut output = None; + + // The task is now completed. + loop { + // If the handle is dropped, we'll need to close it and drop the output. + let new = if state & HANDLE == 0 { + (state & !RUNNING & !SCHEDULED) | COMPLETED | CLOSED + } else { + (state & !RUNNING & !SCHEDULED) | COMPLETED + }; + + // Mark the task as not running and completed. + match (*raw.pdata).state.compare_exchange_weak( + state, + new, + Ordering::AcqRel, + Ordering::Acquire, + ) { + Ok(_) => { + // If the handle is dropped or if the task was closed while running, + // now it's time to drop the output. + if state & HANDLE == 0 || state & CLOSED != 0 { + // Read the output. + output = Some(raw.output.read()); + } + + // Notify the awaiter that the task has been completed. + if state & AWAITER != 0 { + (*raw.pdata).notify(); + } + + // Drop the task reference. + Self::decrement(ptr); + break; + } + Err(s) => state = s, + } + } + + // Drop the output if it was taken out of the task. + drop(output); + } + Poll::Pending => { + // The task is still not completed. + loop { + // If the task was closed while running, we'll need to unschedule in case it + // was woken and then clean up its resources. + let new = if state & CLOSED != 0 { + state & !RUNNING & !SCHEDULED + } else { + state & !RUNNING + }; + + // Mark the task as not running. + match (*raw.pdata).state.compare_exchange_weak( + state, + new, + Ordering::AcqRel, + Ordering::Acquire, + ) { + Ok(state) => { + // If the task was closed while running, we need to drop its future. + // If the task was woken while running, we need to schedule it. + // Otherwise, we just drop the task reference. + if state & CLOSED != 0 { + // The thread that closed the task didn't drop the future because + // it was running so now it's our responsibility to do so. + Self::drop_future(ptr); + + // Drop the task reference. + Self::decrement(ptr); + } else if state & SCHEDULED != 0 { + // The thread that has woken the task didn't reschedule it because + // it was running so now it's our responsibility to do so. + Self::schedule(ptr); + } else { + // Drop the task reference. + Self::decrement(ptr); + } + break; + } + Err(s) => state = s, + } + } + } + } + + /// A guard that closes the task if polling its future panics. + struct Guard(RawProc) + where + F: Future + Send + 'static, + R: Send + 'static, + S: Fn(LightProc) + Send + Sync + 'static; + + impl Drop for Guard + where + F: Future + Send + 'static, + R: Send + 'static, + S: Fn(LightProc) + Send + Sync + 'static + { + fn drop(&mut self) { + let raw = self.0; + let ptr = raw.pdata as *const (); + + unsafe { + let mut state = (*raw.pdata).state.load(Ordering::Acquire); + + loop { + // If the task was closed while running, then unschedule it, drop its + // future, and drop the task reference. + if state & CLOSED != 0 { + // We still need to unschedule the task because it is possible it was + // woken while running. + (*raw.pdata).state.fetch_and(!SCHEDULED, Ordering::AcqRel); + + // The thread that closed the task didn't drop the future because it + // was running so now it's our responsibility to do so. + RawProc::::drop_future(ptr); + + // Drop the task reference. + RawProc::::decrement(ptr); + break; + } + + // Mark the task as not running, not scheduled, and closed. + match (*raw.pdata).state.compare_exchange_weak( + state, + (state & !RUNNING & !SCHEDULED) | CLOSED, + Ordering::AcqRel, + Ordering::Acquire, + ) { + Ok(state) => { + // Drop the future because the task is now closed. + RawProc::::drop_future(ptr); + + // Notify the awaiter that the task has been closed. + if state & AWAITER != 0 { + (*raw.pdata).notify(); + } + + // Drop the task reference. + RawProc::::decrement(ptr); + break; + } + Err(s) => state = s, + } + } + } + } } } } diff --git a/lightproc/src/stack.rs b/lightproc/src/stack.rs index 9b263a42..eb175888 100644 --- a/lightproc/src/stack.rs +++ b/lightproc/src/stack.rs @@ -3,10 +3,8 @@ use std::sync::Arc; use std::fmt::{Formatter, Error}; use std::fmt; -#[derive(Default)] +#[derive(Clone, Default)] pub struct ProcStack { - pub pid: AtomicUsize, - pub after_start: Option>, pub after_complete: Option>, @@ -15,7 +13,6 @@ pub struct ProcStack { impl fmt::Debug for ProcStack { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { f.debug_struct("ProcStack") - .field("pid", &self.pid) .finish() } } diff --git a/lightproc/src/state.rs b/lightproc/src/state.rs index b1899efc..86050aca 100644 --- a/lightproc/src/state.rs +++ b/lightproc/src/state.rs @@ -64,3 +64,7 @@ pub(crate) const LOCKED: usize = 1 << 6; /// tracked separately by the `HANDLE` flag. pub(crate) const REFERENCE: usize = 1 << 7; +/// Finalized build of proc. +/// +/// Determine how drop behavior will change. +pub(crate) const BUILD_FINALIZED: usize = 1 << 8; diff --git a/lightproc/src/utils.rs b/lightproc/src/utils.rs new file mode 100644 index 00000000..d6d87c0c --- /dev/null +++ b/lightproc/src/utils.rs @@ -0,0 +1,21 @@ +use std::alloc::Layout; +use std::mem; + +/// Calls a function and aborts if it panics. +/// +/// This is useful in unsafe code where we can't recover from panics. +#[inline] +pub(crate) fn abort_on_panic(f: impl FnOnce() -> T) -> T { + struct Bomb; + + impl Drop for Bomb { + fn drop(&mut self) { + std::process::abort(); + } + } + + let bomb = Bomb; + let t = f(); + mem::forget(bomb); + t +} From e2ec8e931b7e713cf2b53b0d0cf0b8e58e84d203 Mon Sep 17 00:00:00 2001 From: Mahmut Bulut Date: Sun, 20 Oct 2019 22:11:04 +0200 Subject: [PATCH 11/38] refactor --- lightproc/examples/proc_run.rs | 24 +----- lightproc/examples/task_build.rs | 45 ------------ lightproc/src/align_proc.rs | 57 --------------- lightproc/src/lib.rs | 4 - lightproc/src/lightproc.rs | 75 +++++++++++-------- lightproc/src/proc_data.rs | 28 +++---- lightproc/src/proc_handle.rs | 59 +++++++-------- lightproc/src/proc_layout.rs | 30 ++++---- lightproc/src/proc_vtable.rs | 3 +- lightproc/src/raw_proc.rs | 122 +++++++++++-------------------- lightproc/src/stack.rs | 7 +- lightproc/src/state.rs | 5 -- lightproc/src/utils.rs | 21 ------ 13 files changed, 149 insertions(+), 331 deletions(-) delete mode 100644 lightproc/examples/task_build.rs delete mode 100644 lightproc/src/align_proc.rs delete mode 100644 lightproc/src/utils.rs diff --git a/lightproc/examples/proc_run.rs b/lightproc/examples/proc_run.rs index f51249ab..ad1e2382 100644 --- a/lightproc/examples/proc_run.rs +++ b/lightproc/examples/proc_run.rs @@ -6,35 +6,22 @@ use std::thread; use crossbeam::channel; use futures::executor; -use std::sync::atomic::AtomicUsize; -use lightproc::proc_handle::ProcHandle; -use lightproc::stack::ProcStack; -use lightproc::lightproc::LightProc; -use std::time::Duration; +use lightproc::prelude::*; -/// Spawns a future on a new dedicated thread. -/// -/// The returned handle can be used to await the output of the future. fn spawn_on_thread(fut: F) -> ProcHandle where F: Future + Send + 'static, R: Send + 'static, { - // Create a channel that holds the task when it is scheduled for running. let (sender, receiver) = channel::unbounded(); let sender = Arc::new(sender); let s = Arc::downgrade(&sender); - // Wrap the future into one that disconnects the channel on completion. let future = async move { - // When the inner future completes, the sender gets dropped and disconnects the channel. - println!("exec"); - let _sender = sender; - println!("exec1"); + let _ = sender; fut.await }; - // Create a task that is scheduled by sending itself into the channel. let schedule = move |t| s.upgrade().unwrap().send(t).unwrap(); let (proc, handle) = LightProc::build( future, @@ -42,16 +29,11 @@ fn spawn_on_thread(fut: F) -> ProcHandle ProcStack::default() ); - // Schedule the task by sending it into the channel. proc.schedule(); - // Spawn a thread running the task to completion. thread::spawn(move || { - // Keep taking the task from the channel and running it until completion. for proc in receiver { - println!("ad"); proc.run(); - println!("ad2"); } }); @@ -62,6 +44,4 @@ fn main() { executor::block_on(spawn_on_thread(async { println!("Hello, world!"); })); - - thread::sleep(Duration::new(5, 0)); } diff --git a/lightproc/examples/task_build.rs b/lightproc/examples/task_build.rs deleted file mode 100644 index 100d80a8..00000000 --- a/lightproc/examples/task_build.rs +++ /dev/null @@ -1,45 +0,0 @@ -use lightproc::prelude::*; -use lightproc::stack::ProcStack; -use lightproc::proc_handle::ProcHandle; - -fn main() { - let lp = LightProc::<()>::new().with_future(async move { - println!("test"); - }); - dbg!(lp); - - let lp2 = LightProc::<()>::new() - .with_future(async move { - println!("future"); - }) - .with_schedule(|t| { - println!("scheduler"); - }); - dbg!(lp2); - - let lp3 = LightProc::<()>::new() - .with_schedule(|t| { - println!("scheduler"); - }); - dbg!(lp3); - - let lp4 = LightProc::::new() - .with_future(async move { - println!("future"); - }) - .with_schedule(|t| { - println!("scheduler"); - }) - .with_stack(ProcStack::default()); - dbg!(lp4); - - let (proc, handle) = LightProc::::new() - .with_future(async move { - println!("future"); - }) - .with_schedule(|t| { - println!("scheduler"); - }) - .with_stack(ProcStack::default()) - .returning::(); -} diff --git a/lightproc/src/align_proc.rs b/lightproc/src/align_proc.rs deleted file mode 100644 index 212257d8..00000000 --- a/lightproc/src/align_proc.rs +++ /dev/null @@ -1,57 +0,0 @@ -use crate::proc_layout::ProcLayout; -use std::ptr::NonNull; -use crate::proc_data::ProcData; -use std::sync::atomic::AtomicUsize; -use crate::state::*; -use std::cell::Cell; -use crate::proc_vtable::ProcVTable; -use std::task::RawWakerVTable; - -pub struct AlignProc { - pub(crate) pdata: *const ProcData, - - pub(crate) schedule: *const u8, - - pub(crate) stack: *mut u8, - - pub(crate) future: *mut u8, - - pub(crate) output: *mut u8, -} - -impl AlignProc { - #[inline] - pub(crate) fn from_layout(ptr: *const (), proc_layout: &ProcLayout) -> Self { - let p = ptr as *const u8; - - unsafe { - Self { - pdata: p as *const ProcData, - schedule: p.add( - Self::get_offset(proc_layout, "schedule") - ) as *const u8, - stack: p.add( - Self::get_offset(proc_layout, "stack") - ) as *mut u8, - future: p.add( - Self::get_offset(proc_layout, "future") - ) as *mut u8, - output: p.add( - Self::get_offset(proc_layout, "output") - ) as *mut u8, - } - } - } - - #[inline] - pub(crate) fn get_offset(proc_layout: &ProcLayout, offset_of: &str) -> usize { - if let Some(offset) = proc_layout.offset_table.get(offset_of).cloned() { - dbg!(offset_of); - dbg!(offset); - offset - } else { - dbg!("align_proc::not_found", offset_of); - 0x00_usize - } - } -} diff --git a/lightproc/src/lib.rs b/lightproc/src/lib.rs index 49fe353b..47bd274a 100644 --- a/lightproc/src/lib.rs +++ b/lightproc/src/lib.rs @@ -7,13 +7,9 @@ pub mod proc_vtable; pub mod raw_proc; pub mod stack; pub mod state; -pub mod align_proc; -pub mod utils; pub mod prelude { pub use crate::lightproc::*; pub use crate::stack::*; - pub use crate::proc_layout::*; pub use crate::proc_handle::*; - pub use crate::align_proc::*; } diff --git a/lightproc/src/lightproc.rs b/lightproc/src/lightproc.rs index 7094f225..14f080d3 100644 --- a/lightproc/src/lightproc.rs +++ b/lightproc/src/lightproc.rs @@ -1,23 +1,17 @@ -use std::{alloc, mem, ptr}; +use std::fmt; use std::future::Future; -use std::marker::PhantomData as marker; +use std::marker::PhantomData; +use std::mem; use std::ptr::NonNull; -use crate::proc_layout::ProcLayout; - -use crate::layout_helpers::extend; - -use std::alloc::Layout; +use crate::proc_data::ProcData; use crate::raw_proc::RawProc; use crate::proc_handle::ProcHandle; -use crate::stack::ProcStack; -use crate::proc_data::ProcData; -use crate::align_proc::AlignProc; -use std::sync::atomic::Ordering; +use crate::stack::*; -#[derive(Debug)] pub struct LightProc { + /// A pointer to the heap-allocated task. pub(crate) raw_proc: NonNull<()>, } @@ -26,43 +20,48 @@ unsafe impl Sync for LightProc {} impl LightProc { pub fn build(future: F, schedule: S, stack: ProcStack) -> (LightProc, ProcHandle) - where - F: Future + Send + 'static, - R: Send + 'static, - S: Fn(LightProc) + Send + Sync + 'static + where + F: Future + Send + 'static, + R: Send + 'static, + S: Fn(LightProc) + Send + Sync + 'static { - let raw_proc = RawProc::::allocate( - future, schedule, stack - ); - let proc = LightProc { raw_proc }; - let handle = ProcHandle { raw_proc, _private: marker }; - (proc, handle) + let raw_task = RawProc::::allocate(stack, future, schedule); + let task = LightProc { + raw_proc: raw_task + }; + let handle = ProcHandle { + raw_proc: raw_task, + _marker: PhantomData, + }; + (task, handle) } pub fn schedule(self) { let ptr = self.raw_proc.as_ptr(); - let header = ptr as *const ProcData; + let pdata = ptr as *const ProcData; + mem::forget(self); + unsafe { - ((*header).vtable.schedule)(ptr); + ((*pdata).vtable.schedule)(ptr); } } pub fn run(self) { let ptr = self.raw_proc.as_ptr(); - let header = ptr as *const ProcData; + let pdata = ptr as *const ProcData; mem::forget(self); unsafe { - ((*header).vtable.run)(ptr); + ((*pdata).vtable.run)(ptr); } } pub fn cancel(&self) { let ptr = self.raw_proc.as_ptr(); - let header = ptr as *const ProcData; + let pdata = ptr as *const ProcData; unsafe { - (*header).cancel(); + (*pdata).cancel(); } } @@ -80,17 +79,29 @@ impl LightProc { impl Drop for LightProc { fn drop(&mut self) { let ptr = self.raw_proc.as_ptr(); - let header = ptr as *const ProcData; + let pdata = ptr as *const ProcData; unsafe { // Cancel the task. - (*header).cancel(); + (*pdata).cancel(); // Drop the future. - ((*header).vtable.drop_future)(ptr); + ((*pdata).vtable.drop_future)(ptr); // Drop the task reference. - ((*header).vtable.decrement)(ptr); + ((*pdata).vtable.decrement)(ptr); } } } + +impl fmt::Debug for LightProc { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let ptr = self.raw_proc.as_ptr(); + let pdata = ptr as *const ProcData; + + f.debug_struct("Task") + .field("pdata", unsafe { &(*pdata) }) + .field("stack", self.stack()) + .finish() + } +} diff --git a/lightproc/src/proc_data.rs b/lightproc/src/proc_data.rs index af510751..35cde993 100644 --- a/lightproc/src/proc_data.rs +++ b/lightproc/src/proc_data.rs @@ -7,14 +7,13 @@ use std::task::Waker; use crossbeam_utils::Backoff; use crate::state::*; -use crate::utils::{abort_on_panic}; -use crate::stack::ProcStack; -use crate::proc_vtable::ProcVTable; +use crate::stack::*; +use crate::proc_vtable::TaskVTable; use crate::layout_helpers::extend; -/// The header of a task. +/// The pdata of a task. /// -/// This header is stored right at the beginning of every heap-allocated task. +/// This pdata is stored right at the beginning of every heap-allocated task. pub(crate) struct ProcData { /// Current state of the task. /// @@ -30,7 +29,7 @@ pub(crate) struct ProcData { /// /// In addition to the actual waker virtual table, it also contains pointers to several other /// methods necessary for bookkeeping the heap-allocated task. - pub(crate) vtable: &'static ProcVTable, + pub(crate) vtable: &'static TaskVTable, } impl ProcData { @@ -69,28 +68,24 @@ impl ProcData { /// Notifies the task blocked on the task. /// - /// If there is a registered waker, it will be removed from the header and woken. + /// If there is a registered waker, it will be removed from the pdata and woken. #[inline] pub(crate) fn notify(&self) { if let Some(waker) = self.swap_awaiter(None) { // We need a safeguard against panics because waking can panic. - abort_on_panic(|| { - waker.wake(); - }); + waker.wake(); } } /// Notifies the task blocked on the task unless its waker matches `current`. /// - /// If there is a registered waker, it will be removed from the header. + /// If there is a registered waker, it will be removed from the pdata. #[inline] pub(crate) fn notify_unless(&self, current: &Waker) { if let Some(waker) = self.swap_awaiter(None) { if !waker.will_wake(current) { // We need a safeguard against panics because waking can panic. - abort_on_panic(|| { - waker.wake(); - }); + waker.wake(); } } } @@ -133,12 +128,11 @@ impl ProcData { old } - /// Returns the offset at which the tag of type `T` is stored. #[inline] pub(crate) fn offset_stack() -> usize { - let layout_header = Layout::new::(); + let layout_pdata = Layout::new::(); let layout_t = Layout::new::(); - let (_, offset_t) = extend(layout_header, layout_t); + let (_, offset_t) = extend(layout_pdata, layout_t); offset_t } } diff --git a/lightproc/src/proc_handle.rs b/lightproc/src/proc_handle.rs index e87c455d..c983ae8d 100644 --- a/lightproc/src/proc_handle.rs +++ b/lightproc/src/proc_handle.rs @@ -7,8 +7,7 @@ use std::sync::atomic::Ordering; use std::task::{Context, Poll}; use crate::state::*; -use crate::utils::abort_on_panic; -use crate::stack::ProcStack; +use crate::stack::*; use crate::proc_data::ProcData; /// A handle that awaits the result of a task. @@ -22,7 +21,7 @@ pub struct ProcHandle { pub(crate) raw_proc: NonNull<()>, /// A marker capturing the generic type `R`. - pub(crate) _private: PhantomData, + pub(crate) _marker: PhantomData, } unsafe impl Send for ProcHandle {} @@ -38,10 +37,10 @@ impl ProcHandle { /// When a task is cancelled, its future cannot be polled again and will be dropped instead. pub fn cancel(&self) { let ptr = self.raw_proc.as_ptr(); - let header = ptr as *const ProcData; + let pdata = ptr as *const ProcData; unsafe { - let mut state = (*header).state.load(Ordering::Acquire); + let mut state = (*pdata).state.load(Ordering::Acquire); loop { // If the task has been completed or closed, it can't be cancelled. @@ -57,7 +56,7 @@ impl ProcHandle { }; // Mark the task as closed. - match (*header).state.compare_exchange_weak( + match (*pdata).state.compare_exchange_weak( state, new, Ordering::AcqRel, @@ -67,12 +66,12 @@ impl ProcHandle { // If the task is not scheduled nor running, schedule it so that its future // gets dropped by the executor. if state & (SCHEDULED | RUNNING) == 0 { - ((*header).vtable.schedule)(ptr); + ((*pdata).vtable.schedule)(ptr); } // Notify the awaiter that the task has been closed. if state & AWAITER != 0 { - (*header).notify(); + (*pdata).notify(); } break; @@ -83,7 +82,7 @@ impl ProcHandle { } } - /// Returns a reference to the tag stored inside the task. + /// Returns a reference to the stack stored inside the task. pub fn stack(&self) -> &ProcStack { let offset = ProcData::offset_stack(); let ptr = self.raw_proc.as_ptr(); @@ -98,16 +97,16 @@ impl ProcHandle { impl Drop for ProcHandle { fn drop(&mut self) { let ptr = self.raw_proc.as_ptr(); - let header = ptr as *const ProcData; + let pdata = ptr as *const ProcData; // A place where the output will be stored in case it needs to be dropped. let mut output = None; unsafe { - // Optimistically assume the `ProcHandle` is being dropped just after creating the + // Optimistically assume the `JoinHandle` is being dropped just after creating the // task. This is a common case so if the handle is not used, the overhead of it is only // one compare-exchange operation. - if let Err(mut state) = (*header).state.compare_exchange_weak( + if let Err(mut state) = (*pdata).state.compare_exchange_weak( SCHEDULED | HANDLE | REFERENCE, SCHEDULED | REFERENCE, Ordering::AcqRel, @@ -118,7 +117,7 @@ impl Drop for ProcHandle { // must be dropped. if state & COMPLETED != 0 && state & CLOSED == 0 { // Mark the task as closed in order to grab its output. - match (*header).state.compare_exchange_weak( + match (*pdata).state.compare_exchange_weak( state, state | CLOSED, Ordering::AcqRel, @@ -127,7 +126,7 @@ impl Drop for ProcHandle { Ok(_) => { // Read the output. output = - Some((((*header).vtable.get_output)(ptr) as *mut R).read()); + Some((((*pdata).vtable.get_output)(ptr) as *mut R).read()); // Update the state variable because we're continuing the loop. state |= CLOSED; @@ -145,7 +144,7 @@ impl Drop for ProcHandle { }; // Unset the handle flag. - match (*header).state.compare_exchange_weak( + match (*pdata).state.compare_exchange_weak( state, new, Ordering::AcqRel, @@ -156,9 +155,9 @@ impl Drop for ProcHandle { // schedule dropping its future or destroy it. if state & !(REFERENCE - 1) == 0 { if state & CLOSED == 0 { - ((*header).vtable.schedule)(ptr); + ((*pdata).vtable.schedule)(ptr); } else { - ((*header).vtable.destroy)(ptr); + ((*pdata).vtable.destroy)(ptr); } } @@ -181,17 +180,17 @@ impl Future for ProcHandle { fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let ptr = self.raw_proc.as_ptr(); - let header = ptr as *const ProcData; + let pdata = ptr as *const ProcData; unsafe { - let mut state = (*header).state.load(Ordering::Acquire); + let mut state = (*pdata).state.load(Ordering::Acquire); loop { // If the task has been closed, notify the awaiter and return `None`. if state & CLOSED != 0 { // Even though the awaiter is most likely the current task, it could also be // another task. - (*header).notify_unless(cx.waker()); + (*pdata).notify_unless(cx.waker()); return Poll::Ready(None); } @@ -199,19 +198,17 @@ impl Future for ProcHandle { if state & COMPLETED == 0 { // Replace the waker with one associated with the current task. We need a // safeguard against panics because dropping the previous waker can panic. - abort_on_panic(|| { - (*header).swap_awaiter(Some(cx.waker().clone())); - }); + (*pdata).swap_awaiter(Some(cx.waker().clone())); // Reload the state after registering. It is possible that the task became // completed or closed just before registration so we need to check for that. - state = (*header).state.load(Ordering::Acquire); + state = (*pdata).state.load(Ordering::Acquire); // If the task has been closed, notify the awaiter and return `None`. if state & CLOSED != 0 { // Even though the awaiter is most likely the current task, it could also // be another task. - (*header).notify_unless(cx.waker()); + (*pdata).notify_unless(cx.waker()); return Poll::Ready(None); } @@ -222,7 +219,7 @@ impl Future for ProcHandle { } // Since the task is now completed, mark it as closed in order to grab its output. - match (*header).state.compare_exchange( + match (*pdata).state.compare_exchange( state, state | CLOSED, Ordering::AcqRel, @@ -232,11 +229,11 @@ impl Future for ProcHandle { // Notify the awaiter. Even though the awaiter is most likely the current // task, it could also be another task. if state & AWAITER != 0 { - (*header).notify_unless(cx.waker()); + (*pdata).notify_unless(cx.waker()); } // Take the output from the task. - let output = ((*header).vtable.get_output)(ptr) as *mut R; + let output = ((*pdata).vtable.get_output)(ptr) as *mut R; return Poll::Ready(Some(output.read())); } Err(s) => state = s, @@ -249,10 +246,10 @@ impl Future for ProcHandle { impl fmt::Debug for ProcHandle { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let ptr = self.raw_proc.as_ptr(); - let header = ptr as *const ProcData; + let pdata = ptr as *const ProcData; - f.debug_struct("ProcHandle") - .field("header", unsafe { &(*header) }) + f.debug_struct("JoinHandle") + .field("pdata", unsafe { &(*pdata) }) .finish() } } diff --git a/lightproc/src/proc_layout.rs b/lightproc/src/proc_layout.rs index c690c2ce..d51b36d0 100644 --- a/lightproc/src/proc_layout.rs +++ b/lightproc/src/proc_layout.rs @@ -1,19 +1,19 @@ -use rustc_hash::FxHashMap; use std::alloc::Layout; -use crate::proc_data::ProcData; -use crate::stack::ProcStack; -#[derive(Clone, Debug)] -pub struct ProcLayout { - pub layout: Layout, - pub offset_table: FxHashMap<&'static str, usize>, -} +#[derive(Clone, Copy)] +pub(crate) struct TaskLayout { + /// Memory layout of the whole task. + pub(crate) layout: Layout, + + /// Offset into the task at which the stack is stored. + pub(crate) offset_t: usize, + + /// Offset into the task at which the schedule function is stored. + pub(crate) offset_s: usize, + + /// Offset into the task at which the future is stored. + pub(crate) offset_f: usize, -impl Default for ProcLayout { - fn default() -> Self { - ProcLayout { - layout: Layout::new::(), - offset_table: FxHashMap::default(), - } - } + /// Offset into the task at which the output is stored. + pub(crate) offset_r: usize, } diff --git a/lightproc/src/proc_vtable.rs b/lightproc/src/proc_vtable.rs index 0e818a1e..9ce3d44c 100644 --- a/lightproc/src/proc_vtable.rs +++ b/lightproc/src/proc_vtable.rs @@ -1,6 +1,7 @@ use std::task::RawWakerVTable; -pub(crate) struct ProcVTable { +/// The vtable for a task. +pub(crate) struct TaskVTable { /// The raw waker vtable. pub(crate) raw_waker: RawWakerVTable, diff --git a/lightproc/src/raw_proc.rs b/lightproc/src/raw_proc.rs index 626aa6b1..df0e8507 100644 --- a/lightproc/src/raw_proc.rs +++ b/lightproc/src/raw_proc.rs @@ -1,7 +1,6 @@ use std::alloc::{self, Layout}; use std::cell::Cell; use std::future::Future; -use std::marker::PhantomData; use std::mem::{self, ManuallyDrop}; use std::pin::Pin; use std::ptr::NonNull; @@ -9,30 +8,19 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker}; use crate::state::*; -use crate::utils::{abort_on_panic}; -use crate::stack::ProcStack; -use crate::layout_helpers::extend; -use crate::proc_layout::ProcLayout; -use rustc_hash::FxHashMap; -use crate::lightproc::LightProc; +use crate::stack::*; +use crate::proc_vtable::TaskVTable; +use crate::proc_layout::TaskLayout; use crate::proc_data::ProcData; -use crate::proc_vtable::ProcVTable; +use crate::lightproc::LightProc; +use crate::layout_helpers::extend; /// Raw pointers to the fields of a task. pub(crate) struct RawProc { - /// The task header. pub(crate) pdata: *const ProcData, - - /// The schedule function. pub(crate) schedule: *const S, - - /// The tag inside the task. pub(crate) stack: *mut ProcStack, - - /// The future. pub(crate) future: *mut F, - - /// The output of the future. pub(crate) output: *mut R, } @@ -51,17 +39,17 @@ impl Clone for RawProc { } impl RawProc - where - F: Future + Send + 'static, - R: Send + 'static, - S: Fn(LightProc) + Send + Sync + 'static +where + F: Future + Send + 'static, + R: Send + 'static, + S: Fn(LightProc) + Send + Sync + 'static { /// Allocates a task with the given `future` and `schedule` function. /// /// It is assumed there are initially only the `Task` reference and the `JoinHandle`. - pub(crate) fn allocate(future: F, schedule: S, stack: ProcStack) -> NonNull<()> { + pub(crate) fn allocate(stack: ProcStack, future: F, schedule: S) -> NonNull<()> { // Compute the layout of the task for allocation. Abort if the computation fails. - let task_layout = abort_on_panic(|| Self::task_layout()); + let task_layout = Self::task_layout(); unsafe { // Allocate enough space for the entire task. @@ -72,11 +60,11 @@ impl RawProc let raw = Self::from_ptr(raw_task.as_ptr()); - // Write the header as the first field of the task. + // Write the pdata as the first field of the task. (raw.pdata as *mut ProcData).write(ProcData { state: AtomicUsize::new(SCHEDULED | HANDLE | REFERENCE), awaiter: Cell::new(None), - vtable: &ProcVTable { + vtable: &TaskVTable { raw_waker: RawWakerVTable::new( Self::clone_waker, Self::wake, @@ -92,7 +80,7 @@ impl RawProc }, }); - // Write the tag as the second field of the task. + // Write the stack as the second field of the task. (raw.stack as *mut ProcStack).write(stack); // Write the schedule function as the third field of the task. @@ -108,61 +96,46 @@ impl RawProc /// Creates a `RawTask` from a raw task pointer. #[inline] pub(crate) fn from_ptr(ptr: *const ()) -> Self { - let proc_layout = Self::task_layout(); + let task_layout = Self::task_layout(); let p = ptr as *const u8; unsafe { Self { pdata: p as *const ProcData, - stack: p.add( - proc_layout.offset_table.get("stack").cloned().unwrap() - ) as *mut ProcStack, - schedule: p.add( - proc_layout.offset_table.get("schedule").cloned().unwrap() - ) as *const S, - future: p.add( - proc_layout.offset_table.get("future").cloned().unwrap() - ) as *mut F, - output: p.add( - proc_layout.offset_table.get("output").cloned().unwrap() - ) as *mut R, + stack: p.add(task_layout.offset_t) as *mut ProcStack, + schedule: p.add(task_layout.offset_s) as *const S, + future: p.add(task_layout.offset_f) as *mut F, + output: p.add(task_layout.offset_r) as *mut R, } } } /// Returns the memory layout for a task. #[inline] - fn task_layout() -> ProcLayout { - // Compute the layouts for `ProcData`, `T`, `S`, `F`, and `R`. - let layout_header = Layout::new::(); + fn task_layout() -> TaskLayout { + let layout_pdata = Layout::new::(); let layout_t = Layout::new::(); let layout_s = Layout::new::(); let layout_f = Layout::new::(); let layout_r = Layout::new::(); - // Compute the layout for `union { F, R }`. let size_union = layout_f.size().max(layout_r.size()); let align_union = layout_f.align().max(layout_r.align()); let layout_union = unsafe { Layout::from_size_align_unchecked(size_union, align_union) }; - // Compute the layout for `ProcData` followed by `T`, then `S`, then `union { F, R }`. - let layout = layout_header; + let layout = layout_pdata; let (layout, offset_t) = extend(layout, layout_t); let (layout, offset_s) = extend(layout, layout_s); let (layout, offset_union) = extend(layout, layout_union); let offset_f = offset_union; let offset_r = offset_union; - let mut offset_table - = FxHashMap::default(); - offset_table.insert("future", offset_f); - offset_table.insert("stack", offset_t); - offset_table.insert("output", offset_r); - offset_table.insert("schedule", offset_s); - - ProcLayout { + TaskLayout { layout, - offset_table + offset_t, + offset_s, + offset_f, + offset_r, } } @@ -316,11 +289,7 @@ impl RawProc let raw = Self::from_ptr(ptr); // Decrement the reference count. - let k = (*raw.pdata).state.load(Ordering::SeqCst); - - let new = (*raw.pdata) - .state - .fetch_sub(REFERENCE, Ordering::AcqRel) - REFERENCE; + let new = (*raw.pdata).state.fetch_sub(REFERENCE, Ordering::AcqRel) - REFERENCE; // If this was the last reference to the task and the `JoinHandle` has been dropped as // well, then destroy the task. @@ -347,9 +316,7 @@ impl RawProc let raw = Self::from_ptr(ptr); // We need a safeguard against panics because the destructor can panic. - abort_on_panic(|| { - raw.future.drop_in_place(); - }) + raw.future.drop_in_place(); } /// Returns a pointer to the output inside a task. @@ -361,20 +328,18 @@ impl RawProc /// Cleans up task's resources and deallocates it. /// /// If the task has not been closed, then its future or the output will be dropped. The - /// schedule function and the tag get dropped too. + /// schedule function and the stack get dropped too. #[inline] unsafe fn destroy(ptr: *const ()) { let raw = Self::from_ptr(ptr); let task_layout = Self::task_layout(); // We need a safeguard against panics because destructors can panic. - abort_on_panic(|| { - // Drop the schedule function. - (raw.schedule as *mut S).drop_in_place(); + // Drop the schedule function. + (raw.schedule as *mut S).drop_in_place(); - // Drop the tag. - (raw.stack as *mut ProcStack).drop_in_place(); - }); + // Drop the stack. + (raw.stack as *mut ProcStack).drop_in_place(); // Finally, deallocate the memory reserved by the task. alloc::dealloc(ptr as *mut u8, task_layout.layout); @@ -387,7 +352,7 @@ impl RawProc unsafe fn run(ptr: *const ()) { let raw = Self::from_ptr(ptr); - // Create a context from the raw task pointer and the vtable inside the its header. + // Create a context from the raw task pointer and the vtable inside the its pdata. let waker = ManuallyDrop::new(Waker::from_raw(RawWaker::new( ptr, &(*raw.pdata).vtable.raw_waker, @@ -410,7 +375,6 @@ impl RawProc // Drop the task reference. Self::decrement(ptr); - return; } @@ -532,16 +496,16 @@ impl RawProc /// A guard that closes the task if polling its future panics. struct Guard(RawProc) - where - F: Future + Send + 'static, - R: Send + 'static, - S: Fn(LightProc) + Send + Sync + 'static; + where + F: Future + Send + 'static, + R: Send + 'static, + S: Fn(LightProc) + Send + Sync + 'static; impl Drop for Guard - where - F: Future + Send + 'static, - R: Send + 'static, - S: Fn(LightProc) + Send + Sync + 'static + where + F: Future + Send + 'static, + R: Send + 'static, + S: Fn(LightProc) + Send + Sync + 'static { fn drop(&mut self) { let raw = self.0; diff --git a/lightproc/src/stack.rs b/lightproc/src/stack.rs index eb175888..8bc80cb3 100644 --- a/lightproc/src/stack.rs +++ b/lightproc/src/stack.rs @@ -1,10 +1,12 @@ -use std::sync::atomic::AtomicUsize; +use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; use std::fmt::{Formatter, Error}; use std::fmt; -#[derive(Clone, Default)] +#[derive(Default)] pub struct ProcStack { + pub pid: AtomicUsize, + pub after_start: Option>, pub after_complete: Option>, @@ -13,6 +15,7 @@ pub struct ProcStack { impl fmt::Debug for ProcStack { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { f.debug_struct("ProcStack") + .field("pid", &self.pid.load(Ordering::SeqCst)) .finish() } } diff --git a/lightproc/src/state.rs b/lightproc/src/state.rs index 86050aca..d6ce34fd 100644 --- a/lightproc/src/state.rs +++ b/lightproc/src/state.rs @@ -63,8 +63,3 @@ pub(crate) const LOCKED: usize = 1 << 6; /// Note that the reference counter only tracks the `Task` and `Waker`s. The `JoinHandle` is /// tracked separately by the `HANDLE` flag. pub(crate) const REFERENCE: usize = 1 << 7; - -/// Finalized build of proc. -/// -/// Determine how drop behavior will change. -pub(crate) const BUILD_FINALIZED: usize = 1 << 8; diff --git a/lightproc/src/utils.rs b/lightproc/src/utils.rs deleted file mode 100644 index d6d87c0c..00000000 --- a/lightproc/src/utils.rs +++ /dev/null @@ -1,21 +0,0 @@ -use std::alloc::Layout; -use std::mem; - -/// Calls a function and aborts if it panics. -/// -/// This is useful in unsafe code where we can't recover from panics. -#[inline] -pub(crate) fn abort_on_panic(f: impl FnOnce() -> T) -> T { - struct Bomb; - - impl Drop for Bomb { - fn drop(&mut self) { - std::process::abort(); - } - } - - let bomb = Bomb; - let t = f(); - mem::forget(bomb); - t -} From 3838dec8e98b09a002a432962f0c98d381cf9110 Mon Sep 17 00:00:00 2001 From: Mahmut Bulut Date: Sun, 20 Oct 2019 22:34:23 +0200 Subject: [PATCH 12/38] After complete callback done --- lightproc/examples/proc_run.rs | 11 ++++++++++- lightproc/src/raw_proc.rs | 4 ++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/lightproc/examples/proc_run.rs b/lightproc/examples/proc_run.rs index ad1e2382..62ddd2d5 100644 --- a/lightproc/examples/proc_run.rs +++ b/lightproc/examples/proc_run.rs @@ -7,6 +7,7 @@ use std::thread; use crossbeam::channel; use futures::executor; use lightproc::prelude::*; +use std::sync::atomic::AtomicUsize; fn spawn_on_thread(fut: F) -> ProcHandle where @@ -26,7 +27,15 @@ fn spawn_on_thread(fut: F) -> ProcHandle let (proc, handle) = LightProc::build( future, schedule, - ProcStack::default() + ProcStack { + pid: AtomicUsize::new(1), + after_complete: Some(Arc::new(|| { + println!("After complete"); + })), + after_start: Some(Arc::new(|| { + println!("After start"); + })) + } ); proc.schedule(); diff --git a/lightproc/src/raw_proc.rs b/lightproc/src/raw_proc.rs index df0e8507..2a2f3dc0 100644 --- a/lightproc/src/raw_proc.rs +++ b/lightproc/src/raw_proc.rs @@ -438,6 +438,10 @@ where (*raw.pdata).notify(); } + if let Some(after_complete_cb) = &(*raw.stack).after_complete { + (*after_complete_cb)(); + } + // Drop the task reference. Self::decrement(ptr); break; From 2c548c2bdae01b701476a1fd8d8eb87f373d5daa Mon Sep 17 00:00:00 2001 From: Mahmut Bulut Date: Sun, 20 Oct 2019 22:37:50 +0200 Subject: [PATCH 13/38] Before start callback --- lightproc/examples/proc_run.rs | 4 ++-- lightproc/src/lib.rs | 4 ++-- lightproc/src/lightproc.rs | 2 +- lightproc/src/proc_data.rs | 2 +- lightproc/src/proc_handle.rs | 2 +- lightproc/src/{stack.rs => proc_stack.rs} | 4 +++- lightproc/src/raw_proc.rs | 7 ++++++- 7 files changed, 16 insertions(+), 9 deletions(-) rename lightproc/src/{stack.rs => proc_stack.rs} (79%) diff --git a/lightproc/examples/proc_run.rs b/lightproc/examples/proc_run.rs index 62ddd2d5..ea96ff08 100644 --- a/lightproc/examples/proc_run.rs +++ b/lightproc/examples/proc_run.rs @@ -32,8 +32,8 @@ fn spawn_on_thread(fut: F) -> ProcHandle after_complete: Some(Arc::new(|| { println!("After complete"); })), - after_start: Some(Arc::new(|| { - println!("After start"); + before_start: Some(Arc::new(|| { + println!("Before start"); })) } ); diff --git a/lightproc/src/lib.rs b/lightproc/src/lib.rs index 47bd274a..2ef91203 100644 --- a/lightproc/src/lib.rs +++ b/lightproc/src/lib.rs @@ -5,11 +5,11 @@ pub mod proc_handle; pub mod proc_layout; pub mod proc_vtable; pub mod raw_proc; -pub mod stack; +pub mod proc_stack; pub mod state; pub mod prelude { pub use crate::lightproc::*; - pub use crate::stack::*; + pub use crate::proc_stack::*; pub use crate::proc_handle::*; } diff --git a/lightproc/src/lightproc.rs b/lightproc/src/lightproc.rs index 14f080d3..41cdd1de 100644 --- a/lightproc/src/lightproc.rs +++ b/lightproc/src/lightproc.rs @@ -7,7 +7,7 @@ use std::ptr::NonNull; use crate::proc_data::ProcData; use crate::raw_proc::RawProc; use crate::proc_handle::ProcHandle; -use crate::stack::*; +use crate::proc_stack::*; pub struct LightProc { diff --git a/lightproc/src/proc_data.rs b/lightproc/src/proc_data.rs index 35cde993..4ed97b22 100644 --- a/lightproc/src/proc_data.rs +++ b/lightproc/src/proc_data.rs @@ -7,7 +7,7 @@ use std::task::Waker; use crossbeam_utils::Backoff; use crate::state::*; -use crate::stack::*; +use crate::proc_stack::*; use crate::proc_vtable::TaskVTable; use crate::layout_helpers::extend; diff --git a/lightproc/src/proc_handle.rs b/lightproc/src/proc_handle.rs index c983ae8d..47ac2a9b 100644 --- a/lightproc/src/proc_handle.rs +++ b/lightproc/src/proc_handle.rs @@ -7,7 +7,7 @@ use std::sync::atomic::Ordering; use std::task::{Context, Poll}; use crate::state::*; -use crate::stack::*; +use crate::proc_stack::*; use crate::proc_data::ProcData; /// A handle that awaits the result of a task. diff --git a/lightproc/src/stack.rs b/lightproc/src/proc_stack.rs similarity index 79% rename from lightproc/src/stack.rs rename to lightproc/src/proc_stack.rs index 8bc80cb3..a90c0b6a 100644 --- a/lightproc/src/stack.rs +++ b/lightproc/src/proc_stack.rs @@ -7,8 +7,10 @@ use std::fmt; pub struct ProcStack { pub pid: AtomicUsize, - pub after_start: Option>, + // Before action callbacks + pub before_start: Option>, + // After action callbacks pub after_complete: Option>, } diff --git a/lightproc/src/raw_proc.rs b/lightproc/src/raw_proc.rs index 2a2f3dc0..5627f4a8 100644 --- a/lightproc/src/raw_proc.rs +++ b/lightproc/src/raw_proc.rs @@ -8,7 +8,7 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker}; use crate::state::*; -use crate::stack::*; +use crate::proc_stack::*; use crate::proc_vtable::TaskVTable; use crate::proc_layout::TaskLayout; use crate::proc_data::ProcData; @@ -397,6 +397,11 @@ where // Poll the inner future, but surround it with a guard that closes the task in case polling // panics. let guard = Guard(raw); + + if let Some(before_start_cb) = &(*raw.stack).before_start { + (*before_start_cb)(); + } + let poll = ::poll(Pin::new_unchecked(&mut *raw.future), cx); mem::forget(guard); From 22367c81c76a632c9573ca33f87b6047db5c7198 Mon Sep 17 00:00:00 2001 From: Mahmut Bulut Date: Sun, 20 Oct 2019 22:49:05 +0200 Subject: [PATCH 14/38] Cargo fix --- lightproc/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/lightproc/Cargo.toml b/lightproc/Cargo.toml index 8d143887..4e9536c6 100644 --- a/lightproc/Cargo.toml +++ b/lightproc/Cargo.toml @@ -9,7 +9,6 @@ edition = "2018" [dependencies] crossbeam-utils = "0.6.6" -rustc-hash = "1.0.1" [dev-dependencies] crossbeam = "0.7.1" From 929b1e9be654e0b4bc3dd6a209ea892ee7f48e15 Mon Sep 17 00:00:00 2001 From: Mahmut Bulut Date: Sun, 20 Oct 2019 22:50:19 +0200 Subject: [PATCH 15/38] Cargo fmt --- lightproc/examples/proc_run.rs | 10 +++++----- lightproc/src/layout_helpers.rs | 5 +---- lightproc/src/lib.rs | 4 ++-- lightproc/src/lightproc.rs | 15 ++++++--------- lightproc/src/proc_data.rs | 4 ++-- lightproc/src/proc_handle.rs | 7 +++---- lightproc/src/proc_stack.rs | 4 ++-- lightproc/src/raw_proc.rs | 20 ++++++++++---------- 8 files changed, 31 insertions(+), 38 deletions(-) diff --git a/lightproc/examples/proc_run.rs b/lightproc/examples/proc_run.rs index ea96ff08..7abe484b 100644 --- a/lightproc/examples/proc_run.rs +++ b/lightproc/examples/proc_run.rs @@ -10,9 +10,9 @@ use lightproc::prelude::*; use std::sync::atomic::AtomicUsize; fn spawn_on_thread(fut: F) -> ProcHandle - where - F: Future + Send + 'static, - R: Send + 'static, +where + F: Future + Send + 'static, + R: Send + 'static, { let (sender, receiver) = channel::unbounded(); let sender = Arc::new(sender); @@ -34,8 +34,8 @@ fn spawn_on_thread(fut: F) -> ProcHandle })), before_start: Some(Arc::new(|| { println!("Before start"); - })) - } + })), + }, ); proc.schedule(); diff --git a/lightproc/src/layout_helpers.rs b/lightproc/src/layout_helpers.rs index 86dd9407..07d668b7 100644 --- a/lightproc/src/layout_helpers.rs +++ b/lightproc/src/layout_helpers.rs @@ -16,10 +16,7 @@ pub fn extend(layout: Layout, next: Layout) -> (Layout, usize) { .unwrap(); let new_size = offset .checked_add(next.size()) - .ok_or(Error::new( - ErrorKind::Other, - "New size can't be computed", - )) + .ok_or(Error::new(ErrorKind::Other, "New size can't be computed")) .unwrap(); let layout = Layout::from_size_align(new_size, new_align).unwrap(); diff --git a/lightproc/src/lib.rs b/lightproc/src/lib.rs index 2ef91203..0b2d37c6 100644 --- a/lightproc/src/lib.rs +++ b/lightproc/src/lib.rs @@ -3,13 +3,13 @@ pub mod lightproc; pub mod proc_data; pub mod proc_handle; pub mod proc_layout; +pub mod proc_stack; pub mod proc_vtable; pub mod raw_proc; -pub mod proc_stack; pub mod state; pub mod prelude { pub use crate::lightproc::*; - pub use crate::proc_stack::*; pub use crate::proc_handle::*; + pub use crate::proc_stack::*; } diff --git a/lightproc/src/lightproc.rs b/lightproc/src/lightproc.rs index 41cdd1de..06b1e1ea 100644 --- a/lightproc/src/lightproc.rs +++ b/lightproc/src/lightproc.rs @@ -5,10 +5,9 @@ use std::mem; use std::ptr::NonNull; use crate::proc_data::ProcData; -use crate::raw_proc::RawProc; use crate::proc_handle::ProcHandle; use crate::proc_stack::*; - +use crate::raw_proc::RawProc; pub struct LightProc { /// A pointer to the heap-allocated task. @@ -20,15 +19,13 @@ unsafe impl Sync for LightProc {} impl LightProc { pub fn build(future: F, schedule: S, stack: ProcStack) -> (LightProc, ProcHandle) - where - F: Future + Send + 'static, - R: Send + 'static, - S: Fn(LightProc) + Send + Sync + 'static + where + F: Future + Send + 'static, + R: Send + 'static, + S: Fn(LightProc) + Send + Sync + 'static, { let raw_task = RawProc::::allocate(stack, future, schedule); - let task = LightProc { - raw_proc: raw_task - }; + let task = LightProc { raw_proc: raw_task }; let handle = ProcHandle { raw_proc: raw_task, _marker: PhantomData, diff --git a/lightproc/src/proc_data.rs b/lightproc/src/proc_data.rs index 4ed97b22..27426cb4 100644 --- a/lightproc/src/proc_data.rs +++ b/lightproc/src/proc_data.rs @@ -6,10 +6,10 @@ use std::task::Waker; use crossbeam_utils::Backoff; -use crate::state::*; +use crate::layout_helpers::extend; use crate::proc_stack::*; use crate::proc_vtable::TaskVTable; -use crate::layout_helpers::extend; +use crate::state::*; /// The pdata of a task. /// diff --git a/lightproc/src/proc_handle.rs b/lightproc/src/proc_handle.rs index 47ac2a9b..915ec72e 100644 --- a/lightproc/src/proc_handle.rs +++ b/lightproc/src/proc_handle.rs @@ -6,9 +6,9 @@ use std::ptr::NonNull; use std::sync::atomic::Ordering; use std::task::{Context, Poll}; -use crate::state::*; -use crate::proc_stack::*; use crate::proc_data::ProcData; +use crate::proc_stack::*; +use crate::state::*; /// A handle that awaits the result of a task. /// @@ -125,8 +125,7 @@ impl Drop for ProcHandle { ) { Ok(_) => { // Read the output. - output = - Some((((*pdata).vtable.get_output)(ptr) as *mut R).read()); + output = Some((((*pdata).vtable.get_output)(ptr) as *mut R).read()); // Update the state variable because we're continuing the loop. state |= CLOSED; diff --git a/lightproc/src/proc_stack.rs b/lightproc/src/proc_stack.rs index a90c0b6a..bc85b8d7 100644 --- a/lightproc/src/proc_stack.rs +++ b/lightproc/src/proc_stack.rs @@ -1,7 +1,7 @@ +use std::fmt; +use std::fmt::{Error, Formatter}; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; -use std::fmt::{Formatter, Error}; -use std::fmt; #[derive(Default)] pub struct ProcStack { diff --git a/lightproc/src/raw_proc.rs b/lightproc/src/raw_proc.rs index 5627f4a8..0a6dbb5c 100644 --- a/lightproc/src/raw_proc.rs +++ b/lightproc/src/raw_proc.rs @@ -7,13 +7,13 @@ use std::ptr::NonNull; use std::sync::atomic::{AtomicUsize, Ordering}; use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker}; -use crate::state::*; +use crate::layout_helpers::extend; +use crate::lightproc::LightProc; +use crate::proc_data::ProcData; +use crate::proc_layout::TaskLayout; use crate::proc_stack::*; use crate::proc_vtable::TaskVTable; -use crate::proc_layout::TaskLayout; -use crate::proc_data::ProcData; -use crate::lightproc::LightProc; -use crate::layout_helpers::extend; +use crate::state::*; /// Raw pointers to the fields of a task. pub(crate) struct RawProc { @@ -42,7 +42,7 @@ impl RawProc where F: Future + Send + 'static, R: Send + 'static, - S: Fn(LightProc) + Send + Sync + 'static + S: Fn(LightProc) + Send + Sync + 'static, { /// Allocates a task with the given `future` and `schedule` function. /// @@ -184,7 +184,7 @@ where if state & (SCHEDULED | RUNNING) == 0 { // Schedule the task. let task = LightProc { - raw_proc: NonNull::new_unchecked(ptr as *mut ()) + raw_proc: NonNull::new_unchecked(ptr as *mut ()), }; (*raw.schedule)(task); } else { @@ -250,7 +250,7 @@ where // Schedule the task. let task = LightProc { - raw_proc: NonNull::new_unchecked(ptr as *mut ()) + raw_proc: NonNull::new_unchecked(ptr as *mut ()), }; (*raw.schedule)(task); } @@ -306,7 +306,7 @@ where let raw = Self::from_ptr(ptr); (*raw.schedule)(LightProc { - raw_proc: NonNull::new_unchecked(ptr as *mut ()) + raw_proc: NonNull::new_unchecked(ptr as *mut ()), }); } @@ -514,7 +514,7 @@ where where F: Future + Send + 'static, R: Send + 'static, - S: Fn(LightProc) + Send + Sync + 'static + S: Fn(LightProc) + Send + Sync + 'static, { fn drop(&mut self) { let raw = self.0; From 97f9e9a31814098cd98cb4aebb78f3bc1b8e13aa Mon Sep 17 00:00:00 2001 From: Mahmut Bulut Date: Mon, 21 Oct 2019 10:29:39 +0200 Subject: [PATCH 16/38] Callback clone for sharing callbacks from stack --- lightproc/src/raw_proc.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lightproc/src/raw_proc.rs b/lightproc/src/raw_proc.rs index 0a6dbb5c..55e2e90d 100644 --- a/lightproc/src/raw_proc.rs +++ b/lightproc/src/raw_proc.rs @@ -399,7 +399,7 @@ where let guard = Guard(raw); if let Some(before_start_cb) = &(*raw.stack).before_start { - (*before_start_cb)(); + (*before_start_cb.clone())(); } let poll = ::poll(Pin::new_unchecked(&mut *raw.future), cx); @@ -444,7 +444,7 @@ where } if let Some(after_complete_cb) = &(*raw.stack).after_complete { - (*after_complete_cb)(); + (*after_complete_cb.clone())(); } // Drop the task reference. From a3aba3b76138c8e71862a7c95c312d28e32f9401 Mon Sep 17 00:00:00 2001 From: Mahmut Bulut Date: Thu, 24 Oct 2019 14:30:08 +0200 Subject: [PATCH 17/38] After panic callback is added --- lightproc/Cargo.toml | 2 + lightproc/examples/proc_panic.rs | 65 +++++++++++++++++++++++++++++ lightproc/examples/proc_run.rs | 1 + lightproc/src/catch_unwind.rs | 30 +++++++++++++ lightproc/src/lib.rs | 4 ++ lightproc/src/lightproc.rs | 22 ++++++++-- lightproc/src/panic_helpers.rs | 17 ++++++++ lightproc/src/proc_data.rs | 4 +- lightproc/src/proc_ext.rs | 13 ++++++ lightproc/src/proc_handle.rs | 4 +- lightproc/src/proc_stack.rs | 2 + lightproc/src/proc_vtable.rs | 2 +- lightproc/src/raw_proc.rs | 16 ++++--- lightproc/src/recoverable_handle.rs | 27 ++++++++++++ 14 files changed, 197 insertions(+), 12 deletions(-) create mode 100644 lightproc/examples/proc_panic.rs create mode 100644 lightproc/src/catch_unwind.rs create mode 100644 lightproc/src/panic_helpers.rs create mode 100644 lightproc/src/proc_ext.rs create mode 100644 lightproc/src/recoverable_handle.rs diff --git a/lightproc/Cargo.toml b/lightproc/Cargo.toml index 4e9536c6..8c2fb714 100644 --- a/lightproc/Cargo.toml +++ b/lightproc/Cargo.toml @@ -9,7 +9,9 @@ edition = "2018" [dependencies] crossbeam-utils = "0.6.6" +pin-utils = "0.1.0-alpha.4" [dev-dependencies] crossbeam = "0.7.1" futures-preview = "0.3.0-alpha.17" +lazy_static = "1.3.0" diff --git a/lightproc/examples/proc_panic.rs b/lightproc/examples/proc_panic.rs new file mode 100644 index 00000000..4c395145 --- /dev/null +++ b/lightproc/examples/proc_panic.rs @@ -0,0 +1,65 @@ +use std::future::Future; +use std::sync::Arc; +use std::thread; + +use crossbeam::channel::{unbounded, Sender}; +use futures::{executor, FutureExt}; +use lightproc::prelude::*; +use std::sync::atomic::AtomicUsize; +use lazy_static::lazy_static; +use std::panic::AssertUnwindSafe; +use lightproc::proc_ext::ProcFutureExt; +use lightproc::recoverable_handle::RecoverableHandle; + + +fn spawn_on_thread(future: F) -> RecoverableHandle + where + F: Future + Send + 'static, + R: Send + 'static, +{ + lazy_static! { + // A channel that holds scheduled tasks. + static ref QUEUE: Sender = { + let (sender, receiver) = unbounded::(); + + // Start the executor thread. + thread::spawn(move || { + for proc in receiver { + proc.run(); + } + }); + + sender + }; + } + + let schedule = |t| QUEUE.send(t).unwrap(); + let (proc, handle) = LightProc::recoverable( + future, + schedule, + ProcStack { + pid: AtomicUsize::new(1), + after_complete: Some(Arc::new(|| { + println!("After complete"); + })), + before_start: Some(Arc::new(|| { + println!("Before start"); + })), + after_panic: Some(Arc::new(|| { + println!("After panic"); + })) + }, + ); + + proc.schedule(); + + handle +} + +fn main() { + let handle = spawn_on_thread(async { + panic!("Panic here!"); + }); + + executor::block_on(handle); +} diff --git a/lightproc/examples/proc_run.rs b/lightproc/examples/proc_run.rs index 7abe484b..2c539d20 100644 --- a/lightproc/examples/proc_run.rs +++ b/lightproc/examples/proc_run.rs @@ -35,6 +35,7 @@ where before_start: Some(Arc::new(|| { println!("Before start"); })), + after_panic: None }, ); diff --git a/lightproc/src/catch_unwind.rs b/lightproc/src/catch_unwind.rs new file mode 100644 index 00000000..5857eb58 --- /dev/null +++ b/lightproc/src/catch_unwind.rs @@ -0,0 +1,30 @@ +use std::future::Future; +use std::panic::{UnwindSafe, catch_unwind, AssertUnwindSafe}; +use pin_utils::unsafe_pinned; +use std::any::Any; +use std::task::{Context, Poll}; +use std::pin::Pin; + +#[derive(Debug)] +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct CatchUnwind where Fut: Future { + future: Fut, +} + +impl CatchUnwind where Fut: Future + UnwindSafe { + unsafe_pinned!(future: Fut); + + pub(super) fn new(future: Fut) -> CatchUnwind { + CatchUnwind { future } + } +} + +impl Future for CatchUnwind + where Fut: Future + UnwindSafe, +{ + type Output = Result>; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + catch_unwind(AssertUnwindSafe(|| self.future().poll(cx)))?.map(Ok) + } +} diff --git a/lightproc/src/lib.rs b/lightproc/src/lib.rs index 0b2d37c6..b451c0da 100644 --- a/lightproc/src/lib.rs +++ b/lightproc/src/lib.rs @@ -7,6 +7,10 @@ pub mod proc_stack; pub mod proc_vtable; pub mod raw_proc; pub mod state; +pub mod panic_helpers; +pub mod catch_unwind; +pub mod proc_ext; +pub mod recoverable_handle; pub mod prelude { pub use crate::lightproc::*; diff --git a/lightproc/src/lightproc.rs b/lightproc/src/lightproc.rs index 06b1e1ea..1caacb3f 100644 --- a/lightproc/src/lightproc.rs +++ b/lightproc/src/lightproc.rs @@ -1,4 +1,4 @@ -use std::fmt; +use std::{fmt, thread}; use std::future::Future; use std::marker::PhantomData; use std::mem; @@ -7,7 +7,11 @@ use std::ptr::NonNull; use crate::proc_data::ProcData; use crate::proc_handle::ProcHandle; use crate::proc_stack::*; -use crate::raw_proc::RawProc; +use crate::raw_proc::{RawProc, ProcFuture}; +use std::panic::AssertUnwindSafe; +use crate::proc_ext::ProcFutureExt; +use crate::catch_unwind::CatchUnwind; +use crate::recoverable_handle::RecoverableHandle; pub struct LightProc { /// A pointer to the heap-allocated task. @@ -18,13 +22,25 @@ unsafe impl Send for LightProc {} unsafe impl Sync for LightProc {} impl LightProc { + pub fn recoverable(future: F, schedule: S, stack: ProcStack) -> (LightProc, RecoverableHandle) + where + F: Future + Send + 'static, + R: Send + 'static, + S: Fn(LightProc) + Send + Sync + 'static, + { + let recovery_future = AssertUnwindSafe(future).catch_unwind(); + let (proc, handle) = Self::build(recovery_future, schedule, stack); + (proc, RecoverableHandle(handle)) + } + + pub fn build(future: F, schedule: S, stack: ProcStack) -> (LightProc, ProcHandle) where F: Future + Send + 'static, R: Send + 'static, S: Fn(LightProc) + Send + Sync + 'static, { - let raw_task = RawProc::::allocate(stack, future, schedule); + let raw_task = RawProc::allocate(stack, future, schedule); let task = LightProc { raw_proc: raw_task }; let handle = ProcHandle { raw_proc: raw_task, diff --git a/lightproc/src/panic_helpers.rs b/lightproc/src/panic_helpers.rs new file mode 100644 index 00000000..966e3204 --- /dev/null +++ b/lightproc/src/panic_helpers.rs @@ -0,0 +1,17 @@ +use std::mem; + +#[inline] +pub(crate) fn abort_on_panic(f: impl FnOnce() -> T) -> T { + struct Bomb; + + impl Drop for Bomb { + fn drop(&mut self) { + std::process::abort(); + } + } + + let bomb = Bomb; + let t = f(); + mem::forget(bomb); + t +} diff --git a/lightproc/src/proc_data.rs b/lightproc/src/proc_data.rs index 27426cb4..a52d5a4d 100644 --- a/lightproc/src/proc_data.rs +++ b/lightproc/src/proc_data.rs @@ -8,7 +8,7 @@ use crossbeam_utils::Backoff; use crate::layout_helpers::extend; use crate::proc_stack::*; -use crate::proc_vtable::TaskVTable; +use crate::proc_vtable::ProcVTable; use crate::state::*; /// The pdata of a task. @@ -29,7 +29,7 @@ pub(crate) struct ProcData { /// /// In addition to the actual waker virtual table, it also contains pointers to several other /// methods necessary for bookkeeping the heap-allocated task. - pub(crate) vtable: &'static TaskVTable, + pub(crate) vtable: &'static ProcVTable, } impl ProcData { diff --git a/lightproc/src/proc_ext.rs b/lightproc/src/proc_ext.rs new file mode 100644 index 00000000..187cbed1 --- /dev/null +++ b/lightproc/src/proc_ext.rs @@ -0,0 +1,13 @@ +use std::future::Future; +use std::panic::UnwindSafe; +use crate::catch_unwind::CatchUnwind; + +pub trait ProcFutureExt: Future { + fn catch_unwind(self) -> CatchUnwind + where Self: Sized + UnwindSafe + { + CatchUnwind::new(self) + } +} + +impl ProcFutureExt for T where T: Future {} diff --git a/lightproc/src/proc_handle.rs b/lightproc/src/proc_handle.rs index 915ec72e..8cdfeee5 100644 --- a/lightproc/src/proc_handle.rs +++ b/lightproc/src/proc_handle.rs @@ -1,4 +1,4 @@ -use std::fmt; +use std::{fmt, mem}; use std::future::Future; use std::marker::{PhantomData, Unpin}; use std::pin::Pin; @@ -9,6 +9,7 @@ use std::task::{Context, Poll}; use crate::proc_data::ProcData; use crate::proc_stack::*; use crate::state::*; +use std::any::Any; /// A handle that awaits the result of a task. /// @@ -242,6 +243,7 @@ impl Future for ProcHandle { } } + impl fmt::Debug for ProcHandle { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let ptr = self.raw_proc.as_ptr(); diff --git a/lightproc/src/proc_stack.rs b/lightproc/src/proc_stack.rs index bc85b8d7..68094e98 100644 --- a/lightproc/src/proc_stack.rs +++ b/lightproc/src/proc_stack.rs @@ -12,6 +12,8 @@ pub struct ProcStack { // After action callbacks pub after_complete: Option>, + + pub after_panic: Option>, } impl fmt::Debug for ProcStack { diff --git a/lightproc/src/proc_vtable.rs b/lightproc/src/proc_vtable.rs index 9ce3d44c..ecf1231c 100644 --- a/lightproc/src/proc_vtable.rs +++ b/lightproc/src/proc_vtable.rs @@ -1,7 +1,7 @@ use std::task::RawWakerVTable; /// The vtable for a task. -pub(crate) struct TaskVTable { +pub(crate) struct ProcVTable { /// The raw waker vtable. pub(crate) raw_waker: RawWakerVTable, diff --git a/lightproc/src/raw_proc.rs b/lightproc/src/raw_proc.rs index 55e2e90d..a06ce190 100644 --- a/lightproc/src/raw_proc.rs +++ b/lightproc/src/raw_proc.rs @@ -8,12 +8,17 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker}; use crate::layout_helpers::extend; -use crate::lightproc::LightProc; +use crate::lightproc::{LightProc}; use crate::proc_data::ProcData; use crate::proc_layout::TaskLayout; use crate::proc_stack::*; -use crate::proc_vtable::TaskVTable; +use crate::proc_vtable::ProcVTable; use crate::state::*; +use std::panic::AssertUnwindSafe; +use crate::catch_unwind::CatchUnwind; +use std::any::Any; + +pub type ProcFuture = CatchUnwind>; /// Raw pointers to the fields of a task. pub(crate) struct RawProc { @@ -26,7 +31,8 @@ pub(crate) struct RawProc { impl Copy for RawProc {} -impl Clone for RawProc { +impl Clone for RawProc +{ fn clone(&self) -> Self { Self { pdata: self.pdata, @@ -64,7 +70,7 @@ where (raw.pdata as *mut ProcData).write(ProcData { state: AtomicUsize::new(SCHEDULED | HANDLE | REFERENCE), awaiter: Cell::new(None), - vtable: &TaskVTable { + vtable: &ProcVTable { raw_waker: RawWakerVTable::new( Self::clone_waker, Self::wake, @@ -116,7 +122,7 @@ where let layout_pdata = Layout::new::(); let layout_t = Layout::new::(); let layout_s = Layout::new::(); - let layout_f = Layout::new::(); + let layout_f = Layout::new::>(); let layout_r = Layout::new::(); let size_union = layout_f.size().max(layout_r.size()); diff --git a/lightproc/src/recoverable_handle.rs b/lightproc/src/recoverable_handle.rs new file mode 100644 index 00000000..11cdc7f3 --- /dev/null +++ b/lightproc/src/recoverable_handle.rs @@ -0,0 +1,27 @@ +use crate::proc_handle::ProcHandle; +use std::thread; +use std::future::Future; +use std::task::{Context, Poll}; +use std::pin::Pin; +use std::panic::resume_unwind; + +pub struct RecoverableHandle(pub ProcHandle>); + +impl Future for RecoverableHandle { + type Output = Option; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + match Pin::new(&mut self.0).poll(cx) { + Poll::Pending => Poll::Pending, + Poll::Ready(None) => Poll::Ready(None), + Poll::Ready(Some(Ok(val))) => Poll::Ready(Some(val)), + Poll::Ready(Some(Err(err))) => { + if let Some(after_panic_cb) = self.0.stack().after_panic.clone() { + (*after_panic_cb.clone())(); + } + + resume_unwind(err) + }, + } + } +} From 000d42e4251ba44118356e9a91dda64b3d2932a4 Mon Sep 17 00:00:00 2001 From: Mahmut Bulut Date: Thu, 24 Oct 2019 14:30:31 +0200 Subject: [PATCH 18/38] Format all the code --- lightproc/examples/proc_panic.rs | 15 +++++++-------- lightproc/examples/proc_run.rs | 2 +- lightproc/src/catch_unwind.rs | 19 +++++++++++++------ lightproc/src/lib.rs | 8 ++++---- lightproc/src/lightproc.rs | 25 ++++++++++++++----------- lightproc/src/proc_ext.rs | 5 +++-- lightproc/src/proc_handle.rs | 3 +-- lightproc/src/proc_stack.rs | 2 +- lightproc/src/raw_proc.rs | 9 ++++----- lightproc/src/recoverable_handle.rs | 8 ++++---- 10 files changed, 52 insertions(+), 44 deletions(-) diff --git a/lightproc/examples/proc_panic.rs b/lightproc/examples/proc_panic.rs index 4c395145..01a82e34 100644 --- a/lightproc/examples/proc_panic.rs +++ b/lightproc/examples/proc_panic.rs @@ -4,18 +4,17 @@ use std::thread; use crossbeam::channel::{unbounded, Sender}; use futures::{executor, FutureExt}; -use lightproc::prelude::*; -use std::sync::atomic::AtomicUsize; use lazy_static::lazy_static; -use std::panic::AssertUnwindSafe; +use lightproc::prelude::*; use lightproc::proc_ext::ProcFutureExt; use lightproc::recoverable_handle::RecoverableHandle; - +use std::panic::AssertUnwindSafe; +use std::sync::atomic::AtomicUsize; fn spawn_on_thread(future: F) -> RecoverableHandle - where - F: Future + Send + 'static, - R: Send + 'static, +where + F: Future + Send + 'static, + R: Send + 'static, { lazy_static! { // A channel that holds scheduled tasks. @@ -47,7 +46,7 @@ fn spawn_on_thread(future: F) -> RecoverableHandle })), after_panic: Some(Arc::new(|| { println!("After panic"); - })) + })), }, ); diff --git a/lightproc/examples/proc_run.rs b/lightproc/examples/proc_run.rs index 2c539d20..670cce14 100644 --- a/lightproc/examples/proc_run.rs +++ b/lightproc/examples/proc_run.rs @@ -35,7 +35,7 @@ where before_start: Some(Arc::new(|| { println!("Before start"); })), - after_panic: None + after_panic: None, }, ); diff --git a/lightproc/src/catch_unwind.rs b/lightproc/src/catch_unwind.rs index 5857eb58..00e4de50 100644 --- a/lightproc/src/catch_unwind.rs +++ b/lightproc/src/catch_unwind.rs @@ -1,17 +1,23 @@ -use std::future::Future; -use std::panic::{UnwindSafe, catch_unwind, AssertUnwindSafe}; use pin_utils::unsafe_pinned; use std::any::Any; -use std::task::{Context, Poll}; +use std::future::Future; +use std::panic::{catch_unwind, AssertUnwindSafe, UnwindSafe}; use std::pin::Pin; +use std::task::{Context, Poll}; #[derive(Debug)] #[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct CatchUnwind where Fut: Future { +pub struct CatchUnwind +where + Fut: Future, +{ future: Fut, } -impl CatchUnwind where Fut: Future + UnwindSafe { +impl CatchUnwind +where + Fut: Future + UnwindSafe, +{ unsafe_pinned!(future: Fut); pub(super) fn new(future: Fut) -> CatchUnwind { @@ -20,7 +26,8 @@ impl CatchUnwind where Fut: Future + UnwindSafe { } impl Future for CatchUnwind - where Fut: Future + UnwindSafe, +where + Fut: Future + UnwindSafe, { type Output = Result>; diff --git a/lightproc/src/lib.rs b/lightproc/src/lib.rs index b451c0da..a90e0ac4 100644 --- a/lightproc/src/lib.rs +++ b/lightproc/src/lib.rs @@ -1,16 +1,16 @@ +pub mod catch_unwind; pub mod layout_helpers; pub mod lightproc; +pub mod panic_helpers; pub mod proc_data; +pub mod proc_ext; pub mod proc_handle; pub mod proc_layout; pub mod proc_stack; pub mod proc_vtable; pub mod raw_proc; -pub mod state; -pub mod panic_helpers; -pub mod catch_unwind; -pub mod proc_ext; pub mod recoverable_handle; +pub mod state; pub mod prelude { pub use crate::lightproc::*; diff --git a/lightproc/src/lightproc.rs b/lightproc/src/lightproc.rs index 1caacb3f..4efc472f 100644 --- a/lightproc/src/lightproc.rs +++ b/lightproc/src/lightproc.rs @@ -1,17 +1,17 @@ -use std::{fmt, thread}; use std::future::Future; use std::marker::PhantomData; use std::mem; use std::ptr::NonNull; +use std::{fmt, thread}; +use crate::catch_unwind::CatchUnwind; use crate::proc_data::ProcData; +use crate::proc_ext::ProcFutureExt; use crate::proc_handle::ProcHandle; use crate::proc_stack::*; -use crate::raw_proc::{RawProc, ProcFuture}; -use std::panic::AssertUnwindSafe; -use crate::proc_ext::ProcFutureExt; -use crate::catch_unwind::CatchUnwind; +use crate::raw_proc::{ProcFuture, RawProc}; use crate::recoverable_handle::RecoverableHandle; +use std::panic::AssertUnwindSafe; pub struct LightProc { /// A pointer to the heap-allocated task. @@ -22,18 +22,21 @@ unsafe impl Send for LightProc {} unsafe impl Sync for LightProc {} impl LightProc { - pub fn recoverable(future: F, schedule: S, stack: ProcStack) -> (LightProc, RecoverableHandle) - where - F: Future + Send + 'static, - R: Send + 'static, - S: Fn(LightProc) + Send + Sync + 'static, + pub fn recoverable( + future: F, + schedule: S, + stack: ProcStack, + ) -> (LightProc, RecoverableHandle) + where + F: Future + Send + 'static, + R: Send + 'static, + S: Fn(LightProc) + Send + Sync + 'static, { let recovery_future = AssertUnwindSafe(future).catch_unwind(); let (proc, handle) = Self::build(recovery_future, schedule, stack); (proc, RecoverableHandle(handle)) } - pub fn build(future: F, schedule: S, stack: ProcStack) -> (LightProc, ProcHandle) where F: Future + Send + 'static, diff --git a/lightproc/src/proc_ext.rs b/lightproc/src/proc_ext.rs index 187cbed1..4fb5c319 100644 --- a/lightproc/src/proc_ext.rs +++ b/lightproc/src/proc_ext.rs @@ -1,10 +1,11 @@ +use crate::catch_unwind::CatchUnwind; use std::future::Future; use std::panic::UnwindSafe; -use crate::catch_unwind::CatchUnwind; pub trait ProcFutureExt: Future { fn catch_unwind(self) -> CatchUnwind - where Self: Sized + UnwindSafe + where + Self: Sized + UnwindSafe, { CatchUnwind::new(self) } diff --git a/lightproc/src/proc_handle.rs b/lightproc/src/proc_handle.rs index 8cdfeee5..6124ae53 100644 --- a/lightproc/src/proc_handle.rs +++ b/lightproc/src/proc_handle.rs @@ -1,10 +1,10 @@ -use std::{fmt, mem}; use std::future::Future; use std::marker::{PhantomData, Unpin}; use std::pin::Pin; use std::ptr::NonNull; use std::sync::atomic::Ordering; use std::task::{Context, Poll}; +use std::{fmt, mem}; use crate::proc_data::ProcData; use crate::proc_stack::*; @@ -243,7 +243,6 @@ impl Future for ProcHandle { } } - impl fmt::Debug for ProcHandle { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let ptr = self.raw_proc.as_ptr(); diff --git a/lightproc/src/proc_stack.rs b/lightproc/src/proc_stack.rs index 68094e98..679c9d99 100644 --- a/lightproc/src/proc_stack.rs +++ b/lightproc/src/proc_stack.rs @@ -12,7 +12,7 @@ pub struct ProcStack { // After action callbacks pub after_complete: Option>, - + pub after_panic: Option>, } diff --git a/lightproc/src/raw_proc.rs b/lightproc/src/raw_proc.rs index a06ce190..96064a0f 100644 --- a/lightproc/src/raw_proc.rs +++ b/lightproc/src/raw_proc.rs @@ -7,16 +7,16 @@ use std::ptr::NonNull; use std::sync::atomic::{AtomicUsize, Ordering}; use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker}; +use crate::catch_unwind::CatchUnwind; use crate::layout_helpers::extend; -use crate::lightproc::{LightProc}; +use crate::lightproc::LightProc; use crate::proc_data::ProcData; use crate::proc_layout::TaskLayout; use crate::proc_stack::*; use crate::proc_vtable::ProcVTable; use crate::state::*; -use std::panic::AssertUnwindSafe; -use crate::catch_unwind::CatchUnwind; use std::any::Any; +use std::panic::AssertUnwindSafe; pub type ProcFuture = CatchUnwind>; @@ -31,8 +31,7 @@ pub(crate) struct RawProc { impl Copy for RawProc {} -impl Clone for RawProc -{ +impl Clone for RawProc { fn clone(&self) -> Self { Self { pdata: self.pdata, diff --git a/lightproc/src/recoverable_handle.rs b/lightproc/src/recoverable_handle.rs index 11cdc7f3..d6a9ca6f 100644 --- a/lightproc/src/recoverable_handle.rs +++ b/lightproc/src/recoverable_handle.rs @@ -1,9 +1,9 @@ use crate::proc_handle::ProcHandle; -use std::thread; use std::future::Future; -use std::task::{Context, Poll}; -use std::pin::Pin; use std::panic::resume_unwind; +use std::pin::Pin; +use std::task::{Context, Poll}; +use std::thread; pub struct RecoverableHandle(pub ProcHandle>); @@ -21,7 +21,7 @@ impl Future for RecoverableHandle { } resume_unwind(err) - }, + } } } } From 8778921e2d8d3b3c0120dea8fa6eb6fe6f1a426c Mon Sep 17 00:00:00 2001 From: Mahmut Bulut Date: Thu, 24 Oct 2019 14:30:48 +0200 Subject: [PATCH 19/38] Cargo fix --- lightproc/examples/proc_panic.rs | 6 +++--- lightproc/src/lightproc.rs | 5 ++--- lightproc/src/proc_handle.rs | 3 +-- lightproc/src/raw_proc.rs | 2 +- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/lightproc/examples/proc_panic.rs b/lightproc/examples/proc_panic.rs index 01a82e34..4142b890 100644 --- a/lightproc/examples/proc_panic.rs +++ b/lightproc/examples/proc_panic.rs @@ -3,12 +3,12 @@ use std::sync::Arc; use std::thread; use crossbeam::channel::{unbounded, Sender}; -use futures::{executor, FutureExt}; +use futures::executor; use lazy_static::lazy_static; use lightproc::prelude::*; -use lightproc::proc_ext::ProcFutureExt; + use lightproc::recoverable_handle::RecoverableHandle; -use std::panic::AssertUnwindSafe; + use std::sync::atomic::AtomicUsize; fn spawn_on_thread(future: F) -> RecoverableHandle diff --git a/lightproc/src/lightproc.rs b/lightproc/src/lightproc.rs index 4efc472f..b4a1113d 100644 --- a/lightproc/src/lightproc.rs +++ b/lightproc/src/lightproc.rs @@ -1,15 +1,14 @@ +use std::fmt; use std::future::Future; use std::marker::PhantomData; use std::mem; use std::ptr::NonNull; -use std::{fmt, thread}; -use crate::catch_unwind::CatchUnwind; use crate::proc_data::ProcData; use crate::proc_ext::ProcFutureExt; use crate::proc_handle::ProcHandle; use crate::proc_stack::*; -use crate::raw_proc::{ProcFuture, RawProc}; +use crate::raw_proc::RawProc; use crate::recoverable_handle::RecoverableHandle; use std::panic::AssertUnwindSafe; diff --git a/lightproc/src/proc_handle.rs b/lightproc/src/proc_handle.rs index 6124ae53..915ec72e 100644 --- a/lightproc/src/proc_handle.rs +++ b/lightproc/src/proc_handle.rs @@ -1,15 +1,14 @@ +use std::fmt; use std::future::Future; use std::marker::{PhantomData, Unpin}; use std::pin::Pin; use std::ptr::NonNull; use std::sync::atomic::Ordering; use std::task::{Context, Poll}; -use std::{fmt, mem}; use crate::proc_data::ProcData; use crate::proc_stack::*; use crate::state::*; -use std::any::Any; /// A handle that awaits the result of a task. /// diff --git a/lightproc/src/raw_proc.rs b/lightproc/src/raw_proc.rs index 96064a0f..1b54a1b3 100644 --- a/lightproc/src/raw_proc.rs +++ b/lightproc/src/raw_proc.rs @@ -15,7 +15,7 @@ use crate::proc_layout::TaskLayout; use crate::proc_stack::*; use crate::proc_vtable::ProcVTable; use crate::state::*; -use std::any::Any; + use std::panic::AssertUnwindSafe; pub type ProcFuture = CatchUnwind>; From e7d16b72c2bd63e251a7db181b47266a60fef0f5 Mon Sep 17 00:00:00 2001 From: Mahmut Bulut Date: Thu, 24 Oct 2019 14:31:44 +0200 Subject: [PATCH 20/38] Remove unnecessary panic helpers --- lightproc/src/lib.rs | 1 - lightproc/src/panic_helpers.rs | 17 ----------------- 2 files changed, 18 deletions(-) delete mode 100644 lightproc/src/panic_helpers.rs diff --git a/lightproc/src/lib.rs b/lightproc/src/lib.rs index a90e0ac4..00b51c6d 100644 --- a/lightproc/src/lib.rs +++ b/lightproc/src/lib.rs @@ -1,7 +1,6 @@ pub mod catch_unwind; pub mod layout_helpers; pub mod lightproc; -pub mod panic_helpers; pub mod proc_data; pub mod proc_ext; pub mod proc_handle; diff --git a/lightproc/src/panic_helpers.rs b/lightproc/src/panic_helpers.rs deleted file mode 100644 index 966e3204..00000000 --- a/lightproc/src/panic_helpers.rs +++ /dev/null @@ -1,17 +0,0 @@ -use std::mem; - -#[inline] -pub(crate) fn abort_on_panic(f: impl FnOnce() -> T) -> T { - struct Bomb; - - impl Drop for Bomb { - fn drop(&mut self) { - std::process::abort(); - } - } - - let bomb = Bomb; - let t = f(); - mem::forget(bomb); - t -} From 58e0c723f6783252611d6aa3dd4f7cc6bf215a1f Mon Sep 17 00:00:00 2001 From: Mahmut Bulut Date: Thu, 24 Oct 2019 19:44:58 +0200 Subject: [PATCH 21/38] Builder pattern --- lightproc/examples/proc_panic.rs | 17 +++++------------ lightproc/examples/proc_run.rs | 14 ++++---------- lightproc/src/proc_stack.rs | 31 +++++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+), 22 deletions(-) diff --git a/lightproc/examples/proc_panic.rs b/lightproc/examples/proc_panic.rs index 4142b890..d0e2e371 100644 --- a/lightproc/examples/proc_panic.rs +++ b/lightproc/examples/proc_panic.rs @@ -36,18 +36,11 @@ where let (proc, handle) = LightProc::recoverable( future, schedule, - ProcStack { - pid: AtomicUsize::new(1), - after_complete: Some(Arc::new(|| { - println!("After complete"); - })), - before_start: Some(Arc::new(|| { - println!("Before start"); - })), - after_panic: Some(Arc::new(|| { - println!("After panic"); - })), - }, + ProcStack::default() + .with_pid(1) + .with_before_start(|| { println!("Before start"); }) + .with_after_complete(|| { println!("After complete"); }) + .with_after_panic(|| { println!("After panic"); }) ); proc.schedule(); diff --git a/lightproc/examples/proc_run.rs b/lightproc/examples/proc_run.rs index 670cce14..7894bb63 100644 --- a/lightproc/examples/proc_run.rs +++ b/lightproc/examples/proc_run.rs @@ -27,16 +27,10 @@ where let (proc, handle) = LightProc::build( future, schedule, - ProcStack { - pid: AtomicUsize::new(1), - after_complete: Some(Arc::new(|| { - println!("After complete"); - })), - before_start: Some(Arc::new(|| { - println!("Before start"); - })), - after_panic: None, - }, + ProcStack::default() + .with_pid(1) + .with_before_start(|| { println!("Before start"); }) + .with_after_complete(|| { println!("After complete"); }) ); proc.schedule(); diff --git a/lightproc/src/proc_stack.rs b/lightproc/src/proc_stack.rs index 679c9d99..04694888 100644 --- a/lightproc/src/proc_stack.rs +++ b/lightproc/src/proc_stack.rs @@ -16,6 +16,37 @@ pub struct ProcStack { pub after_panic: Option>, } +impl ProcStack { + pub fn with_pid(mut self, pid: usize) -> Self { + self.pid = AtomicUsize::new(pid); + self + } + + pub fn with_before_start(mut self, callback: T) -> Self + where + T: Fn() + Send + Sync + 'static + { + self.before_start = Some(Arc::new(callback)); + self + } + + pub fn with_after_complete(mut self, callback: T) -> Self + where + T: Fn() + Send + Sync + 'static + { + self.after_complete = Some(Arc::new(callback)); + self + } + + pub fn with_after_panic(mut self, callback: T) -> Self + where + T: Fn() + Send + Sync + 'static + { + self.after_panic = Some(Arc::new(callback)); + self + } +} + impl fmt::Debug for ProcStack { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { f.debug_struct("ProcStack") From 5f358efc72ec5ce2cf990829c103abe73fb88d29 Mon Sep 17 00:00:00 2001 From: Mahmut Bulut Date: Thu, 24 Oct 2019 19:45:41 +0200 Subject: [PATCH 22/38] Fix and format --- lightproc/examples/proc_panic.rs | 16 ++++++++++------ lightproc/examples/proc_run.rs | 9 ++++++--- lightproc/src/proc_stack.rs | 6 +++--- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/lightproc/examples/proc_panic.rs b/lightproc/examples/proc_panic.rs index d0e2e371..ea984cbf 100644 --- a/lightproc/examples/proc_panic.rs +++ b/lightproc/examples/proc_panic.rs @@ -1,5 +1,5 @@ use std::future::Future; -use std::sync::Arc; + use std::thread; use crossbeam::channel::{unbounded, Sender}; @@ -9,8 +9,6 @@ use lightproc::prelude::*; use lightproc::recoverable_handle::RecoverableHandle; -use std::sync::atomic::AtomicUsize; - fn spawn_on_thread(future: F) -> RecoverableHandle where F: Future + Send + 'static, @@ -38,9 +36,15 @@ where schedule, ProcStack::default() .with_pid(1) - .with_before_start(|| { println!("Before start"); }) - .with_after_complete(|| { println!("After complete"); }) - .with_after_panic(|| { println!("After panic"); }) + .with_before_start(|| { + println!("Before start"); + }) + .with_after_complete(|| { + println!("After complete"); + }) + .with_after_panic(|| { + println!("After panic"); + }), ); proc.schedule(); diff --git a/lightproc/examples/proc_run.rs b/lightproc/examples/proc_run.rs index 7894bb63..f0230746 100644 --- a/lightproc/examples/proc_run.rs +++ b/lightproc/examples/proc_run.rs @@ -7,7 +7,6 @@ use std::thread; use crossbeam::channel; use futures::executor; use lightproc::prelude::*; -use std::sync::atomic::AtomicUsize; fn spawn_on_thread(fut: F) -> ProcHandle where @@ -29,8 +28,12 @@ where schedule, ProcStack::default() .with_pid(1) - .with_before_start(|| { println!("Before start"); }) - .with_after_complete(|| { println!("After complete"); }) + .with_before_start(|| { + println!("Before start"); + }) + .with_after_complete(|| { + println!("After complete"); + }), ); proc.schedule(); diff --git a/lightproc/src/proc_stack.rs b/lightproc/src/proc_stack.rs index 04694888..c1e02fb9 100644 --- a/lightproc/src/proc_stack.rs +++ b/lightproc/src/proc_stack.rs @@ -24,7 +24,7 @@ impl ProcStack { pub fn with_before_start(mut self, callback: T) -> Self where - T: Fn() + Send + Sync + 'static + T: Fn() + Send + Sync + 'static, { self.before_start = Some(Arc::new(callback)); self @@ -32,7 +32,7 @@ impl ProcStack { pub fn with_after_complete(mut self, callback: T) -> Self where - T: Fn() + Send + Sync + 'static + T: Fn() + Send + Sync + 'static, { self.after_complete = Some(Arc::new(callback)); self @@ -40,7 +40,7 @@ impl ProcStack { pub fn with_after_panic(mut self, callback: T) -> Self where - T: Fn() + Send + Sync + 'static + T: Fn() + Send + Sync + 'static, { self.after_panic = Some(Arc::new(callback)); self From 7321b6deb42eeb97153c2a4e902af7bee15ae869 Mon Sep 17 00:00:00 2001 From: Mahmut Bulut Date: Thu, 24 Oct 2019 21:10:29 +0200 Subject: [PATCH 23/38] Add categories --- lightproc/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lightproc/Cargo.toml b/lightproc/Cargo.toml index 8c2fb714..ff6b245b 100644 --- a/lightproc/Cargo.toml +++ b/lightproc/Cargo.toml @@ -3,8 +3,8 @@ name = "lightproc" version = "0.2.1-alpha.0" description = "Lightweight process abstraction for Rust" authors = ["Mahmut Bulut "] -keywords = ["fault-tolerant", "runtime", "actor", "system"] -categories = [] +keywords = ["fault-tolerant", "runtime", "actor", "system", "lightweight-process"] +categories = ["concurrency", "asynchronous"] edition = "2018" [dependencies] From a6e7e9f82c353b7d91c40e9bebea6cfc0d7288d9 Mon Sep 17 00:00:00 2001 From: Mahmut Bulut Date: Thu, 24 Oct 2019 21:53:06 +0200 Subject: [PATCH 24/38] Expose organization --- lightproc/src/lib.rs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/lightproc/src/lib.rs b/lightproc/src/lib.rs index 00b51c6d..be0c1b14 100644 --- a/lightproc/src/lib.rs +++ b/lightproc/src/lib.rs @@ -1,18 +1,20 @@ -pub mod catch_unwind; -pub mod layout_helpers; +mod catch_unwind; +mod layout_helpers; +mod proc_data; +mod proc_ext; +mod proc_layout; +mod proc_vtable; +mod raw_proc; +mod state; + pub mod lightproc; -pub mod proc_data; -pub mod proc_ext; pub mod proc_handle; -pub mod proc_layout; -pub mod proc_stack; -pub mod proc_vtable; -pub mod raw_proc; pub mod recoverable_handle; -pub mod state; +pub mod proc_stack; pub mod prelude { pub use crate::lightproc::*; pub use crate::proc_handle::*; + pub use crate::recoverable_handle::*; pub use crate::proc_stack::*; } From 9e29a09872f4e6b8f39625679b33ffc3f1b78fc6 Mon Sep 17 00:00:00 2001 From: Mahmut Bulut Date: Thu, 24 Oct 2019 21:53:34 +0200 Subject: [PATCH 25/38] Add bastion categories too --- bastion/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bastion/Cargo.toml b/bastion/Cargo.toml index 18173c71..ae2c702c 100644 --- a/bastion/Cargo.toml +++ b/bastion/Cargo.toml @@ -4,7 +4,7 @@ version = "0.2.1-alpha.0" description = "Fault-tolerant Runtime for Rust applications" authors = ["Mahmut Bulut "] keywords = ["fault-tolerant", "runtime", "actor", "system"] -categories = [] +categories = ["concurrency", "asynchronous"] homepage = "https://github.com/bastion-rs/bastion" repository = "https://github.com/bastion-rs/bastion" documentation = "https://docs.rs/bastion" From a9042b28bfe24490ab42889385dcbe5842901b82 Mon Sep 17 00:00:00 2001 From: Mahmut Bulut Date: Thu, 24 Oct 2019 22:23:14 +0200 Subject: [PATCH 26/38] Update lightproc/Cargo.toml Co-Authored-By: Matthieu Le brazidec --- lightproc/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lightproc/Cargo.toml b/lightproc/Cargo.toml index ff6b245b..2df8de74 100644 --- a/lightproc/Cargo.toml +++ b/lightproc/Cargo.toml @@ -13,5 +13,5 @@ pin-utils = "0.1.0-alpha.4" [dev-dependencies] crossbeam = "0.7.1" -futures-preview = "0.3.0-alpha.17" +futures-preview = "=0.3.0-alpha.19" lazy_static = "1.3.0" From 3fb375a4b6f7592bf283273b35f033df9d2ac584 Mon Sep 17 00:00:00 2001 From: Mahmut Bulut Date: Thu, 24 Oct 2019 22:24:52 +0200 Subject: [PATCH 27/38] Import reorganization --- lightproc/examples/proc_panic.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lightproc/examples/proc_panic.rs b/lightproc/examples/proc_panic.rs index ea984cbf..b389fb43 100644 --- a/lightproc/examples/proc_panic.rs +++ b/lightproc/examples/proc_panic.rs @@ -1,13 +1,10 @@ -use std::future::Future; - -use std::thread; - use crossbeam::channel::{unbounded, Sender}; use futures::executor; use lazy_static::lazy_static; use lightproc::prelude::*; +use std::future::Future; +use std::thread; -use lightproc::recoverable_handle::RecoverableHandle; fn spawn_on_thread(future: F) -> RecoverableHandle where From 3f4fae0ff968e0610a4df68573d441fe12014d41 Mon Sep 17 00:00:00 2001 From: Mahmut Bulut Date: Thu, 24 Oct 2019 22:26:51 +0200 Subject: [PATCH 28/38] Expose catch_unwind --- lightproc/src/lib.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lightproc/src/lib.rs b/lightproc/src/lib.rs index be0c1b14..4bc1160c 100644 --- a/lightproc/src/lib.rs +++ b/lightproc/src/lib.rs @@ -1,20 +1,22 @@ -mod catch_unwind; mod layout_helpers; mod proc_data; -mod proc_ext; mod proc_layout; mod proc_vtable; mod raw_proc; mod state; pub mod lightproc; +pub mod catch_unwind; pub mod proc_handle; -pub mod recoverable_handle; +pub mod proc_ext; pub mod proc_stack; +pub mod recoverable_handle; pub mod prelude { pub use crate::lightproc::*; + pub use crate::catch_unwind::*; + pub use crate::proc_ext::*; pub use crate::proc_handle::*; - pub use crate::recoverable_handle::*; pub use crate::proc_stack::*; + pub use crate::recoverable_handle::*; } From 9c19e6dff71c4f0fb1ba905433c2debacfd09786 Mon Sep 17 00:00:00 2001 From: Mahmut Bulut Date: Thu, 24 Oct 2019 22:28:00 +0200 Subject: [PATCH 29/38] Update lightproc/src/layout_helpers.rs Co-Authored-By: Matthieu Le brazidec --- lightproc/src/layout_helpers.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lightproc/src/layout_helpers.rs b/lightproc/src/layout_helpers.rs index 07d668b7..63eb6414 100644 --- a/lightproc/src/layout_helpers.rs +++ b/lightproc/src/layout_helpers.rs @@ -24,7 +24,7 @@ pub fn extend(layout: Layout, next: Layout) -> (Layout, usize) { } #[inline] -pub fn padding_needed_for(layout: Layout, align: usize) -> usize { +pub(crate) fn padding_needed_for(layout: Layout, align: usize) -> usize { let len = layout.size(); let len_rounded_up = len.wrapping_add(align).wrapping_sub(1) & !align.wrapping_sub(1); len_rounded_up.wrapping_sub(len) From a758aead112f9c1891078925b5f58d02e2055faa Mon Sep 17 00:00:00 2001 From: Mahmut Bulut Date: Thu, 24 Oct 2019 22:28:41 +0200 Subject: [PATCH 30/38] Example organization --- lightproc/examples/proc_run.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/lightproc/examples/proc_run.rs b/lightproc/examples/proc_run.rs index f0230746..b87c4d10 100644 --- a/lightproc/examples/proc_run.rs +++ b/lightproc/examples/proc_run.rs @@ -1,12 +1,9 @@ -//! A function that runs a future to completion on a dedicated thread. - -use std::future::Future; -use std::sync::Arc; -use std::thread; - use crossbeam::channel; use futures::executor; use lightproc::prelude::*; +use std::future::Future; +use std::sync::Arc; +use std::thread; fn spawn_on_thread(fut: F) -> ProcHandle where From b793f7400a295952de6e6ea64032b6dc82574ee0 Mon Sep 17 00:00:00 2001 From: Mahmut Bulut Date: Thu, 24 Oct 2019 22:29:23 +0200 Subject: [PATCH 31/38] Update lightproc/src/layout_helpers.rs Co-Authored-By: Matthieu Le brazidec --- lightproc/src/layout_helpers.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lightproc/src/layout_helpers.rs b/lightproc/src/layout_helpers.rs index 63eb6414..feb29761 100644 --- a/lightproc/src/layout_helpers.rs +++ b/lightproc/src/layout_helpers.rs @@ -2,7 +2,7 @@ use std::alloc::Layout; use std::io::{Error, ErrorKind}; #[inline] -pub fn extend(layout: Layout, next: Layout) -> (Layout, usize) { +pub(crate) fn extend(layout: Layout, next: Layout) -> (Layout, usize) { let new_align = std::cmp::max(layout.align(), next.align()); let pad = padding_needed_for(layout, next.align()); From dcb9bc7ac74b46d3aab51b969531f4d078e815b2 Mon Sep 17 00:00:00 2001 From: Mahmut Bulut Date: Thu, 24 Oct 2019 22:31:27 +0200 Subject: [PATCH 32/38] Update lightproc/src/raw_proc.rs Co-Authored-By: Matthieu Le brazidec --- lightproc/src/raw_proc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lightproc/src/raw_proc.rs b/lightproc/src/raw_proc.rs index 1b54a1b3..8efb46a8 100644 --- a/lightproc/src/raw_proc.rs +++ b/lightproc/src/raw_proc.rs @@ -18,7 +18,7 @@ use crate::state::*; use std::panic::AssertUnwindSafe; -pub type ProcFuture = CatchUnwind>; +pub(crate) type ProcFuture = CatchUnwind>; /// Raw pointers to the fields of a task. pub(crate) struct RawProc { From 523525e22845a628cf63bb0bfd959dbab18b1f35 Mon Sep 17 00:00:00 2001 From: Mahmut Bulut Date: Thu, 24 Oct 2019 22:32:12 +0200 Subject: [PATCH 33/38] Update lightproc/Cargo.toml Co-Authored-By: Matthieu Le brazidec --- lightproc/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lightproc/Cargo.toml b/lightproc/Cargo.toml index 2df8de74..cf680815 100644 --- a/lightproc/Cargo.toml +++ b/lightproc/Cargo.toml @@ -12,6 +12,6 @@ crossbeam-utils = "0.6.6" pin-utils = "0.1.0-alpha.4" [dev-dependencies] -crossbeam = "0.7.1" +crossbeam = "0.7" futures-preview = "=0.3.0-alpha.19" lazy_static = "1.3.0" From 30dfd4d932a40fb44bdb9e7e4c99334a51589e2c Mon Sep 17 00:00:00 2001 From: Mahmut Bulut Date: Thu, 24 Oct 2019 22:32:20 +0200 Subject: [PATCH 34/38] Update lightproc/Cargo.toml Co-Authored-By: Matthieu Le brazidec --- lightproc/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lightproc/Cargo.toml b/lightproc/Cargo.toml index cf680815..411593e7 100644 --- a/lightproc/Cargo.toml +++ b/lightproc/Cargo.toml @@ -8,7 +8,7 @@ categories = ["concurrency", "asynchronous"] edition = "2018" [dependencies] -crossbeam-utils = "0.6.6" +crossbeam-utils = "0.6" pin-utils = "0.1.0-alpha.4" [dev-dependencies] From 5be1840bf69e38cf63c21a3fc45402db4eb1161b Mon Sep 17 00:00:00 2001 From: Mahmut Bulut Date: Thu, 24 Oct 2019 22:32:27 +0200 Subject: [PATCH 35/38] Update lightproc/Cargo.toml Co-Authored-By: Matthieu Le brazidec --- lightproc/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lightproc/Cargo.toml b/lightproc/Cargo.toml index 411593e7..3673dc84 100644 --- a/lightproc/Cargo.toml +++ b/lightproc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lightproc" -version = "0.2.1-alpha.0" +version = "0.3.0-alpha.0" description = "Lightweight process abstraction for Rust" authors = ["Mahmut Bulut "] keywords = ["fault-tolerant", "runtime", "actor", "system", "lightweight-process"] From b9b8e1cd1bd41b1e24ed83fe1b48e745667402e8 Mon Sep 17 00:00:00 2001 From: Mahmut Bulut Date: Thu, 24 Oct 2019 22:41:41 +0200 Subject: [PATCH 36/38] Remove global proc_future type --- lightproc/src/raw_proc.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lightproc/src/raw_proc.rs b/lightproc/src/raw_proc.rs index 8efb46a8..c45831d3 100644 --- a/lightproc/src/raw_proc.rs +++ b/lightproc/src/raw_proc.rs @@ -18,8 +18,6 @@ use crate::state::*; use std::panic::AssertUnwindSafe; -pub(crate) type ProcFuture = CatchUnwind>; - /// Raw pointers to the fields of a task. pub(crate) struct RawProc { pub(crate) pdata: *const ProcData, @@ -121,7 +119,7 @@ where let layout_pdata = Layout::new::(); let layout_t = Layout::new::(); let layout_s = Layout::new::(); - let layout_f = Layout::new::>(); + let layout_f = Layout::new::>>(); let layout_r = Layout::new::(); let size_union = layout_f.size().max(layout_r.size()); From 36d1ace381dddfc08814565cb84879023068f18f Mon Sep 17 00:00:00 2001 From: Mahmut Bulut Date: Thu, 24 Oct 2019 22:42:13 +0200 Subject: [PATCH 37/38] Order imports --- lightproc/examples/proc_panic.rs | 1 - lightproc/src/lib.rs | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/lightproc/examples/proc_panic.rs b/lightproc/examples/proc_panic.rs index b389fb43..6aa4b9c9 100644 --- a/lightproc/examples/proc_panic.rs +++ b/lightproc/examples/proc_panic.rs @@ -5,7 +5,6 @@ use lightproc::prelude::*; use std::future::Future; use std::thread; - fn spawn_on_thread(future: F) -> RecoverableHandle where F: Future + Send + 'static, diff --git a/lightproc/src/lib.rs b/lightproc/src/lib.rs index 4bc1160c..1c902c95 100644 --- a/lightproc/src/lib.rs +++ b/lightproc/src/lib.rs @@ -5,16 +5,16 @@ mod proc_vtable; mod raw_proc; mod state; -pub mod lightproc; pub mod catch_unwind; -pub mod proc_handle; +pub mod lightproc; pub mod proc_ext; +pub mod proc_handle; pub mod proc_stack; pub mod recoverable_handle; pub mod prelude { - pub use crate::lightproc::*; pub use crate::catch_unwind::*; + pub use crate::lightproc::*; pub use crate::proc_ext::*; pub use crate::proc_handle::*; pub use crate::proc_stack::*; From 7049d701e0cbcd319941e36ba413440fa01e412e Mon Sep 17 00:00:00 2001 From: Mahmut Bulut Date: Thu, 24 Oct 2019 22:47:51 +0200 Subject: [PATCH 38/38] Rename task naming to proc --- lightproc/examples/proc_panic.rs | 2 +- lightproc/src/lightproc.rs | 16 +-- lightproc/src/proc_data.rs | 28 ++--- lightproc/src/proc_handle.rs | 68 ++++++------ lightproc/src/proc_layout.rs | 10 +- lightproc/src/proc_vtable.rs | 12 +-- lightproc/src/raw_proc.rs | 172 +++++++++++++++---------------- lightproc/src/state.rs | 52 +++++----- 8 files changed, 180 insertions(+), 180 deletions(-) diff --git a/lightproc/examples/proc_panic.rs b/lightproc/examples/proc_panic.rs index 6aa4b9c9..f9d1164e 100644 --- a/lightproc/examples/proc_panic.rs +++ b/lightproc/examples/proc_panic.rs @@ -11,7 +11,7 @@ where R: Send + 'static, { lazy_static! { - // A channel that holds scheduled tasks. + // A channel that holds scheduled procs. static ref QUEUE: Sender = { let (sender, receiver) = unbounded::(); diff --git a/lightproc/src/lightproc.rs b/lightproc/src/lightproc.rs index b4a1113d..9693b34e 100644 --- a/lightproc/src/lightproc.rs +++ b/lightproc/src/lightproc.rs @@ -13,7 +13,7 @@ use crate::recoverable_handle::RecoverableHandle; use std::panic::AssertUnwindSafe; pub struct LightProc { - /// A pointer to the heap-allocated task. + /// A pointer to the heap-allocated proc. pub(crate) raw_proc: NonNull<()>, } @@ -42,13 +42,13 @@ impl LightProc { R: Send + 'static, S: Fn(LightProc) + Send + Sync + 'static, { - let raw_task = RawProc::allocate(stack, future, schedule); - let task = LightProc { raw_proc: raw_task }; + let raw_proc = RawProc::allocate(stack, future, schedule); + let proc = LightProc { raw_proc: raw_proc }; let handle = ProcHandle { - raw_proc: raw_task, + raw_proc: raw_proc, _marker: PhantomData, }; - (task, handle) + (proc, handle) } pub fn schedule(self) { @@ -97,13 +97,13 @@ impl Drop for LightProc { let pdata = ptr as *const ProcData; unsafe { - // Cancel the task. + // Cancel the proc. (*pdata).cancel(); // Drop the future. ((*pdata).vtable.drop_future)(ptr); - // Drop the task reference. + // Drop the proc reference. ((*pdata).vtable.decrement)(ptr); } } @@ -114,7 +114,7 @@ impl fmt::Debug for LightProc { let ptr = self.raw_proc.as_ptr(); let pdata = ptr as *const ProcData; - f.debug_struct("Task") + f.debug_struct("LightProc") .field("pdata", unsafe { &(*pdata) }) .field("stack", self.stack()) .finish() diff --git a/lightproc/src/proc_data.rs b/lightproc/src/proc_data.rs index a52d5a4d..42bdde1b 100644 --- a/lightproc/src/proc_data.rs +++ b/lightproc/src/proc_data.rs @@ -11,42 +11,42 @@ use crate::proc_stack::*; use crate::proc_vtable::ProcVTable; use crate::state::*; -/// The pdata of a task. +/// The pdata of a proc. /// -/// This pdata is stored right at the beginning of every heap-allocated task. +/// This pdata is stored right at the beginning of every heap-allocated proc. pub(crate) struct ProcData { - /// Current state of the task. + /// Current state of the proc. /// /// Contains flags representing the current state and the reference count. pub(crate) state: AtomicUsize, - /// The task that is blocked on the `JoinHandle`. + /// The proc that is blocked on the `ProcHandle`. /// - /// This waker needs to be woken once the task completes or is closed. + /// This waker needs to be woken once the proc completes or is closed. pub(crate) awaiter: Cell>, /// The virtual table. /// /// In addition to the actual waker virtual table, it also contains pointers to several other - /// methods necessary for bookkeeping the heap-allocated task. + /// methods necessary for bookkeeping the heap-allocated proc. pub(crate) vtable: &'static ProcVTable, } impl ProcData { - /// Cancels the task. + /// Cancels the proc. /// - /// This method will only mark the task as closed and will notify the awaiter, but it won't - /// reschedule the task if it's not completed. + /// This method will only mark the proc as closed and will notify the awaiter, but it won't + /// reschedule the proc if it's not completed. pub(crate) fn cancel(&self) { let mut state = self.state.load(Ordering::Acquire); loop { - // If the task has been completed or closed, it can't be cancelled. + // If the proc has been completed or closed, it can't be cancelled. if state & (COMPLETED | CLOSED) != 0 { break; } - // Mark the task as closed. + // Mark the proc as closed. match self.state.compare_exchange_weak( state, state | CLOSED, @@ -54,7 +54,7 @@ impl ProcData { Ordering::Acquire, ) { Ok(_) => { - // Notify the awaiter that the task has been closed. + // Notify the awaiter that the proc has been closed. if state & AWAITER != 0 { self.notify(); } @@ -66,7 +66,7 @@ impl ProcData { } } - /// Notifies the task blocked on the task. + /// Notifies the proc blocked on the proc. /// /// If there is a registered waker, it will be removed from the pdata and woken. #[inline] @@ -77,7 +77,7 @@ impl ProcData { } } - /// Notifies the task blocked on the task unless its waker matches `current`. + /// Notifies the proc blocked on the proc unless its waker matches `current`. /// /// If there is a registered waker, it will be removed from the pdata. #[inline] diff --git a/lightproc/src/proc_handle.rs b/lightproc/src/proc_handle.rs index 915ec72e..2327185a 100644 --- a/lightproc/src/proc_handle.rs +++ b/lightproc/src/proc_handle.rs @@ -10,14 +10,14 @@ use crate::proc_data::ProcData; use crate::proc_stack::*; use crate::state::*; -/// A handle that awaits the result of a task. +/// A handle that awaits the result of a proc. /// /// This type is a future that resolves to an `Option` where: /// -/// * `None` indicates the task has panicked or was cancelled -/// * `Some(res)` indicates the task has completed with `res` +/// * `None` indicates the proc has panicked or was cancelled +/// * `Some(res)` indicates the proc has completed with `res` pub struct ProcHandle { - /// A raw task pointer. + /// A raw proc pointer. pub(crate) raw_proc: NonNull<()>, /// A marker capturing the generic type `R`. @@ -30,11 +30,11 @@ unsafe impl Sync for ProcHandle {} impl Unpin for ProcHandle {} impl ProcHandle { - /// Cancels the task. + /// Cancels the proc. /// - /// If the task has already completed, calling this method will have no effect. + /// If the proc has already completed, calling this method will have no effect. /// - /// When a task is cancelled, its future cannot be polled again and will be dropped instead. + /// When a proc is cancelled, its future cannot be polled again and will be dropped instead. pub fn cancel(&self) { let ptr = self.raw_proc.as_ptr(); let pdata = ptr as *const ProcData; @@ -43,19 +43,19 @@ impl ProcHandle { let mut state = (*pdata).state.load(Ordering::Acquire); loop { - // If the task has been completed or closed, it can't be cancelled. + // If the proc has been completed or closed, it can't be cancelled. if state & (COMPLETED | CLOSED) != 0 { break; } - // If the task is not scheduled nor running, we'll need to schedule it. + // If the proc is not scheduled nor running, we'll need to schedule it. let new = if state & (SCHEDULED | RUNNING) == 0 { (state | SCHEDULED | CLOSED) + REFERENCE } else { state | CLOSED }; - // Mark the task as closed. + // Mark the proc as closed. match (*pdata).state.compare_exchange_weak( state, new, @@ -63,13 +63,13 @@ impl ProcHandle { Ordering::Acquire, ) { Ok(_) => { - // If the task is not scheduled nor running, schedule it so that its future + // If the proc is not scheduled nor running, schedule it so that its future // gets dropped by the executor. if state & (SCHEDULED | RUNNING) == 0 { ((*pdata).vtable.schedule)(ptr); } - // Notify the awaiter that the task has been closed. + // Notify the awaiter that the proc has been closed. if state & AWAITER != 0 { (*pdata).notify(); } @@ -82,7 +82,7 @@ impl ProcHandle { } } - /// Returns a reference to the stack stored inside the task. + /// Returns a reference to the stack stored inside the proc. pub fn stack(&self) -> &ProcStack { let offset = ProcData::offset_stack(); let ptr = self.raw_proc.as_ptr(); @@ -103,8 +103,8 @@ impl Drop for ProcHandle { let mut output = None; unsafe { - // Optimistically assume the `JoinHandle` is being dropped just after creating the - // task. This is a common case so if the handle is not used, the overhead of it is only + // Optimistically assume the `ProcHandle` is being dropped just after creating the + // proc. This is a common case so if the handle is not used, the overhead of it is only // one compare-exchange operation. if let Err(mut state) = (*pdata).state.compare_exchange_weak( SCHEDULED | HANDLE | REFERENCE, @@ -113,10 +113,10 @@ impl Drop for ProcHandle { Ordering::Acquire, ) { loop { - // If the task has been completed but not yet closed, that means its output + // If the proc has been completed but not yet closed, that means its output // must be dropped. if state & COMPLETED != 0 && state & CLOSED == 0 { - // Mark the task as closed in order to grab its output. + // Mark the proc as closed in order to grab its output. match (*pdata).state.compare_exchange_weak( state, state | CLOSED, @@ -133,7 +133,7 @@ impl Drop for ProcHandle { Err(s) => state = s, } } else { - // If this is the last reference to the task and it's not closed, then + // If this is the last reference to the proc and it's not closed, then // close it and schedule one more time so that its future gets dropped by // the executor. let new = if state & (!(REFERENCE - 1) | CLOSED) == 0 { @@ -150,7 +150,7 @@ impl Drop for ProcHandle { Ordering::Acquire, ) { Ok(_) => { - // If this is the last reference to the task, we need to either + // If this is the last reference to the proc, we need to either // schedule dropping its future or destroy it. if state & !(REFERENCE - 1) == 0 { if state & CLOSED == 0 { @@ -169,7 +169,7 @@ impl Drop for ProcHandle { } } - // Drop the output if it was taken out of the task. + // Drop the output if it was taken out of the proc. drop(output); } } @@ -185,39 +185,39 @@ impl Future for ProcHandle { let mut state = (*pdata).state.load(Ordering::Acquire); loop { - // If the task has been closed, notify the awaiter and return `None`. + // If the proc has been closed, notify the awaiter and return `None`. if state & CLOSED != 0 { - // Even though the awaiter is most likely the current task, it could also be - // another task. + // Even though the awaiter is most likely the current proc, it could also be + // another proc. (*pdata).notify_unless(cx.waker()); return Poll::Ready(None); } - // If the task is not completed, register the current task. + // If the proc is not completed, register the current proc. if state & COMPLETED == 0 { - // Replace the waker with one associated with the current task. We need a + // Replace the waker with one associated with the current proc. We need a // safeguard against panics because dropping the previous waker can panic. (*pdata).swap_awaiter(Some(cx.waker().clone())); - // Reload the state after registering. It is possible that the task became + // Reload the state after registering. It is possible that the proc became // completed or closed just before registration so we need to check for that. state = (*pdata).state.load(Ordering::Acquire); - // If the task has been closed, notify the awaiter and return `None`. + // If the proc has been closed, notify the awaiter and return `None`. if state & CLOSED != 0 { - // Even though the awaiter is most likely the current task, it could also - // be another task. + // Even though the awaiter is most likely the current proc, it could also + // be another proc. (*pdata).notify_unless(cx.waker()); return Poll::Ready(None); } - // If the task is still not completed, we're blocked on it. + // If the proc is still not completed, we're blocked on it. if state & COMPLETED == 0 { return Poll::Pending; } } - // Since the task is now completed, mark it as closed in order to grab its output. + // Since the proc is now completed, mark it as closed in order to grab its output. match (*pdata).state.compare_exchange( state, state | CLOSED, @@ -226,12 +226,12 @@ impl Future for ProcHandle { ) { Ok(_) => { // Notify the awaiter. Even though the awaiter is most likely the current - // task, it could also be another task. + // proc, it could also be another proc. if state & AWAITER != 0 { (*pdata).notify_unless(cx.waker()); } - // Take the output from the task. + // Take the output from the proc. let output = ((*pdata).vtable.get_output)(ptr) as *mut R; return Poll::Ready(Some(output.read())); } @@ -247,7 +247,7 @@ impl fmt::Debug for ProcHandle { let ptr = self.raw_proc.as_ptr(); let pdata = ptr as *const ProcData; - f.debug_struct("JoinHandle") + f.debug_struct("ProcHandle") .field("pdata", unsafe { &(*pdata) }) .finish() } diff --git a/lightproc/src/proc_layout.rs b/lightproc/src/proc_layout.rs index d51b36d0..df719784 100644 --- a/lightproc/src/proc_layout.rs +++ b/lightproc/src/proc_layout.rs @@ -2,18 +2,18 @@ use std::alloc::Layout; #[derive(Clone, Copy)] pub(crate) struct TaskLayout { - /// Memory layout of the whole task. + /// Memory layout of the whole proc. pub(crate) layout: Layout, - /// Offset into the task at which the stack is stored. + /// Offset into the proc at which the stack is stored. pub(crate) offset_t: usize, - /// Offset into the task at which the schedule function is stored. + /// Offset into the proc at which the schedule function is stored. pub(crate) offset_s: usize, - /// Offset into the task at which the future is stored. + /// Offset into the proc at which the future is stored. pub(crate) offset_f: usize, - /// Offset into the task at which the output is stored. + /// Offset into the proc at which the output is stored. pub(crate) offset_r: usize, } diff --git a/lightproc/src/proc_vtable.rs b/lightproc/src/proc_vtable.rs index ecf1231c..1dea6719 100644 --- a/lightproc/src/proc_vtable.rs +++ b/lightproc/src/proc_vtable.rs @@ -1,25 +1,25 @@ use std::task::RawWakerVTable; -/// The vtable for a task. +/// The vtable for a proc. pub(crate) struct ProcVTable { /// The raw waker vtable. pub(crate) raw_waker: RawWakerVTable, - /// Schedules the task. + /// Schedules the proc. pub(crate) schedule: unsafe fn(*const ()), - /// Drops the future inside the task. + /// Drops the future inside the proc. pub(crate) drop_future: unsafe fn(*const ()), /// Returns a pointer to the output stored after completion. pub(crate) get_output: unsafe fn(*const ()) -> *const (), - /// Drops a waker or a task. + /// Drops a waker or a proc. pub(crate) decrement: unsafe fn(ptr: *const ()), - /// Destroys the task. + /// Destroys the proc. pub(crate) destroy: unsafe fn(*const ()), - /// Runs the task. + /// Runs the proc. pub(crate) run: unsafe fn(*const ()), } diff --git a/lightproc/src/raw_proc.rs b/lightproc/src/raw_proc.rs index c45831d3..8364cf85 100644 --- a/lightproc/src/raw_proc.rs +++ b/lightproc/src/raw_proc.rs @@ -18,7 +18,7 @@ use crate::state::*; use std::panic::AssertUnwindSafe; -/// Raw pointers to the fields of a task. +/// Raw pointers to the fields of a proc. pub(crate) struct RawProc { pub(crate) pdata: *const ProcData, pub(crate) schedule: *const S, @@ -47,23 +47,23 @@ where R: Send + 'static, S: Fn(LightProc) + Send + Sync + 'static, { - /// Allocates a task with the given `future` and `schedule` function. + /// Allocates a proc with the given `future` and `schedule` function. /// - /// It is assumed there are initially only the `Task` reference and the `JoinHandle`. + /// It is assumed there are initially only the `LightProc` reference and the `ProcHandle`. pub(crate) fn allocate(stack: ProcStack, future: F, schedule: S) -> NonNull<()> { - // Compute the layout of the task for allocation. Abort if the computation fails. - let task_layout = Self::task_layout(); + // Compute the layout of the proc for allocation. Abort if the computation fails. + let proc_layout = Self::proc_layout(); unsafe { - // Allocate enough space for the entire task. - let raw_task = match NonNull::new(alloc::alloc(task_layout.layout) as *mut ()) { + // Allocate enough space for the entire proc. + let raw_proc = match NonNull::new(alloc::alloc(proc_layout.layout) as *mut ()) { None => std::process::abort(), Some(p) => p, }; - let raw = Self::from_ptr(raw_task.as_ptr()); + let raw = Self::from_ptr(raw_proc.as_ptr()); - // Write the pdata as the first field of the task. + // Write the pdata as the first field of the proc. (raw.pdata as *mut ProcData).write(ProcData { state: AtomicUsize::new(SCHEDULED | HANDLE | REFERENCE), awaiter: Cell::new(None), @@ -83,39 +83,39 @@ where }, }); - // Write the stack as the second field of the task. + // Write the stack as the second field of the proc. (raw.stack as *mut ProcStack).write(stack); - // Write the schedule function as the third field of the task. + // Write the schedule function as the third field of the proc. (raw.schedule as *mut S).write(schedule); - // Write the future as the fourth field of the task. + // Write the future as the fourth field of the proc. raw.future.write(future); - raw_task + raw_proc } } - /// Creates a `RawTask` from a raw task pointer. + /// Creates a `RawProc` from a raw proc pointer. #[inline] pub(crate) fn from_ptr(ptr: *const ()) -> Self { - let task_layout = Self::task_layout(); + let proc_layout = Self::proc_layout(); let p = ptr as *const u8; unsafe { Self { pdata: p as *const ProcData, - stack: p.add(task_layout.offset_t) as *mut ProcStack, - schedule: p.add(task_layout.offset_s) as *const S, - future: p.add(task_layout.offset_f) as *mut F, - output: p.add(task_layout.offset_r) as *mut R, + stack: p.add(proc_layout.offset_t) as *mut ProcStack, + schedule: p.add(proc_layout.offset_s) as *const S, + future: p.add(proc_layout.offset_f) as *mut F, + output: p.add(proc_layout.offset_r) as *mut R, } } } - /// Returns the memory layout for a task. + /// Returns the memory layout for a proc. #[inline] - fn task_layout() -> TaskLayout { + fn proc_layout() -> TaskLayout { let layout_pdata = Layout::new::(); let layout_t = Layout::new::(); let layout_s = Layout::new::(); @@ -149,15 +149,15 @@ where let mut state = (*raw.pdata).state.load(Ordering::Acquire); loop { - // If the task is completed or closed, it can't be woken. + // If the proc is completed or closed, it can't be woken. if state & (COMPLETED | CLOSED) != 0 { // Drop the waker. Self::decrement(ptr); break; } - // If the task is already scheduled, we just need to synchronize with the thread that - // will run the task by "publishing" our current view of the memory. + // If the proc is already scheduled, we just need to synchronize with the thread that + // will run the proc by "publishing" our current view of the memory. if state & SCHEDULED != 0 { // Update the state without actually modifying it. match (*raw.pdata).state.compare_exchange_weak( @@ -174,7 +174,7 @@ where Err(s) => state = s, } } else { - // Mark the task as scheduled. + // Mark the proc as scheduled. match (*raw.pdata).state.compare_exchange_weak( state, state | SCHEDULED, @@ -182,14 +182,14 @@ where Ordering::Acquire, ) { Ok(_) => { - // If the task is not yet scheduled and isn't currently running, now is the + // If the proc is not yet scheduled and isn't currently running, now is the // time to schedule it. if state & (SCHEDULED | RUNNING) == 0 { - // Schedule the task. - let task = LightProc { + // Schedule the proc. + let proc = LightProc { raw_proc: NonNull::new_unchecked(ptr as *mut ()), }; - (*raw.schedule)(task); + (*raw.schedule)(proc); } else { // Drop the waker. Self::decrement(ptr); @@ -210,13 +210,13 @@ where let mut state = (*raw.pdata).state.load(Ordering::Acquire); loop { - // If the task is completed or closed, it can't be woken. + // If the proc is completed or closed, it can't be woken. if state & (COMPLETED | CLOSED) != 0 { break; } - // If the task is already scheduled, we just need to synchronize with the thread that - // will run the task by "publishing" our current view of the memory. + // If the proc is already scheduled, we just need to synchronize with the thread that + // will run the proc by "publishing" our current view of the memory. if state & SCHEDULED != 0 { // Update the state without actually modifying it. match (*raw.pdata).state.compare_exchange_weak( @@ -229,14 +229,14 @@ where Err(s) => state = s, } } else { - // If the task is not scheduled nor running, we'll need to schedule after waking. + // If the proc is not scheduled nor running, we'll need to schedule after waking. let new = if state & (SCHEDULED | RUNNING) == 0 { (state | SCHEDULED) + REFERENCE } else { state | SCHEDULED }; - // Mark the task as scheduled. + // Mark the proc as scheduled. match (*raw.pdata).state.compare_exchange_weak( state, new, @@ -244,18 +244,18 @@ where Ordering::Acquire, ) { Ok(_) => { - // If the task is not scheduled nor running, now is the time to schedule. + // If the proc is not scheduled nor running, now is the time to schedule. if state & (SCHEDULED | RUNNING) == 0 { // If the reference count overflowed, abort. if state > isize::max_value() as usize { std::process::abort(); } - // Schedule the task. - let task = LightProc { + // Schedule the proc. + let proc = LightProc { raw_proc: NonNull::new_unchecked(ptr as *mut ()), }; - (*raw.schedule)(task); + (*raw.schedule)(proc); } break; @@ -283,10 +283,10 @@ where RawWaker::new(ptr, raw_waker) } - /// Drops a waker or a task. + /// Drops a waker or a proc. /// /// This function will decrement the reference count. If it drops down to zero and the - /// associated join handle has been dropped too, then the task gets destroyed. + /// associated join handle has been dropped too, then the proc gets destroyed. #[inline] unsafe fn decrement(ptr: *const ()) { let raw = Self::from_ptr(ptr); @@ -294,16 +294,16 @@ where // Decrement the reference count. let new = (*raw.pdata).state.fetch_sub(REFERENCE, Ordering::AcqRel) - REFERENCE; - // If this was the last reference to the task and the `JoinHandle` has been dropped as - // well, then destroy the task. + // If this was the last reference to the proc and the `ProcHandle` has been dropped as + // well, then destroy the proc. if new & !(REFERENCE - 1) == 0 && new & HANDLE == 0 { Self::destroy(ptr); } } - /// Schedules a task for running. + /// Schedules a proc for running. /// - /// This function doesn't modify the state of the task. It only passes the task reference to + /// This function doesn't modify the state of the proc. It only passes the proc reference to /// its schedule function. unsafe fn schedule(ptr: *const ()) { let raw = Self::from_ptr(ptr); @@ -313,7 +313,7 @@ where }); } - /// Drops the future inside a task. + /// Drops the future inside a proc. #[inline] unsafe fn drop_future(ptr: *const ()) { let raw = Self::from_ptr(ptr); @@ -322,20 +322,20 @@ where raw.future.drop_in_place(); } - /// Returns a pointer to the output inside a task. + /// Returns a pointer to the output inside a proc. unsafe fn get_output(ptr: *const ()) -> *const () { let raw = Self::from_ptr(ptr); raw.output as *const () } - /// Cleans up task's resources and deallocates it. + /// Cleans up proc's resources and deallocates it. /// - /// If the task has not been closed, then its future or the output will be dropped. The + /// If the proc has not been closed, then its future or the output will be dropped. The /// schedule function and the stack get dropped too. #[inline] unsafe fn destroy(ptr: *const ()) { let raw = Self::from_ptr(ptr); - let task_layout = Self::task_layout(); + let proc_layout = Self::proc_layout(); // We need a safeguard against panics because destructors can panic. // Drop the schedule function. @@ -344,18 +344,18 @@ where // Drop the stack. (raw.stack as *mut ProcStack).drop_in_place(); - // Finally, deallocate the memory reserved by the task. - alloc::dealloc(ptr as *mut u8, task_layout.layout); + // Finally, deallocate the memory reserved by the proc. + alloc::dealloc(ptr as *mut u8, proc_layout.layout); } - /// Runs a task. + /// Runs a proc. /// - /// If polling its future panics, the task will be closed and the panic propagated into the + /// If polling its future panics, the proc will be closed and the panic propagated into the /// caller. unsafe fn run(ptr: *const ()) { let raw = Self::from_ptr(ptr); - // Create a context from the raw task pointer and the vtable inside the its pdata. + // Create a context from the raw proc pointer and the vtable inside the its pdata. let waker = ManuallyDrop::new(Waker::from_raw(RawWaker::new( ptr, &(*raw.pdata).vtable.raw_waker, @@ -364,11 +364,11 @@ where let mut state = (*raw.pdata).state.load(Ordering::Acquire); - // Update the task's state before polling its future. + // Update the proc's state before polling its future. loop { - // If the task has been closed, drop the task reference and return. + // If the proc has been closed, drop the proc reference and return. if state & CLOSED != 0 { - // Notify the awaiter that the task has been closed. + // Notify the awaiter that the proc has been closed. if state & AWAITER != 0 { (*raw.pdata).notify(); } @@ -376,12 +376,12 @@ where // Drop the future. Self::drop_future(ptr); - // Drop the task reference. + // Drop the proc reference. Self::decrement(ptr); return; } - // Mark the task as unscheduled and running. + // Mark the proc as unscheduled and running. match (*raw.pdata).state.compare_exchange_weak( state, (state & !SCHEDULED) | RUNNING, @@ -397,7 +397,7 @@ where } } - // Poll the inner future, but surround it with a guard that closes the task in case polling + // Poll the inner future, but surround it with a guard that closes the proc in case polling // panics. let guard = Guard(raw); @@ -417,7 +417,7 @@ where // A place where the output will be stored in case it needs to be dropped. let mut output = None; - // The task is now completed. + // The proc is now completed. loop { // If the handle is dropped, we'll need to close it and drop the output. let new = if state & HANDLE == 0 { @@ -426,7 +426,7 @@ where (state & !RUNNING & !SCHEDULED) | COMPLETED }; - // Mark the task as not running and completed. + // Mark the proc as not running and completed. match (*raw.pdata).state.compare_exchange_weak( state, new, @@ -434,14 +434,14 @@ where Ordering::Acquire, ) { Ok(_) => { - // If the handle is dropped or if the task was closed while running, + // If the handle is dropped or if the proc was closed while running, // now it's time to drop the output. if state & HANDLE == 0 || state & CLOSED != 0 { // Read the output. output = Some(raw.output.read()); } - // Notify the awaiter that the task has been completed. + // Notify the awaiter that the proc has been completed. if state & AWAITER != 0 { (*raw.pdata).notify(); } @@ -450,7 +450,7 @@ where (*after_complete_cb.clone())(); } - // Drop the task reference. + // Drop the proc reference. Self::decrement(ptr); break; } @@ -458,13 +458,13 @@ where } } - // Drop the output if it was taken out of the task. + // Drop the output if it was taken out of the proc. drop(output); } Poll::Pending => { - // The task is still not completed. + // The proc is still not completed. loop { - // If the task was closed while running, we'll need to unschedule in case it + // If the proc was closed while running, we'll need to unschedule in case it // was woken and then clean up its resources. let new = if state & CLOSED != 0 { state & !RUNNING & !SCHEDULED @@ -472,7 +472,7 @@ where state & !RUNNING }; - // Mark the task as not running. + // Mark the proc as not running. match (*raw.pdata).state.compare_exchange_weak( state, new, @@ -480,22 +480,22 @@ where Ordering::Acquire, ) { Ok(state) => { - // If the task was closed while running, we need to drop its future. - // If the task was woken while running, we need to schedule it. - // Otherwise, we just drop the task reference. + // If the proc was closed while running, we need to drop its future. + // If the proc was woken while running, we need to schedule it. + // Otherwise, we just drop the proc reference. if state & CLOSED != 0 { - // The thread that closed the task didn't drop the future because + // The thread that closed the proc didn't drop the future because // it was running so now it's our responsibility to do so. Self::drop_future(ptr); - // Drop the task reference. + // Drop the proc reference. Self::decrement(ptr); } else if state & SCHEDULED != 0 { - // The thread that has woken the task didn't reschedule it because + // The thread that has woken the proc didn't reschedule it because // it was running so now it's our responsibility to do so. Self::schedule(ptr); } else { - // Drop the task reference. + // Drop the proc reference. Self::decrement(ptr); } break; @@ -506,7 +506,7 @@ where } } - /// A guard that closes the task if polling its future panics. + /// A guard that closes the proc if polling its future panics. struct Guard(RawProc) where F: Future + Send + 'static, @@ -527,23 +527,23 @@ where let mut state = (*raw.pdata).state.load(Ordering::Acquire); loop { - // If the task was closed while running, then unschedule it, drop its - // future, and drop the task reference. + // If the proc was closed while running, then unschedule it, drop its + // future, and drop the proc reference. if state & CLOSED != 0 { - // We still need to unschedule the task because it is possible it was + // We still need to unschedule the proc because it is possible it was // woken while running. (*raw.pdata).state.fetch_and(!SCHEDULED, Ordering::AcqRel); - // The thread that closed the task didn't drop the future because it + // The thread that closed the proc didn't drop the future because it // was running so now it's our responsibility to do so. RawProc::::drop_future(ptr); - // Drop the task reference. + // Drop the proc reference. RawProc::::decrement(ptr); break; } - // Mark the task as not running, not scheduled, and closed. + // Mark the proc as not running, not scheduled, and closed. match (*raw.pdata).state.compare_exchange_weak( state, (state & !RUNNING & !SCHEDULED) | CLOSED, @@ -551,15 +551,15 @@ where Ordering::Acquire, ) { Ok(state) => { - // Drop the future because the task is now closed. + // Drop the future because the proc is now closed. RawProc::::drop_future(ptr); - // Notify the awaiter that the task has been closed. + // Notify the awaiter that the proc has been closed. if state & AWAITER != 0 { (*raw.pdata).notify(); } - // Drop the task reference. + // Drop the proc reference. RawProc::::decrement(ptr); break; } diff --git a/lightproc/src/state.rs b/lightproc/src/state.rs index d6ce34fd..41696297 100644 --- a/lightproc/src/state.rs +++ b/lightproc/src/state.rs @@ -1,52 +1,52 @@ -/// Set if the task is scheduled for running. +/// Set if the proc is scheduled for running. /// -/// A task is considered to be scheduled whenever its `Task` reference exists. It is in scheduled -/// state at the moment of creation and when it gets unapused either by its `JoinHandle` or woken +/// A proc is considered to be scheduled whenever its `LightProc` reference exists. It is in scheduled +/// state at the moment of creation and when it gets unapused either by its `ProcHandle` or woken /// by a `Waker`. /// -/// This flag can't be set when the task is completed. However, it can be set while the task is +/// This flag can't be set when the proc is completed. However, it can be set while the proc is /// running, in which case it will be rescheduled as soon as polling finishes. pub(crate) const SCHEDULED: usize = 1 << 0; -/// Set if the task is running. +/// Set if the proc is running. /// -/// A task is running state while its future is being polled. +/// A proc is running state while its future is being polled. /// -/// This flag can't be set when the task is completed. However, it can be in scheduled state while +/// This flag can't be set when the proc is completed. However, it can be in scheduled state while /// it is running, in which case it will be rescheduled when it stops being polled. pub(crate) const RUNNING: usize = 1 << 1; -/// Set if the task has been completed. +/// Set if the proc has been completed. /// /// This flag is set when polling returns `Poll::Ready`. The output of the future is then stored -/// inside the task until it becomes stopped. In fact, `JoinHandle` picks the output up by marking -/// the task as stopped. +/// inside the proc until it becomes stopped. In fact, `ProcHandle` picks the output up by marking +/// the proc as stopped. /// -/// This flag can't be set when the task is scheduled or completed. +/// This flag can't be set when the proc is scheduled or completed. pub(crate) const COMPLETED: usize = 1 << 2; -/// Set if the task is closed. +/// Set if the proc is closed. /// -/// If a task is closed, that means its either cancelled or its output has been consumed by the -/// `JoinHandle`. A task becomes closed when: +/// If a proc is closed, that means its either cancelled or its output has been consumed by the +/// `ProcHandle`. A proc becomes closed when: /// -/// 1. It gets cancelled by `Task::cancel()` or `JoinHandle::cancel()`. -/// 2. Its output is awaited by the `JoinHandle`. +/// 1. It gets cancelled by `LightProc::cancel()` or `ProcHandle::cancel()`. +/// 2. Its output is awaited by the `ProcHandle`. /// 3. It panics while polling the future. -/// 4. It is completed and the `JoinHandle` is dropped. +/// 4. It is completed and the `ProcHandle` is dropped. pub(crate) const CLOSED: usize = 1 << 3; -/// Set if the `JoinHandle` still exists. +/// Set if the `ProcHandle` still exists. /// -/// The `JoinHandle` is a special case in that it is only tracked by this flag, while all other -/// task references (`Task` and `Waker`s) are tracked by the reference count. +/// The `ProcHandle` is a special case in that it is only tracked by this flag, while all other +/// proc references (`LightProc` and `Waker`s) are tracked by the reference count. pub(crate) const HANDLE: usize = 1 << 4; -/// Set if the `JoinHandle` is awaiting the output. +/// Set if the `ProcHandle` is awaiting the output. /// -/// This flag is set while there is a registered awaiter of type `Waker` inside the task. When the -/// task gets closed or completed, we need to wake the awaiter. This flag can be used as a fast -/// check that tells us if we need to wake anyone without acquiring the lock inside the task. +/// This flag is set while there is a registered awaiter of type `Waker` inside the proc. When the +/// proc gets closed or completed, we need to wake the awaiter. This flag can be used as a fast +/// check that tells us if we need to wake anyone without acquiring the lock inside the proc. pub(crate) const AWAITER: usize = 1 << 5; /// Set if the awaiter is locked. @@ -56,10 +56,10 @@ pub(crate) const LOCKED: usize = 1 << 6; /// A single reference. /// -/// The lower bits in the state contain various flags representing the task state, while the upper +/// The lower bits in the state contain various flags representing the proc state, while the upper /// bits contain the reference count. The value of `REFERENCE` represents a single reference in the /// total reference count. /// -/// Note that the reference counter only tracks the `Task` and `Waker`s. The `JoinHandle` is +/// Note that the reference counter only tracks the `LightProc` and `Waker`s. The `ProcHandle` is /// tracked separately by the `HANDLE` flag. pub(crate) const REFERENCE: usize = 1 << 7;