maniac_runtime/generator/
scope.rs

1//! # yield
2//!
3//! generator yield implementation
4//!
5
6use std::marker::PhantomData;
7use std::sync::atomic;
8
9use crate::generator::gen_impl::Generator;
10use crate::generator::rt::{Context, ContextStack, Error};
11use crate::generator::yield_::raw_yield_now;
12
13/// passed in scope type
14/// it not use the context to pass data, but keep it's own data ref
15/// this struct provide both compile type info and runtime data
16pub struct Scope<'scope, 'a, A, T> {
17    para: &'a mut Option<A>,
18    ret: &'a mut Option<T>,
19    scope: PhantomData<&'scope mut &'scope ()>,
20}
21
22impl<'a, A, T> Scope<'_, 'a, A, T> {
23    /// create a new scope object
24    pub(crate) fn new(para: &'a mut Option<A>, ret: &'a mut Option<T>) -> Self {
25        Scope {
26            para,
27            ret,
28            scope: PhantomData,
29        }
30    }
31
32    /// set current generator return value
33    #[inline]
34    fn set_ret(&mut self, v: T) {
35        *self.ret = Some(v);
36    }
37
38    /// raw yield without catch passed in para
39    #[inline]
40    fn raw_yield(&mut self, env: &ContextStack, context: &mut Context, v: T) {
41        // check the context
42        if !context.is_generator() {
43            panic!("yield from none generator context");
44        }
45
46        self.set_ret(v);
47        context._ref -= 1;
48        raw_yield_now(env, context);
49
50        // here we just panic to exit the func
51        if context._ref != 1 {
52            std::panic::panic_any(Error::Cancel);
53        }
54    }
55
56    /// yield something without catch passed in para
57    #[inline]
58    pub fn yield_with(&mut self, v: T) {
59        let env = ContextStack::current();
60        let context = env.top();
61        self.raw_yield(&env, context, v);
62    }
63
64    /// get current generator send para
65    #[inline]
66    pub fn get_yield(&mut self) -> Option<A> {
67        self.para.take()
68    }
69
70    /// yield and get the send para
71    /// # Safety
72    /// When yield out, the reference of the captured data must be still valid
73    /// normally, you should always call the `drop` of the generator
74    #[inline]
75    pub unsafe fn yield_unsafe(&mut self, v: T) -> Option<A> {
76        self.yield_with(v);
77        atomic::compiler_fence(atomic::Ordering::Acquire);
78        self.get_yield()
79    }
80
81    /// `yield_from_unsafe`
82    /// the from generator must has the same type as itself
83    /// # Safety
84    /// When yield out, the reference of the captured data must be still valid
85    /// normally, you should always call the `drop` of the generator
86    pub unsafe fn yield_from_unsafe(&mut self, mut g: Generator<A, T>) -> Option<A> {
87        let env = ContextStack::current();
88        let context = env.top();
89        let mut p = self.get_yield();
90        while !g.is_done() {
91            match g.raw_send(p) {
92                None => return None,
93                Some(r) => self.raw_yield(&env, context, r),
94            }
95            p = self.get_yield();
96        }
97        drop(g); // explicitly consume g
98        p
99    }
100}
101
102impl<A, T> Scope<'_, 'static, A, T> {
103    /// yield and get the send para
104    // it's totally safe that we can refer to the function block
105    // since we will come back later
106    #[inline]
107    pub fn yield_(&mut self, v: T) -> Option<A> {
108        unsafe { self.yield_unsafe(v) }
109    }
110
111    /// `yield_from`
112    /// the from generator must has the same type as itself
113    pub fn yield_from(&mut self, g: Generator<A, T>) -> Option<A> {
114        unsafe { self.yield_from_unsafe(g) }
115    }
116}