Skip to main content

mco_gen/
rt.rs

1//! # generator run time support
2//!
3//! generator run time context management
4//!
5use std::any::Any;
6use std::mem::MaybeUninit;
7use std::ptr;
8
9use crate::reg_context::RegContext;
10
11thread_local!(
12    /// each thread has it's own generator context stack
13    static ROOT_CONTEXT: Box<Context> = {
14        let mut root = Box::new(Context::new());
15        let p = &mut *root as *mut _;
16        root.parent = p; // init top to current
17        root
18    }
19);
20
21// fast access pointer, this is will be init only once
22// when ROOT_CONTEXT get initialized. but in debug mode it
23// will be zero in generator context since the stack changed
24// to a different place, be careful about that.
25#[cfg(nightly)]
26#[thread_local]
27static mut ROOT_CONTEXT_P: *mut Context = ptr::null_mut();
28
29/// yield panic error types
30#[allow(dead_code)]
31#[derive(Debug, Copy, Clone, Eq, PartialEq)]
32pub enum Error {
33    /// Cancel panic
34    Cancel,
35    /// Type mismatch panic
36    TypeErr,
37    /// Stack overflow panic
38    StackErr,
39    /// Wrong Context panic
40    ContextErr,
41}
42
43/// generator context
44#[repr(C)]
45pub struct Context {
46    /// generator regs context
47    pub regs: RegContext,
48    /// child context
49    child: *mut Context,
50    /// parent context
51    pub parent: *mut Context,
52    /// passed in para for send
53    pub para: MaybeUninit<*mut dyn Any>,
54    /// this is just a buffer for the return value
55    pub ret: MaybeUninit<*mut dyn Any>,
56    /// track generator ref, yield will -1, send will +1
57    pub _ref: usize,
58    /// context local storage
59    pub local_data: *mut u8,
60    /// propagate panic
61    pub err: Option<Box<dyn Any + Send>>,
62}
63
64impl Context {
65    /// return a default generator context
66    pub fn new() -> Context {
67        Context {
68            regs: RegContext::empty(),
69            para: MaybeUninit::zeroed(),
70            ret: MaybeUninit::zeroed(),
71            _ref: 1, // none zero means it's not running
72            err: None,
73            child: ptr::null_mut(),
74            parent: ptr::null_mut(),
75            local_data: ptr::null_mut(),
76        }
77    }
78
79    /// judge it's generator context
80    #[inline]
81    pub fn is_generator(&self) -> bool {
82        self.parent != self as *const _ as *mut _
83    }
84
85    /// get current generator send para
86    #[inline]
87    pub fn get_para<A>(&mut self) -> Option<A>
88    where
89        A: Any,
90    {
91        let para = unsafe {
92            let para_ptr = *self.para.as_mut_ptr();
93            assert!(!para_ptr.is_null());
94            &mut *para_ptr
95        };
96        match para.downcast_mut::<Option<A>>() {
97            Some(v) => v.take(),
98            None => type_error::<A>("get yield type mismatch error detected"),
99        }
100    }
101
102    /// get coroutine send para
103    #[inline]
104    pub fn co_get_para<A>(&mut self) -> Option<A> {
105        let para = unsafe {
106            let para_ptr = *self.para.as_mut_ptr();
107            debug_assert!(!para_ptr.is_null());
108            &mut *(para_ptr as *mut Option<A>)
109        };
110        para.take()
111    }
112
113    /// set coroutine send para
114    /// without check the data type for coroutine performance reason
115    #[inline]
116    pub fn co_set_para<A>(&mut self, data: A) {
117        let para = unsafe {
118            let para_ptr = *self.para.as_mut_ptr();
119            debug_assert!(!para_ptr.is_null());
120            &mut *(para_ptr as *mut Option<A>)
121        };
122        *para = Some(data);
123    }
124
125    /// set current generator return value
126    #[inline]
127    pub fn set_ret<T>(&mut self, v: T)
128    where
129        T: Any,
130    {
131        let ret = unsafe {
132            let ret_ptr = *self.ret.as_mut_ptr();
133            assert!(!ret_ptr.is_null());
134            &mut *ret_ptr
135        };
136        match ret.downcast_mut::<Option<T>>() {
137            Some(r) => *r = Some(v),
138            None => type_error::<T>("yield type mismatch error detected"),
139        }
140    }
141
142    /// set coroutine return value
143    /// without check the data type for coroutine performance reason
144    #[inline]
145    pub fn co_set_ret<T>(&mut self, v: T) {
146        let ret = unsafe {
147            let ret_ptr = *self.ret.as_mut_ptr();
148            debug_assert!(!ret_ptr.is_null());
149            &mut *(ret_ptr as *mut Option<T>)
150        };
151        *ret = Some(v);
152    }
153}
154
155/// Coroutine managing environment
156pub struct ContextStack {
157    root: *mut Context,
158}
159
160#[cfg(nightly)]
161#[inline(never)]
162unsafe fn init_root_p() {
163    ROOT_CONTEXT_P = ROOT_CONTEXT.with(|r| &**r as *const _ as *mut Context);
164}
165
166impl ContextStack {
167    #[cfg(nightly)]
168    #[inline(never)]
169    pub fn current() -> ContextStack {
170        unsafe {
171            if ROOT_CONTEXT_P.is_null() {
172                init_root_p();
173            }
174            ContextStack {
175                root: ROOT_CONTEXT_P,
176            }
177        }
178    }
179
180    #[cfg(not(nightly))]
181    #[inline(never)]
182    pub fn current() -> ContextStack {
183        let root = ROOT_CONTEXT.with(|r| &**r as *const _ as *mut Context);
184        ContextStack { root }
185    }
186
187    /// get the top context
188    #[inline]
189    pub fn top(&self) -> &'static mut Context {
190        let root = unsafe { &mut *self.root };
191        unsafe { &mut *root.parent }
192    }
193
194    /// get the coroutine context
195    #[inline]
196    pub fn co_ctx(&self) -> Option<&'static mut Context> {
197        let root = unsafe { &mut *self.root };
198
199        // search from top
200        let mut ctx = unsafe { &mut *root.parent };
201        while ctx as *const _ != root as *const _ {
202            if !ctx.local_data.is_null() {
203                return Some(ctx);
204            }
205            ctx = unsafe { &mut *ctx.parent };
206        }
207        // not find any coroutine
208        None
209    }
210
211    /// push the context to the thread context list
212    #[inline]
213    pub fn push_context(&self, ctx: *mut Context) {
214        let root = unsafe { &mut *self.root };
215        let ctx = unsafe { &mut *ctx };
216        let top = unsafe { &mut *root.parent };
217        let new_top = ctx.parent;
218
219        // link top and new ctx
220        top.child = ctx;
221        ctx.parent = top;
222
223        // save the new top
224        root.parent = new_top;
225    }
226
227    /// pop the context from the thread context list and return it's parent context
228    #[inline]
229    pub fn pop_context(&self, ctx: *mut Context) -> &'static mut Context {
230        let root = unsafe { &mut *self.root };
231        let ctx = unsafe { &mut *ctx };
232        let parent = unsafe { &mut *ctx.parent };
233
234        // save the old top in ctx's parent
235        ctx.parent = root.parent;
236        // unlink ctx and it's parent
237        parent.child = ptr::null_mut();
238
239        // save the new top
240        root.parent = parent;
241
242        parent
243    }
244}
245
246#[inline]
247fn type_error<A>(msg: &str) -> ! {
248    #[cfg(nightly)]
249    #[allow(unused_unsafe)]
250    {
251        use std::intrinsics::type_name;
252        let t = unsafe { type_name::<A>() };
253        error!("{}, expected type: {}", msg, t);
254    }
255
256    #[cfg(not(nightly))]
257    {
258        error!("{}", msg);
259    }
260    std::panic::panic_any(Error::TypeErr)
261}
262
263/// check the current context if it's generator
264#[inline]
265pub fn is_generator() -> bool {
266    let env = ContextStack::current();
267    let root = unsafe { &mut *env.root };
268    !root.child.is_null()
269}
270
271/// get the current context local data
272/// only coroutine support local data
273#[inline]
274pub fn get_local_data() -> *mut u8 {
275    let env = ContextStack::current();
276    let root = unsafe { &mut *env.root };
277
278    // search from top
279    let mut ctx = unsafe { &mut *root.parent };
280    while ctx as *const _ != root as *const _ {
281        if !ctx.local_data.is_null() {
282            return ctx.local_data;
283        }
284        ctx = unsafe { &mut *ctx.parent };
285    }
286
287    ptr::null_mut()
288}
289
290#[cfg(test)]
291mod test {
292    use super::is_generator;
293
294    #[test]
295    fn test_is_context() {
296        // this is the root context
297        assert!(!is_generator());
298    }
299}