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