use std::fmt;
use c_void;
use stack::Stack;
extern "C" {
fn make_fcontext(sp: *mut c_void, size: usize, f: ContextFn) -> &'static c_void;
fn jump_fcontext(to: &'static c_void, p: usize) -> Transfer;
fn ontop_fcontext(to: &'static c_void, p: usize, f: ResumeOntopFn) -> Transfer;
}
pub type ContextFn = extern "C" fn(t: Transfer) -> !;
pub type ResumeOntopFn = extern "C" fn(t: Transfer) -> Transfer;
#[repr(C)]
pub struct Context(&'static c_void);
impl Context {
#[inline(always)]
pub unsafe fn new(stack: &Stack, f: ContextFn) -> Context {
Context(make_fcontext(stack.top(), stack.len(), f))
}
#[inline(always)]
pub unsafe fn resume(self, data: usize) -> Transfer {
jump_fcontext(self.0, data)
}
#[inline(always)]
pub unsafe fn resume_ontop(self, data: usize, f: ResumeOntopFn) -> Transfer {
ontop_fcontext(self.0, data, f)
}
}
impl fmt::Debug for Context {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Context({:p})", self.0)
}
}
#[repr(C)]
#[derive(Debug)]
pub struct Transfer {
pub context: Context,
pub data: usize,
}
impl Transfer {
#[inline(always)]
pub fn new(context: Context, data: usize) -> Transfer {
Transfer {
context: context,
data: data,
}
}
}
#[cfg(test)]
mod tests {
use std::mem;
use super::*;
use c_void;
use stack::ProtectedFixedSizeStack;
#[test]
fn type_sizes() {
assert_eq!(mem::size_of::<Context>(), mem::size_of::<usize>());
assert_eq!(mem::size_of::<Context>(), mem::size_of::<*const c_void>());
}
#[cfg(feature = "nightly")]
#[test]
fn stack_alignment() {
#[allow(non_camel_case_types)]
#[repr(simd)]
struct u8x16(
u8,
u8,
u8,
u8,
u8,
u8,
u8,
u8,
u8,
u8,
u8,
u8,
u8,
u8,
u8,
u8,
);
extern "C" fn context_function(t: Transfer) -> ! {
let data: [u8x16; 1] = unsafe { mem::uninitialized() };
let addr = &data as *const _ as usize;
unsafe { t.context.resume(addr % mem::align_of::<u8x16>()) };
unreachable!();
}
let stack = ProtectedFixedSizeStack::default();
let mut t = Transfer::new(unsafe { Context::new(&stack, context_function) }, 0);
t = unsafe { t.context.resume(0) };
assert_eq!(t.data, 0);
}
#[test]
fn number_generator() {
extern "C" fn context_function(mut t: Transfer) -> ! {
for i in 0usize.. {
assert_eq!(t.data, i);
t = unsafe { t.context.resume(i) };
}
unreachable!();
}
let stack = ProtectedFixedSizeStack::default();
let mut t = Transfer::new(unsafe { Context::new(&stack, context_function) }, 0);
for i in 0..10usize {
t = unsafe { t.context.resume(i) };
assert_eq!(t.data, i);
if t.data == 9 {
break;
}
}
}
#[test]
fn resume_ontop() {
extern "C" fn resume(t: Transfer) -> ! {
assert_eq!(t.data, 0);
unsafe { t.context.resume_ontop(1, resume_ontop) };
unreachable!();
}
extern "C" fn resume_ontop(mut t: Transfer) -> Transfer {
assert_eq!(t.data, 1);
t.data = 123;
t
}
let stack = ProtectedFixedSizeStack::default();
let mut t = Transfer::new(unsafe { Context::new(&stack, resume) }, 0);
t = unsafe { t.context.resume(0) };
assert_eq!(t.data, 123);
}
}