Skip to main content

greenie/
generator.rs

1/// The result of a generator resumption.
2/// This enum is returned from the Generator::resume method and indicates the possible return values of a generator.
3///  Currently this corresponds to either a suspension point (Yielded) or a termination point (Complete).
4pub enum GeneratorState {
5    /// Generator is ready to work.
6    Ready,
7    /// The generator suspended with a value.
8    /// This state indicates that a generator has been suspended. The value provided in this variant corresponds to the expression passed to yield and allows generators to provide a value each time they yield.
9    Yielded(Box<dyn std::any::Any>),
10    /// The generator completed with a return value.
11    ///
12    /// This state indicates that a generator has finished execution with the provided value. Once a generator has returned Complete it is considered a programmer error to call resume again.
13    Complete(Box<dyn std::any::Any>),
14}
15
16impl Default for GeneratorState {
17    fn default() -> Self {
18        Self::Ready
19    }
20}
21
22use crate::ctx::*;
23use crate::ptr::*;
24use crate::scheduler::*;
25
26pub struct Generator {
27    pub state: Ptr<GeneratorState>,
28    pub(crate) complete: std::cell::Cell<bool>,
29    pub thread: Ptr<Context>,
30    pub to: Ptr<Context>,
31    pub is_join: bool,
32}
33
34use std::rc::Rc;
35
36impl Generator {
37    /// Spawn generator
38    pub fn spawn<F: 'static, A: 'static + crate::ctx::ApplyTo<F> + Clone>(
39        closure: F,
40        args: A,
41    ) -> Rc<Self> {
42        crate::scheduler::RUNTIME.with(|rt| {
43            let to = rt.active_ctx;
44            let thread = rt.get().spawn_not_schedule(closure, args).thread();
45
46            let generator = Rc::new(Generator {
47                state: Ptr::new(GeneratorState::Ready),
48                thread,
49                to,
50                complete: std::cell::Cell::new(false),
51                is_join: false,
52            });
53            thread.get().generator = Some(generator.clone());
54            generator
55        })
56    }
57
58    /// Resumes the execution of this generator.
59    /// This function will resume execution of the generator or start execution if it hasn't already. This call will return back into the
60    /// generator's last suspension point, resuming execution from the latest yield. The generator will continue executing until it
61    /// either yields or returns, at which point this function will return.
62    pub fn resume(&self) -> Result<GeneratorState, &'static str> {
63        if self.complete.get() {
64            return Err("Generator already complete");
65        }
66        RUNTIME.with(|rt| {
67            rt.get().resume(self.thread);
68            rt.get().switch_without_current();
69        });
70        if let GeneratorState::Complete(_) = &self.state.get() {
71            self.complete.set(true);
72        }
73        let state = self.state.take();
74        Ok(state)
75    }
76}
77
78/// Yield generator with a value
79pub fn generator_yield<T: 'static>(val: T) -> Result<(), &'static str> {
80    crate::scheduler::RUNTIME.with(|rt| rt.get().t_yield_generator(val))
81}
82
83//// Iterates through generator
84#[macro_export]
85macro_rules! iterate_generator {
86    (for ($x: ident in $generator: expr) $b: block) => {
87        loop {
88            match $generator.resume() {
89                Ok(greenie::generator::GeneratorState::Yielded($x)) => $b,
90                Ok(greenie::generator::GeneratorState::Complete($x)) => {
91                    break $x
92                },
93                Err(e) => {
94                    eprintln!("{}",e);
95                    std::process::exit(1);
96                }
97                _ => panic!("Unexpected")
98            }
99        }
100    };
101    (for ($x: ident in $generator: expr) $b: block and $b2: block) => {
102        loop {
103            match $generator.resume() {
104                Ok(greenie::generator::GeneratorState::Yielded($x)) => $b,
105                Ok(greenie::generator::GeneratorState::Complete($x)) => {
106                    $b2
107                    break;
108                },
109                Ok(GeneratorState::Ready) => panic!("impossible"),
110                Err(e) => {
111                    eprintln!("{}",e);
112                    std::process::exit(1);
113                }
114                _ => unreachable!()
115            }
116        }
117    };
118
119    (enumerate for ($c: ident, $x: ident in $generator: expr) $b: block) => {
120        let mut $c = 0;
121        loop {
122            match $generator.resume() {
123                Ok(greenie::generator::GeneratorState::Yielded($x)) => $b,
124                Ok(greenie::generator::GeneratorState::Complete($x)) => {
125                    break $x
126                },
127                Err(e) => {
128                    eprintln!("{}",e);
129                    std::process::exit(1);
130                }
131                _ => unreachable!()
132            }
133            $c += 1;
134        }
135    };
136
137    (enumerate for ($c: ident, $x: ident in $generator: expr) $b: block and $b2: block) => {
138        let mut $c = 0;
139        loop {
140            match $generator.resume() {
141                Ok(greenie::generator::GeneratorState::Yielded($x)) => $b,
142                Ok(greenie::generator::GeneratorState::Complete($x)) => {
143                    $b2
144                    break;
145                },
146                Ok(greenie::generator::GeneratorState::Ready) => panic!("impossible"),
147                Err(e) => {
148                    eprintln!("{}",e);
149                    std::process::exit(1);
150                }
151                _ => unreachable!()
152            }
153            $c += 1;
154        }
155    };
156}