use detail::{Registers, initialize_call_frame, swap_registers, load_registers};
#[derive(Debug)]
pub struct Context {
regs: Registers,
}
pub type InitFn = fn(usize, *mut usize) -> !;
impl Context {
pub fn empty() -> Context {
Context { regs: Registers::new() }
}
#[allow(dead_code)]
pub fn new(init: InitFn, arg: usize, start: *mut usize, stack: *const usize) -> Context {
let mut ctx = Context::empty();
ctx.init_with(init, arg, start, stack);
ctx
}
pub fn init_with(&mut self, init: InitFn, arg: usize, start: *mut usize, stack: *const usize) {
let sp: *mut usize = stack as *mut usize;
initialize_call_frame(&mut self.regs, init, arg, start, sp);
}
#[inline(always)]
pub fn swap(out_context: &mut Context, in_context: &Context) {
debug!("swapping contexts");
let out_regs: &mut Registers = match out_context {
&mut Context { regs: ref mut r, .. } => r,
};
let in_regs: &Registers = match in_context {
&Context { regs: ref r, .. } => r,
};
debug!("register raw swap");
unsafe { swap_registers(out_regs, in_regs) }
}
#[inline(always)]
#[allow(dead_code)]
pub fn load(to_context: &Context) {
let regs: &Registers = &to_context.regs;
unsafe {
load_registers(regs);
}
}
}
#[cfg(test)]
mod test {
use std::mem::transmute;
use stack::Stack;
use reg_context::Context;
const MIN_STACK: usize = 2 * 1024 * 1024;
fn init_fn(arg: usize, f: *mut usize) -> ! {
let func: fn() = unsafe { transmute(f) };
func();
let ctx: &Context = unsafe { transmute(arg) };
Context::load(ctx);
unreachable!("Should never comeback");
}
#[test]
fn test_swap_context() {
static mut VAL: bool = false;
let mut cur = Context::empty();
fn callback() {
unsafe {
VAL = true;
}
}
let stk = Stack::new(MIN_STACK);
let ctx = Context::new(init_fn,
unsafe { transmute(&cur) },
unsafe { transmute(callback) },
stk.end());
Context::swap(&mut cur, &ctx);
unsafe {
assert!(VAL);
}
}
}