forked from M-Labs/libfringe
parent
1783c25c46
commit
fc4cdbf4f5
18
src/lib.rs
18
src/lib.rs
|
@ -33,17 +33,9 @@
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate std;
|
extern crate std;
|
||||||
|
|
||||||
pub use stack::Stack;
|
pub use stack::*;
|
||||||
pub use stack::GuardedStack;
|
|
||||||
pub use slice_stack::SliceStack;
|
|
||||||
pub use generator::Generator;
|
pub use generator::Generator;
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
|
||||||
pub use owned_stack::OwnedStack;
|
|
||||||
|
|
||||||
#[cfg(unix)]
|
|
||||||
pub use os::Stack as OsStack;
|
|
||||||
|
|
||||||
mod arch;
|
mod arch;
|
||||||
|
|
||||||
/// Minimum alignment of a stack base address on the target platform.
|
/// Minimum alignment of a stack base address on the target platform.
|
||||||
|
@ -51,12 +43,6 @@ pub const STACK_ALIGNMENT: usize = arch::STACK_ALIGNMENT;
|
||||||
|
|
||||||
mod debug;
|
mod debug;
|
||||||
|
|
||||||
mod stack;
|
|
||||||
mod slice_stack;
|
|
||||||
pub mod generator;
|
pub mod generator;
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
mod stack;
|
||||||
mod owned_stack;
|
|
||||||
|
|
||||||
#[cfg(unix)]
|
|
||||||
mod os;
|
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
// This file is part of libfringe, a low-level green threading library.
|
|
||||||
// Copyright (c) whitequark <whitequark@whitequark.org>
|
|
||||||
// See the LICENSE file included in this distribution.
|
|
||||||
|
|
||||||
/// SliceStack holds a non-guarded stack allocated elsewhere and provided as a mutable slice.
|
|
||||||
///
|
|
||||||
/// Any slice used in a SliceStack must adhere to the [Stack contract][contract].
|
|
||||||
/// [contract]: trait.Stack.html
|
|
||||||
#[derive(Debug)]
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -6,6 +6,19 @@
|
||||||
// copied, modified, or distributed except according to those terms.
|
// copied, modified, or distributed except according to those terms.
|
||||||
//! Traits for stacks.
|
//! Traits for stacks.
|
||||||
|
|
||||||
|
mod slice_stack;
|
||||||
|
pub use stack::slice_stack::SliceStack;
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
mod owned_stack;
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
pub use stack::owned_stack::OwnedStack;
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
mod os;
|
||||||
|
#[cfg(unix)]
|
||||||
|
pub use stack::os::OsStack;
|
||||||
|
|
||||||
/// A trait for objects that hold ownership of a stack.
|
/// A trait for objects that hold ownership of a stack.
|
||||||
///
|
///
|
||||||
/// To preserve memory safety, an implementation of this trait must fulfill
|
/// To preserve memory safety, an implementation of this trait must fulfill
|
||||||
|
@ -16,7 +29,7 @@
|
||||||
/// * Every address between the base and the limit must be readable and writable.
|
/// * Every address between the base and the limit must be readable and writable.
|
||||||
///
|
///
|
||||||
/// [align]: constant.STACK_ALIGNMENT.html
|
/// [align]: constant.STACK_ALIGNMENT.html
|
||||||
pub trait Stack {
|
pub unsafe trait Stack {
|
||||||
/// Returns the base address of the stack.
|
/// Returns the base address of the stack.
|
||||||
/// On all modern architectures, the stack grows downwards,
|
/// On all modern architectures, the stack grows downwards,
|
||||||
/// so this is the highest address.
|
/// so this is the highest address.
|
|
@ -5,27 +5,28 @@
|
||||||
// http://opensource.org/licenses/MIT>, at your option. This file may not be
|
// http://opensource.org/licenses/MIT>, at your option. This file may not be
|
||||||
// copied, modified, or distributed except according to those terms.
|
// copied, modified, or distributed except according to those terms.
|
||||||
extern crate std;
|
extern crate std;
|
||||||
|
|
||||||
use self::std::io::Error as IoError;
|
use self::std::io::Error as IoError;
|
||||||
use stack;
|
use stack::{Stack, GuardedStack};
|
||||||
|
|
||||||
mod sys;
|
mod sys;
|
||||||
|
|
||||||
/// OsStack holds a guarded stack allocated using the operating system's anonymous
|
/// OsStack holds a guarded stack allocated using the operating system's anonymous
|
||||||
/// memory mapping facility.
|
/// memory mapping facility.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Stack {
|
pub struct OsStack {
|
||||||
ptr: *mut u8,
|
ptr: *mut u8,
|
||||||
len: usize
|
len: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for Stack {}
|
unsafe impl Send for OsStack {}
|
||||||
|
|
||||||
impl Stack {
|
impl OsStack {
|
||||||
/// Allocates a new stack with at least `size` accessible bytes.
|
/// Allocates a new stack with at least `size` accessible bytes.
|
||||||
/// `size` is rounded up to an integral number of pages; `Stack::new(0)` is legal
|
/// `size` is rounded up to an integral number of pages; `OsStack::new(0)` is legal
|
||||||
/// and allocates the smallest possible stack, consisting of one data page and
|
/// and allocates the smallest possible stack, consisting of one data page and
|
||||||
/// one guard page.
|
/// one guard page.
|
||||||
pub fn new(size: usize) -> Result<Stack, IoError> {
|
pub fn new(size: usize) -> Result<OsStack, IoError> {
|
||||||
let page_size = sys::page_size();
|
let page_size = sys::page_size();
|
||||||
|
|
||||||
// Stacks have to be at least one page long.
|
// Stacks have to be at least one page long.
|
||||||
|
@ -39,9 +40,10 @@ impl Stack {
|
||||||
let len = len + page_size;
|
let len = len + page_size;
|
||||||
|
|
||||||
// Allocate a stack.
|
// Allocate a stack.
|
||||||
let stack = Stack {
|
let ptr = try!(unsafe { sys::map_stack(len) });
|
||||||
ptr: try!(unsafe { sys::map_stack(len) }),
|
let stack = OsStack {
|
||||||
len: len
|
ptr: ptr,
|
||||||
|
len: len,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Mark the guard page. If this fails, `stack` will be dropped,
|
// Mark the guard page. If this fails, `stack` will be dropped,
|
||||||
|
@ -52,7 +54,7 @@ impl Stack {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl stack::Stack for Stack {
|
unsafe impl Stack for OsStack {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn base(&self) -> *mut u8 {
|
fn base(&self) -> *mut u8 {
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -68,9 +70,9 @@ impl stack::Stack for Stack {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl stack::GuardedStack for Stack {}
|
unsafe impl GuardedStack for OsStack {}
|
||||||
|
|
||||||
impl Drop for Stack {
|
impl Drop for OsStack {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe { sys::unmap_stack(self.ptr, self.len) }.expect("cannot unmap stack")
|
unsafe { sys::unmap_stack(self.ptr, self.len) }.expect("cannot unmap stack")
|
||||||
}
|
}
|
|
@ -6,6 +6,7 @@ extern crate alloc;
|
||||||
use core::slice;
|
use core::slice;
|
||||||
use self::alloc::heap;
|
use self::alloc::heap;
|
||||||
use self::alloc::boxed::Box;
|
use self::alloc::boxed::Box;
|
||||||
|
use stack::Stack;
|
||||||
|
|
||||||
/// OwnedStack holds a non-guarded, heap-allocated stack.
|
/// OwnedStack holds a non-guarded, heap-allocated stack.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -16,18 +17,19 @@ impl OwnedStack {
|
||||||
/// for the current platform using the default Rust allocator.
|
/// for the current platform using the default Rust allocator.
|
||||||
pub fn new(size: usize) -> OwnedStack {
|
pub fn new(size: usize) -> OwnedStack {
|
||||||
unsafe {
|
unsafe {
|
||||||
let ptr = heap::allocate(size, ::STACK_ALIGNMENT);
|
let aligned_size = size & !(::STACK_ALIGNMENT - 1);
|
||||||
OwnedStack(Box::from_raw(slice::from_raw_parts_mut(ptr, size)))
|
let ptr = heap::allocate(aligned_size, ::STACK_ALIGNMENT);
|
||||||
|
OwnedStack(Box::from_raw(slice::from_raw_parts_mut(ptr, aligned_size)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ::stack::Stack for OwnedStack {
|
unsafe impl Stack for OwnedStack {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn base(&self) -> *mut u8 {
|
fn base(&self) -> *mut u8 {
|
||||||
// The slice cannot wrap around the address space, so the conversion from usize
|
// The slice cannot wrap around the address space, so the conversion from usize
|
||||||
// to isize will not wrap either.
|
// to isize will not wrap either.
|
||||||
let len: isize = self.0.len() as isize;
|
let len = self.0.len() as isize;
|
||||||
unsafe { self.limit().offset(len) }
|
unsafe { self.limit().offset(len) }
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
// This file is part of libfringe, a low-level green threading library.
|
||||||
|
// Copyright (c) whitequark <whitequark@whitequark.org>
|
||||||
|
// See the LICENSE file included in this distribution.
|
||||||
|
|
||||||
|
use stack::Stack;
|
||||||
|
|
||||||
|
/// SliceStack holds a non-guarded stack allocated elsewhere and provided as a mutable slice.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct SliceStack<'a>(&'a mut [u8]);
|
||||||
|
|
||||||
|
impl<'a> SliceStack<'a> {
|
||||||
|
/// Creates a `SliceStack` from an existing slice.
|
||||||
|
///
|
||||||
|
/// This function will automatically align the slice to make it suitable for
|
||||||
|
/// use as a stack. However this function may panic if the slice is smaller
|
||||||
|
/// than `STACK_ALIGNMENT`.
|
||||||
|
pub fn new(slice: &'a mut [u8]) -> SliceStack<'a> {
|
||||||
|
// Align the given slice so that it matches platform requirements
|
||||||
|
let ptr = slice.as_ptr() as usize;
|
||||||
|
let adjusted_ptr = (ptr + ::STACK_ALIGNMENT - 1) & !(::STACK_ALIGNMENT - 1);
|
||||||
|
let offset = adjusted_ptr - ptr;
|
||||||
|
if offset > slice.len() {
|
||||||
|
panic!("SliceStack too small");
|
||||||
|
}
|
||||||
|
|
||||||
|
let adjusted_len = (slice.len() - offset) & !(::STACK_ALIGNMENT - 1);
|
||||||
|
SliceStack(&mut slice[offset..(offset + adjusted_len)])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe 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 = self.0.len() as isize;
|
||||||
|
unsafe { self.limit().offset(len) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn limit(&self) -> *mut u8 {
|
||||||
|
self.0.as_ptr() as *mut u8
|
||||||
|
}
|
||||||
|
}
|
|
@ -69,7 +69,7 @@ fn panic_safety() {
|
||||||
#[test]
|
#[test]
|
||||||
fn with_slice_stack() {
|
fn with_slice_stack() {
|
||||||
let mut memory = [0; 1024];
|
let mut memory = [0; 1024];
|
||||||
let stack = SliceStack(&mut memory);
|
let stack = SliceStack::new(&mut memory);
|
||||||
let mut add_one = unsafe { Generator::unsafe_new(stack, add_one_fn) };
|
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(1), Some(2));
|
||||||
assert_eq!(add_one.resume(2), Some(3));
|
assert_eq!(add_one.resume(2), Some(3));
|
||||||
|
|
|
@ -4,26 +4,84 @@
|
||||||
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
|
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
|
||||||
// http://opensource.org/licenses/MIT>, at your option. This file may not be
|
// http://opensource.org/licenses/MIT>, at your option. This file may not be
|
||||||
// copied, modified, or distributed except according to those terms.
|
// copied, modified, or distributed except according to those terms.
|
||||||
|
#![feature(alloc, heap_api)]
|
||||||
|
|
||||||
|
extern crate alloc;
|
||||||
extern crate fringe;
|
extern crate fringe;
|
||||||
|
|
||||||
use fringe::{Stack, SliceStack, OwnedStack, OsStack};
|
use alloc::heap;
|
||||||
|
use alloc::boxed::Box;
|
||||||
|
use std::slice;
|
||||||
|
use fringe::{STACK_ALIGNMENT, Stack, SliceStack, OwnedStack, OsStack};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn slice_aligned() {
|
||||||
|
unsafe {
|
||||||
|
let ptr = heap::allocate(16384, STACK_ALIGNMENT);
|
||||||
|
let mut slice = Box::from_raw(slice::from_raw_parts_mut(ptr, 16384));
|
||||||
|
let stack = SliceStack::new(&mut slice[4096..8192]);
|
||||||
|
assert_eq!(stack.base() as usize & (STACK_ALIGNMENT - 1), 0);
|
||||||
|
assert_eq!(stack.limit() as usize & (STACK_ALIGNMENT - 1), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn slice_unaligned() {
|
||||||
|
unsafe {
|
||||||
|
let ptr = heap::allocate(16384, STACK_ALIGNMENT);
|
||||||
|
let mut slice = Box::from_raw(slice::from_raw_parts_mut(ptr, 16384));
|
||||||
|
let stack = SliceStack::new(&mut slice[4097..8193]);
|
||||||
|
assert_eq!(stack.base() as usize & (STACK_ALIGNMENT - 1), 0);
|
||||||
|
assert_eq!(stack.limit() as usize & (STACK_ALIGNMENT - 1), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn slice_too_small() {
|
||||||
|
unsafe {
|
||||||
|
let ptr = heap::allocate(STACK_ALIGNMENT, STACK_ALIGNMENT);
|
||||||
|
let mut slice = Box::from_raw(slice::from_raw_parts_mut(ptr, STACK_ALIGNMENT));
|
||||||
|
let stack = SliceStack::new(&mut slice[0..1]);
|
||||||
|
assert_eq!(stack.base() as usize & (STACK_ALIGNMENT - 1), 0);
|
||||||
|
assert_eq!(stack.limit() as usize & (STACK_ALIGNMENT - 1), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic="SliceStack too small"]
|
||||||
|
fn slice_too_small_unaligned() {
|
||||||
|
unsafe {
|
||||||
|
let ptr = heap::allocate(STACK_ALIGNMENT, STACK_ALIGNMENT);
|
||||||
|
let mut slice = Box::from_raw(slice::from_raw_parts_mut(ptr, STACK_ALIGNMENT));
|
||||||
|
SliceStack::new(&mut slice[1..2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn slice_stack() {
|
fn slice_stack() {
|
||||||
let mut memory = [0; 1024];
|
let mut memory = [0; 1024];
|
||||||
let stack = SliceStack(&mut memory);
|
let stack = SliceStack::new(&mut memory);
|
||||||
assert_eq!(stack.base() as isize - stack.limit() as isize, 1024);
|
assert_eq!(stack.base() as usize & (STACK_ALIGNMENT - 1), 0);
|
||||||
|
assert_eq!(stack.limit() as usize & (STACK_ALIGNMENT - 1), 0);
|
||||||
|
|
||||||
|
// Size may be a bit smaller due to alignment
|
||||||
|
assert!(stack.base() as usize - stack.limit() as usize > 1024 - STACK_ALIGNMENT * 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn owned_stack() {
|
fn owned_stack() {
|
||||||
let stack = OwnedStack::new(1024);
|
let stack = OwnedStack::new(1024);
|
||||||
assert_eq!(stack.base() as isize - stack.limit() as isize, 1024);
|
assert_eq!(stack.base() as usize & (STACK_ALIGNMENT - 1), 0);
|
||||||
|
assert_eq!(stack.limit() as usize & (STACK_ALIGNMENT - 1), 0);
|
||||||
|
assert_eq!(stack.base() as usize - stack.limit() as usize, 1024);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn default_os_stack() {
|
fn default_os_stack() {
|
||||||
let stack = OsStack::new(0).unwrap();
|
let stack = OsStack::new(0).unwrap();
|
||||||
|
assert_eq!(stack.base() as usize & (STACK_ALIGNMENT - 1), 0);
|
||||||
|
assert_eq!(stack.limit() as usize & (STACK_ALIGNMENT - 1), 0);
|
||||||
|
|
||||||
// Make sure the topmost page of the stack, at least, is accessible.
|
// Make sure the topmost page of the stack, at least, is accessible.
|
||||||
unsafe { *(stack.base().offset(-1)) = 0; }
|
unsafe { *(stack.base().offset(-1)) = 0; }
|
||||||
}
|
}
|
||||||
|
@ -31,6 +89,9 @@ fn default_os_stack() {
|
||||||
#[test]
|
#[test]
|
||||||
fn one_page_os_stack() {
|
fn one_page_os_stack() {
|
||||||
let stack = OsStack::new(4096).unwrap();
|
let stack = OsStack::new(4096).unwrap();
|
||||||
|
assert_eq!(stack.base() as usize & (STACK_ALIGNMENT - 1), 0);
|
||||||
|
assert_eq!(stack.limit() as usize & (STACK_ALIGNMENT - 1), 0);
|
||||||
|
|
||||||
// Make sure the topmost page of the stack, at least, is accessible.
|
// Make sure the topmost page of the stack, at least, is accessible.
|
||||||
unsafe { *(stack.base().offset(-1)) = 0; }
|
unsafe { *(stack.base().offset(-1)) = 0; }
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue