use std::mem;
use std::ptr;
use std::any::Any;
use stack::Stack;
use reg_context::RegContext;
thread_local!(static ROOT_CONTEXT: Box<Context> = {
let mut root = Box::new(Context::empty());
let p = &mut *root as *mut _;
root.parent = p; root
});
#[cfg(nightly)]
#[thread_local]
static mut ROOT_CONTEXT_P: *mut Context = ptr::null_mut();
#[allow(dead_code)]
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Error {
Cancel,
TypeErr,
StackErr,
ContextErr,
}
pub struct Context {
pub regs: RegContext,
pub stack: Stack,
pub para: *mut Any,
pub ret: *mut Any,
pub _ref: u32,
pub err: Option<Box<Any + Send>>,
pub local_data: *mut u8,
child: *mut Context,
pub parent: *mut Context,
}
impl Context {
fn empty() -> Self {
Context {
regs: RegContext::empty(),
stack: Stack::empty(),
para: unsafe { mem::uninitialized() },
ret: unsafe { mem::uninitialized() },
_ref: 1, err: None,
child: ptr::null_mut(),
parent: ptr::null_mut(),
local_data: ptr::null_mut(),
}
}
pub fn new(size: usize) -> Context {
Context {
regs: RegContext::empty(),
stack: Stack::new(size),
para: unsafe { mem::uninitialized() },
ret: unsafe { mem::uninitialized() },
_ref: 1, err: None,
child: ptr::null_mut(),
parent: ptr::null_mut(),
local_data: ptr::null_mut(),
}
}
#[inline]
pub fn is_generator(&self) -> bool {
self.parent != self as *const _ as *mut _
}
#[inline]
pub fn get_para<A>(&self) -> Option<A>
where
A: Any,
{
let para = unsafe { &mut *self.para };
match para.downcast_mut::<Option<A>>() {
Some(v) => v.take(),
None => type_error::<A>("get yield type mismatch error detected"),
}
}
#[inline]
pub fn set_ret<T>(&mut self, v: T)
where
T: Any,
{
let ret = unsafe { &mut *self.ret };
match ret.downcast_mut::<Option<T>>() {
Some(r) => *r = Some(v),
None => type_error::<T>("yield type mismatch error detected"),
}
}
}
pub struct ContextStack {
root: *mut Context,
}
#[cfg(nightly)]
#[cold]
#[inline(never)]
unsafe fn init_root_p() {
ROOT_CONTEXT_P = ROOT_CONTEXT.with(|r| &**r as *const _ as *mut Context);
}
impl ContextStack {
#[cfg(nightly)]
#[inline]
pub fn current() -> ContextStack {
unsafe {
if ROOT_CONTEXT_P.is_null() {
init_root_p();
}
ContextStack {
root: ROOT_CONTEXT_P,
}
}
}
#[cfg(not(nightly))]
#[inline]
pub fn current() -> ContextStack {
let root = ROOT_CONTEXT.with(|r| &**r as *const _ as *mut Context);
ContextStack { root: root }
}
#[inline]
pub fn top(&self) -> &'static mut Context {
let root = unsafe { &mut *self.root };
unsafe { &mut *root.parent }
}
#[inline]
pub fn co_ctx(&self) -> Option<&'static mut Context> {
let root = unsafe { &mut *self.root };
let mut ctx = unsafe { &mut *root.parent };
while ctx as *const _ != root as *const _ {
if !ctx.local_data.is_null() {
return Some(ctx);
}
ctx = unsafe { &mut *ctx.parent };
}
None
}
#[inline]
pub fn push_context(&self, ctx: *mut Context) {
let root = unsafe { &mut *self.root };
let ctx = unsafe { &mut *ctx };
let top = unsafe { &mut *root.parent };
let new_top = ctx.parent;
top.child = ctx;
ctx.parent = top;
root.parent = new_top;
}
#[inline]
pub fn pop_context(&self, ctx: *mut Context) -> &'static mut Context {
let root = unsafe { &mut *self.root };
let ctx = unsafe { &mut *ctx };
let parent = unsafe { &mut *ctx.parent };
ctx.parent = root.parent;
parent.child = ptr::null_mut();
root.parent = parent;
parent
}
}
#[inline]
fn type_error<A>(msg: &str) -> ! {
#[cfg(nightly)]
{
use std::intrinsics::type_name;
let t = unsafe { type_name::<A>() };
error!("{}, expected type: {}", msg, t);
}
#[cfg(not(nightly))]
{
error!("{}", msg);
}
panic!(Error::TypeErr)
}
#[inline]
pub fn is_generator() -> bool {
let env = ContextStack::current();
let root = unsafe { &mut *env.root };
!root.child.is_null()
}
#[inline]
pub fn get_local_data() -> *mut u8 {
let env = ContextStack::current();
let root = unsafe { &mut *env.root };
let mut ctx = unsafe { &mut *root.parent };
while ctx as *const _ != root as *const _ {
if !ctx.local_data.is_null() {
return ctx.local_data;
}
ctx = unsafe { &mut *ctx.parent };
}
ptr::null_mut()
}
#[cfg(test)]
mod test {
use super::is_generator;
#[test]
fn test_is_context() {
assert!(!is_generator());
}
}