maniac_runtime/generator/
yield_.rs

1//! # yield
2//!
3//! generator yield implementation
4//!
5use std::any::Any;
6use std::sync::atomic;
7
8use crate::generator::gen_impl::{unlikely, Generator};
9use crate::generator::reg_context::RegContext;
10use crate::generator::rt::{is_generator, Context, ContextStack, Error};
11
12/// it's a special return instruction that yield nothing
13/// but only terminate the generator safely
14#[macro_export]
15macro_rules! done {
16    () => {{
17        return crate::generator::done();
18    }};
19}
20
21/// don't use it directly, use done!() macro instead
22/// would panic if use in none generator context
23#[doc(hidden)]
24#[inline]
25pub fn done<T>() -> T {
26    assert!(is_generator(), "done is only possible in a generator");
27    std::panic::panic_any(Error::Done)
28}
29
30/// switch back to parent context
31#[inline]
32pub fn yield_now() {
33    let env = ContextStack::current();
34    let cur = env.top();
35    raw_yield_now(&env, cur);
36}
37
38#[inline]
39pub fn raw_yield_now(env: &ContextStack, cur: &mut Context) {
40    let parent = env.pop_context(cur as *mut _);
41    RegContext::swap(&mut cur.regs, &parent.regs);
42}
43
44/// raw yield without catch passed in para
45#[inline]
46fn raw_yield<T: Any>(env: &ContextStack, context: &mut Context, v: T) {
47    // check the context
48    if unlikely(!context.is_generator()) {
49        panic!("yield from none generator context");
50    }
51
52    context.set_ret(v);
53    context._ref -= 1;
54    raw_yield_now(env, context);
55
56    // here we just panic to exit the func
57    if unlikely(context._ref != 1) {
58        std::panic::panic_any(Error::Cancel);
59    }
60}
61
62/// yield something without catch passed in para
63#[inline]
64#[deprecated(since = "0.6.18", note = "please use `scope` version instead")]
65pub fn yield_with<T: Any>(v: T) {
66    let env = ContextStack::current();
67    let context = env.top();
68    raw_yield(&env, context, v);
69}
70
71/// get the passed in para
72#[inline]
73#[deprecated(since = "0.6.18", note = "please use `scope` version instead")]
74pub fn get_yield<A: Any>() -> Option<A> {
75    let context = ContextStack::current().top();
76    raw_get_yield(context)
77}
78
79/// get the passed in para from context
80#[inline]
81fn raw_get_yield<A: Any>(context: &mut Context) -> Option<A> {
82    // check the context
83    if unlikely(!context.is_generator()) {
84        {
85            log::error!("get yield from none generator context");
86            std::panic::panic_any(Error::ContextErr);
87        }
88    }
89
90    context.get_para()
91}
92
93/// yield and get the send para
94// here yield need to return a static lifetime value, which is Any required
95// this is fine, but it's totally safe that we can refer to the function block
96// since we will come back later
97#[inline]
98#[deprecated(since = "0.6.18", note = "please use `scope` version instead")]
99pub fn yield_<A: Any, T: Any>(v: T) -> Option<A> {
100    let env = ContextStack::current();
101    let context = env.top();
102    raw_yield(&env, context, v);
103    atomic::compiler_fence(atomic::Ordering::Acquire);
104    raw_get_yield(context)
105}
106
107/// `yield_from`
108#[deprecated(since = "0.6.18", note = "please use `scope` version instead")]
109pub fn yield_from<A: Any, T: Any>(mut g: Generator<A, T>) -> Option<A> {
110    let env = ContextStack::current();
111    let context = env.top();
112    let mut p = context.get_para();
113    while unlikely(!g.is_done()) {
114        match g.raw_send(p) {
115            None => return None,
116            Some(r) => raw_yield(&env, context, r),
117        }
118        p = context.get_para();
119    }
120    drop(g); // explicitly consume g
121    p
122}
123
124/// coroutine yield
125pub fn co_yield_with<T: Any>(v: T) {
126    let env = ContextStack::current();
127    let context = env.co_ctx().unwrap();
128
129    // check the context, already checked in co_ctx()
130    // if !context.is_generator() {
131    //     info!("yield from none coroutine context");
132    //     // do nothing, just return
133    //     return;
134    // }
135
136    // here we just panic to exit the func
137    if unlikely(context._ref != 1) {
138        std::panic::panic_any(Error::Cancel);
139    }
140
141    context.co_set_ret(v);
142    context._ref -= 1;
143
144    let parent = env.pop_context(context);
145    let top = unsafe { &mut *context.parent };
146    // here we should use the top regs
147    RegContext::swap(&mut top.regs, &parent.regs);
148}
149
150/// coroutine get passed in yield para
151pub fn co_get_yield<A: Any>() -> Option<A> {
152    ContextStack::current()
153        .co_ctx()
154        .and_then(|ctx| ctx.co_get_para())
155}
156
157/// set current coroutine para in user space
158pub fn co_set_para<A: Any>(para: A) {
159    if let Some(ctx) = ContextStack::current().co_ctx() {
160        ctx.co_set_para(para)
161    }
162}