9
9
//! afterwards.
10
10
11
11
use core:: marker:: PhantomData ;
12
- use core:: iter:: Iterator ;
13
12
use core:: { ptr, mem} ;
14
13
15
14
use stack;
16
- use context;
15
+ use context:: Context ;
17
16
18
17
#[ derive( Debug , Clone , Copy ) ]
19
18
pub enum State {
@@ -24,18 +23,20 @@ pub enum State {
24
23
Unavailable
25
24
}
26
25
27
- /// Generator wraps a function and allows suspending its execution more than
28
- /// once, return a value each time.
26
+ /// Generator wraps a function and allows suspending its execution more than once, returning
27
+ /// a value each time.
29
28
///
30
- /// It implements the Iterator trait. The first time `next( )` is called,
31
- /// the function is called as `f(yielder)`; every time `next()` is called afterwards,
32
- /// the function is resumed. In either case, it runs until it suspends its execution
33
- /// through `yielder.generate(val)`), in which case `next()` returns `Some(val)`, or
34
- /// returns, in which case `next ()` returns `None`. `next()` will return `None`
35
- /// every time after that.
29
+ /// The first time `resume(input0 )` is called, the function is called as `f(yielder, input0)`.
30
+ /// It runs until it suspends its execution through `yielder.generate(output0)`, after which
31
+ /// `resume(input0)` returns `output0`. The function can be resumed again using `resume(input1)`,
32
+ /// after which `yielder.generate(output0)` returns `input1`, and so on. Once the function returns,
33
+ /// the `resume ()` call will return `None`, and it will return `None` every time it is called
34
+ /// after that.
36
35
///
37
- /// After the generator function returns, it is safe to reclaim the generator
38
- /// stack using `unwrap()`.
36
+ /// If the generator function panics, the panic is propagated through the `resume()` call as usual.
37
+ ///
38
+ /// After the generator function returns or panics, it is safe to reclaim the generator stack
39
+ /// using `unwrap()`.
39
40
///
40
41
/// `state()` can be used to determine whether the generator function has returned;
41
42
/// the state is `State::Runnable` after creation and suspension, and `State::Unavailable`
@@ -47,28 +48,29 @@ pub enum State {
47
48
/// use fringe::{OsStack, Generator};
48
49
///
49
50
/// let stack = OsStack::new(0).unwrap();
50
- /// let mut gen = Generator::new(stack, move |yielder| {
51
- /// for i in 1..4 {
52
- /// yielder.generate(i);
51
+ /// let mut add_one = Generator::new(stack, move |yielder, mut input| {
52
+ /// loop {
53
+ /// if input == 0 { break }
54
+ /// input = yielder.generate(input + 1)
53
55
/// }
54
56
/// });
55
- /// println!("{:?}", gen.next()); // prints Some(1)
56
- /// println!("{:?}", gen.next()); // prints Some(2)
57
- /// println!("{:?}", gen.next()); // prints Some(3)
58
- /// println!("{:?}", gen.next()); // prints None
57
+ /// println!("{:?}", add_one.resume(2)); // prints Some(3)
58
+ /// println!("{:?}", add_one.resume(3)); // prints Some(4)
59
+ /// println!("{:?}", add_one.resume(0)); // prints None
59
60
/// ```
60
61
#[ derive( Debug ) ]
61
- pub struct Generator < Item : Send , Stack : stack:: Stack > {
62
+ pub struct Generator < Input : Send , Output : Send , Stack : stack:: Stack > {
62
63
state : State ,
63
- context : context :: Context < Stack > ,
64
- phantom : PhantomData < * const Item >
64
+ context : Context < Stack > ,
65
+ phantom : ( PhantomData < * const Input > , PhantomData < * const Output > )
65
66
}
66
67
67
- impl < Item , Stack > Generator < Item , Stack >
68
- where Item : Send , Stack : stack:: Stack {
68
+ impl < Input , Output , Stack > Generator < Input , Output , Stack >
69
+ where Input : Send , Output : Send , Stack : stack:: Stack {
69
70
/// Creates a new generator.
70
- pub fn new < F > ( stack : Stack , f : F ) -> Generator < Item , Stack >
71
- where Stack : stack:: GuardedStack , F : FnOnce ( & mut Yielder < Item , Stack > ) + Send {
71
+ pub fn new < F > ( stack : Stack , f : F ) -> Generator < Input , Output , Stack >
72
+ where Stack : stack:: GuardedStack ,
73
+ F : FnOnce ( & mut Yielder < Input , Output , Stack > , Input ) + Send {
72
74
unsafe { Generator :: unsafe_new ( stack, f) }
73
75
}
74
76
@@ -77,36 +79,70 @@ impl<Item, Stack> Generator<Item, Stack>
77
79
/// This function is unsafe because the generator function can easily violate
78
80
/// memory safety by overflowing the stack. It is useful in environments where
79
81
/// guarded stacks do not exist, e.g. in absence of an MMU.
80
- pub unsafe fn unsafe_new < F > ( stack : Stack , f : F ) -> Generator < Item , Stack >
81
- where F : FnOnce ( & mut Yielder < Item , Stack > ) + Send {
82
- unsafe extern "C" fn generator_wrapper < Item , Stack , F > ( info : usize ) -> !
83
- where Item : Send , Stack : stack:: Stack , F : FnOnce ( & mut Yielder < Item , Stack > ) {
82
+ pub unsafe fn unsafe_new < F > ( stack : Stack , f : F ) -> Generator < Input , Output , Stack >
83
+ where F : FnOnce ( & mut Yielder < Input , Output , Stack > , Input ) + Send {
84
+ unsafe extern "C" fn generator_wrapper < Input , Output , Stack , F > ( env : usize ) -> !
85
+ where Input : Send , Output : Send , Stack : stack:: Stack ,
86
+ F : FnOnce ( & mut Yielder < Input , Output , Stack > , Input ) {
84
87
// Retrieve our environment from the callee and return control to it.
85
- let ( mut yielder, f) = ptr:: read ( info as * mut ( Yielder < Item , Stack > , F ) ) ;
86
- let new_context = context:: Context :: swap ( yielder. context , yielder. context , 0 ) ;
87
- // See Yielder::return_.
88
- yielder. context = new_context as * mut context:: Context < Stack > ;
88
+ let ( mut yielder, f) = ptr:: read ( env as * mut ( Yielder < Input , Output , Stack > , F ) ) ;
89
+ let data = Context :: swap ( yielder. context , yielder. context , 0 ) ;
90
+ // See the second half of Yielder::generate_bare.
91
+ let ( new_context, input) = ptr:: read ( data as * mut ( * mut Context < Stack > , Input ) ) ;
92
+ yielder. context = new_context as * mut Context < Stack > ;
89
93
// Run the body of the generator.
90
- f ( & mut yielder) ;
94
+ f ( & mut yielder, input ) ;
91
95
// Past this point, the generator has dropped everything it has held.
92
- loop { yielder. return_ ( None ) }
96
+ loop { yielder. generate_bare ( None ) ; }
93
97
}
94
98
95
99
let mut generator = Generator {
96
100
state : State :: Runnable ,
97
- context : context :: Context :: new ( stack, generator_wrapper :: < Item , Stack , F > ) ,
98
- phantom : PhantomData
101
+ context : Context :: new ( stack, generator_wrapper :: < Input , Output , Stack , F > ) ,
102
+ phantom : ( PhantomData , PhantomData )
99
103
} ;
100
104
101
105
// Transfer environment to the callee.
102
- let mut data = ( Yielder :: new ( & mut generator. context ) , f) ;
103
- context :: Context :: swap ( & mut generator. context , & generator. context ,
104
- & mut data as * mut ( Yielder < Item , Stack > , F ) as usize ) ;
105
- mem:: forget ( data ) ;
106
+ let mut env = ( Yielder :: new ( & mut generator. context ) , f) ;
107
+ Context :: swap ( & mut generator. context , & generator. context ,
108
+ & mut env as * mut ( Yielder < Input , Output , Stack > , F ) as usize ) ;
109
+ mem:: forget ( env ) ;
106
110
107
111
generator
108
112
}
109
113
114
+ /// Resumes the generator and return the next value it yields.
115
+ /// If the generator function has returned, returns `None`.
116
+ #[ inline]
117
+ pub fn resume ( & mut self , input : Input ) -> Option < Output > {
118
+ match self . state {
119
+ State :: Runnable => {
120
+ // Set the state to Unavailable. Since we have exclusive access to the generator,
121
+ // the only case where this matters is the generator function panics, after which
122
+ // it must not be invocable again.
123
+ self . state = State :: Unavailable ;
124
+
125
+ // Switch to the generator function, and retrieve the yielded value.
126
+ let val = unsafe {
127
+ let mut data_in = ( & mut self . context as * mut Context < Stack > , input) ;
128
+ let data_out =
129
+ ptr:: read ( Context :: swap ( & mut self . context , & self . context ,
130
+ & mut data_in as * mut ( * mut Context < Stack > , Input ) as usize )
131
+ as * mut Option < Output > ) ;
132
+ mem:: forget ( data_in) ;
133
+ data_out
134
+ } ;
135
+
136
+ // Unless the generator function has returned, it can be switched to again, so
137
+ // set the state to Runnable.
138
+ if val. is_some ( ) { self . state = State :: Runnable }
139
+
140
+ val
141
+ }
142
+ State :: Unavailable => None
143
+ }
144
+ }
145
+
110
146
/// Returns the state of the generator.
111
147
#[ inline]
112
148
pub fn state ( & self ) -> State { self . state }
@@ -125,72 +161,41 @@ impl<Item, Stack> Generator<Item, Stack>
125
161
/// Yielder is an interface provided to every generator through which it
126
162
/// returns a value.
127
163
#[ derive( Debug ) ]
128
- pub struct Yielder < Item : Send , Stack : stack:: Stack > {
129
- context : * mut context :: Context < Stack > ,
130
- phantom : PhantomData < Item >
164
+ pub struct Yielder < Input : Send , Output : Send , Stack : stack:: Stack > {
165
+ context : * mut Context < Stack > ,
166
+ phantom : ( PhantomData < * const Input > , PhantomData < * const Output > )
131
167
}
132
168
133
- impl < Item , Stack > Yielder < Item , Stack >
134
- where Item : Send , Stack : stack:: Stack {
135
- fn new ( context : * mut context :: Context < Stack > ) -> Yielder < Item , Stack > {
169
+ impl < Input , Output , Stack > Yielder < Input , Output , Stack >
170
+ where Input : Send , Output : Send , Stack : stack:: Stack {
171
+ fn new ( context : * mut Context < Stack > ) -> Yielder < Input , Output , Stack > {
136
172
Yielder {
137
173
context : context,
138
- phantom : PhantomData
174
+ phantom : ( PhantomData , PhantomData )
139
175
}
140
176
}
141
177
142
178
#[ inline( always) ]
143
- fn return_ ( & mut self , mut val : Option < Item > ) {
179
+ fn generate_bare ( & mut self , mut val : Option < Output > ) -> Input {
144
180
unsafe {
145
- let new_context = context:: Context :: swap ( self . context , self . context ,
146
- & mut val as * mut Option < Item > as usize ) ;
181
+ let data = Context :: swap ( self . context , self . context ,
182
+ & mut val as * mut Option < Output > as usize ) ;
183
+ let ( new_context, input) = ptr:: read ( data as * mut ( * mut Context < Stack > , Input ) ) ;
147
184
// The generator can be moved (and with it, the context).
148
185
// This changes the address of the context.
149
186
// Thus, we update it after each swap.
150
- self . context = new_context as * mut context :: Context < Stack > ;
151
- // However, between this point and the next time we enter return_
187
+ self . context = new_context;
188
+ // However, between this point and the next time we enter generate_bare
152
189
// the generator cannot be moved, as a &mut Generator is necessary
153
190
// to resume the generator function.
191
+ input
154
192
}
155
193
}
156
194
157
- /// Suspends the generator and returns `Some(item)` from the `next ()`
195
+ /// Suspends the generator and returns `Some(item)` from the `resume ()`
158
196
/// invocation that resumed the generator.
159
197
#[ inline( always) ]
160
- pub fn generate ( & mut self , item : Item ) {
161
- self . return_ ( Some ( item) )
162
- }
163
- }
164
-
165
- impl < Item , Stack > Iterator for Generator < Item , Stack >
166
- where Item : Send , Stack : stack:: Stack {
167
- type Item = Item ;
168
-
169
- /// Resumes the generator and return the next value it yields.
170
- /// If the generator function has returned, returns `None`.
171
- #[ inline]
172
- fn next ( & mut self ) -> Option < Self :: Item > {
173
- match self . state {
174
- State :: Runnable => {
175
- // Set the state to Unavailable. Since we have exclusive access to the generator,
176
- // the only case where this matters is the generator function panics, after which
177
- // it must not be invocable again.
178
- self . state = State :: Unavailable ;
179
-
180
- // Switch to the generator function.
181
- let new_context = & mut self . context as * mut context:: Context < Stack > as usize ;
182
- let val = unsafe {
183
- ptr:: read ( context:: Context :: swap ( & mut self . context , & self . context , new_context)
184
- as * mut Option < Item > )
185
- } ;
186
-
187
- // Unless the generator function has returned, it can be switched to again, so
188
- // set the state to Runnable.
189
- if val. is_some ( ) { self . state = State :: Runnable }
190
-
191
- val
192
- }
193
- State :: Unavailable => None
194
- }
198
+ pub fn generate ( & mut self , item : Output ) -> Input {
199
+ self . generate_bare ( Some ( item) )
195
200
}
196
201
}
0 commit comments