diff --git a/Cargo.toml b/Cargo.toml index 4cc041b..b956741 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,8 @@ rev = "9ef793e9549aabfd2d969615180b69d29ce28d88" simd = "0.1" [features] -default = ["valgrind"] +default = ["alloc", "valgrind"] +alloc = [] # These apply only to tests within this library; assembly at -O0 is completely # unreadable, so use -O1. diff --git a/README.md b/README.md index 4adeced..c3ce806 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,10 @@ It provides the following safe abstractions: It also provides the necessary low-level building blocks: * a trait that can be implemented by stack allocators, [Stack](https://edef1c.github.io/libfringe/fringe/struct.Stack.html); + * a wrapper for using slice references as stacks, + [SliceStack](https://edef1c.github.io/libfringe/fringe/struct.SliceStack.html); + * a stack allocator based on `Box<[u8]>`, + [OwnedStack](https://edef1c.github.io/libfringe/fringe/struct.OwnedStack.html); * a stack allocator based on anonymous memory mappings with guard pages, [OsStack](https://edef1c.github.io/libfringe/fringe/struct.OsStack.html). @@ -136,10 +140,16 @@ no-default-features = true libfringe provides some optional features through [Cargo's feature flags]. Currently, all of them are enabled by default. +#### `alloc` + +This flag enables dependency on the `alloc` crate, which is required for +the [OwnedStack](https://edef1c.github.io/libfringe/fringe/struct.OwnedStack.html). + #### `valgrind` +This flag enables [Valgrind] integration. libfringe will register context stacks with Valgrind. + [Valgrind]: http://valgrind.org -[Valgrind] integration. libfringe will register context stacks with Valgrind. ## Internals diff --git a/src/lib.rs b/src/lib.rs index 3650583..8fd6052 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,6 +5,7 @@ // http://opensource.org/licenses/MIT>, at your option. This file may not be // copied, modified, or distributed except according to those terms. #![feature(asm, naked_functions)] +#![cfg_attr(feature = "alloc", feature(alloc))] #![cfg_attr(test, feature(test, thread_local, const_fn))] #![no_std] @@ -21,6 +22,10 @@ //! //! * a trait that can be implemented by stack allocators, //! [Stack](struct.Stack.html); +//! * a wrapper for using slice references as stacks, +//! [SliceStack](struct.SliceStack.html); +//! * a stack allocator based on `Box<[u8]>`, +//! [OwnedStack](struct.OwnedStack.html); //! * a stack allocator based on anonymous memory mappings with guard pages, //! [OsStack](struct.OsStack.html). @@ -30,19 +35,25 @@ extern crate std; pub use stack::Stack; pub use stack::GuardedStack; -pub use stack::SliceStack; - +pub use slice_stack::SliceStack; pub use generator::Generator; +#[cfg(feature = "alloc")] +pub use owned_stack::OwnedStack; + #[cfg(unix)] pub use os::Stack as OsStack; mod arch; mod debug; -mod stack; mod context; +mod stack; +mod slice_stack; pub mod generator; +#[cfg(feature = "alloc")] +mod owned_stack; + #[cfg(unix)] mod os; diff --git a/src/owned_stack.rs b/src/owned_stack.rs new file mode 100644 index 0000000..95205e4 --- /dev/null +++ b/src/owned_stack.rs @@ -0,0 +1,33 @@ +// This file is part of libfringe, a low-level green threading library. +// Copyright (c) whitequark +// See the LICENSE file included in this distribution. +extern crate alloc; + +use self::alloc::raw_vec::RawVec; +use self::alloc::boxed::Box; + +/// OwnedStack allocates on heap and owns a non-guarded stack. +pub struct OwnedStack(Box<[u8]>); + +impl OwnedStack { + /// Allocates a new stack with exactly `size` accessible bytes using + /// the default Rust allocator. + pub fn new(size: usize) -> OwnedStack { + OwnedStack(unsafe { RawVec::with_capacity(size).into_box() }) + } +} + +impl ::stack::Stack for OwnedStack { + #[inline(always)] + fn base(&self) -> *mut u8 { + // The slice cannot wrap around the address space, so the conversion from usize + // to isize will not wrap either. + let len: isize = self.0.len() as isize; + unsafe { self.limit().offset(len) } + } + + #[inline(always)] + fn limit(&self) -> *mut u8 { + self.0.as_ptr() as *mut u8 + } +} diff --git a/src/slice_stack.rs b/src/slice_stack.rs new file mode 100644 index 0000000..b1e34a2 --- /dev/null +++ b/src/slice_stack.rs @@ -0,0 +1,22 @@ +// This file is part of libfringe, a low-level green threading library. +// Copyright (c) whitequark +// See the LICENSE file included in this distribution. + +/// SliceStack holds a non-guarded stack allocated elsewhere and provided as a mutable +/// slice. +pub struct SliceStack<'a>(pub &'a mut [u8]); + +impl<'a> ::stack::Stack for SliceStack<'a> { + #[inline(always)] + fn base(&self) -> *mut u8 { + // The slice cannot wrap around the address space, so the conversion from usize + // to isize will not wrap either. + let len: isize = self.0.len() as isize; + unsafe { self.limit().offset(len) } + } + + #[inline(always)] + fn limit(&self) -> *mut u8 { + self.0.as_ptr() as *mut u8 + } +} diff --git a/src/stack.rs b/src/stack.rs index 62014cb..3e6a0ae 100644 --- a/src/stack.rs +++ b/src/stack.rs @@ -4,6 +4,7 @@ // http://apache.org/licenses/LICENSE-2.0> or the MIT license , at your option. This file may not be // copied, modified, or distributed except according to those terms. +//! Traits for stacks. /// A trait for objects that hold ownership of a stack. pub trait Stack { @@ -22,22 +23,3 @@ pub trait Stack { /// A guarded stack must guarantee that any access of data at addresses `limit()` to /// `limit().offset(4096)` will abnormally terminate the program. pub unsafe trait GuardedStack {} - -/// SliceStack holds a non-guarded stack allocated elsewhere and provided as a mutable -/// slice. -pub struct SliceStack<'a>(pub &'a mut [u8]); - -impl<'a> Stack for SliceStack<'a> { - #[inline(always)] - fn base(&self) -> *mut u8 { - // The slice cannot wrap around the address space, so the conversion from usize - // to isize will not wrap either. - let len: isize = self.0.len() as isize; - unsafe { self.limit().offset(len) } - } - - #[inline(always)] - fn limit(&self) -> *mut u8 { - self.0.as_ptr() as *mut u8 - } -} diff --git a/tests/generator.rs b/tests/generator.rs index f52196b..042eb0a 100644 --- a/tests/generator.rs +++ b/tests/generator.rs @@ -6,17 +6,19 @@ // copied, modified, or distributed except according to those terms. extern crate fringe; -use fringe::{OsStack, SliceStack}; -use fringe::generator::Generator; +use fringe::{Stack, SliceStack, OwnedStack, OsStack}; +use fringe::generator::{Generator, Yielder}; + +fn add_one_fn(yielder: &mut Yielder, mut input: i32) { + loop { + if input == 0 { break } + input = yielder.suspend(input + 1) + } +} fn new_add_one() -> Generator { let stack = OsStack::new(0).unwrap(); - Generator::new(stack, move |yielder, mut input| { - loop { - if input == 0 { break } - input = yielder.suspend(input + 1) - } - }) + Generator::new(stack, add_one_fn) } #[test] @@ -67,14 +69,15 @@ fn panic_safety() { fn with_slice_stack() { let mut memory = [0; 1024]; let stack = SliceStack(&mut memory); - let mut add_one = unsafe { - Generator::unsafe_new(stack, move |yielder, mut input| { - loop { - if input == 0 { break } - input = yielder.suspend(input + 1) - } - }) - }; + let mut add_one = unsafe { Generator::unsafe_new(stack, add_one_fn) }; + assert_eq!(add_one.resume(1), Some(2)); + assert_eq!(add_one.resume(2), Some(3)); +} + +#[test] +fn with_owned_stack() { + let stack = OwnedStack::new(1024); + let mut add_one = unsafe { Generator::unsafe_new(stack, add_one_fn) }; assert_eq!(add_one.resume(1), Some(2)); assert_eq!(add_one.resume(2), Some(3)); } diff --git a/tests/stack.rs b/tests/stack.rs index 9c87d65..79c7095 100644 --- a/tests/stack.rs +++ b/tests/stack.rs @@ -6,7 +6,7 @@ // copied, modified, or distributed except according to those terms. extern crate fringe; -use fringe::{Stack, SliceStack, OsStack}; +use fringe::{Stack, SliceStack, OwnedStack, OsStack}; #[test] fn slice_stack() { @@ -15,6 +15,12 @@ fn slice_stack() { assert_eq!(stack.base() as isize - stack.limit() as isize, 1024); } +#[test] +fn owned_stack() { + let stack = OwnedStack::new(1024); + assert_eq!(stack.base() as isize - stack.limit() as isize, 1024); +} + #[test] fn default_os_stack() { let stack = OsStack::new(0).unwrap();