Skip to content

Commit 8043496

Browse files
whitequarkedef1c
authored andcommitted
Reimplement Generator to pass both input and output values around.
1 parent 5e1bf25 commit 8043496

File tree

6 files changed

+127
-123
lines changed

6 files changed

+127
-123
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ without relying on kernel services. It can be used in hosted environments
1313
(using `std`) as well as on bare metal (using `core`).
1414

1515
It provides the following safe abstractions:
16-
* an implementation of internal iterators, also known as generators,
16+
* an implementation of generators,
1717
[Generator](https://nathan7.github.io/libfringe/fringe/generator/struct.Generator.html).
1818

1919
It also provides the necessary low-level building blocks:

benches/generator.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ use fringe::{OsStack, Generator};
1010
#[bench]
1111
fn generate(b: &mut test::Bencher) {
1212
let stack = OsStack::new(0).unwrap();
13-
let mut gen = Generator::new(stack, move |yielder| {
14-
for i in 1.. { yielder.generate(i) }
13+
let mut identity = Generator::new(stack, move |yielder, mut input| {
14+
loop { input = yielder.generate(input) }
1515
});
1616

17-
b.iter(|| test::black_box(gen.next()));
17+
b.iter(|| test::black_box(identity.resume(test::black_box(0))));
1818
}

src/generator.rs

Lines changed: 95 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,10 @@
99
//! afterwards.
1010
1111
use core::marker::PhantomData;
12-
use core::iter::Iterator;
1312
use core::{ptr, mem};
1413

1514
use stack;
16-
use context;
15+
use context::Context;
1716

1817
#[derive(Debug, Clone, Copy)]
1918
pub enum State {
@@ -24,18 +23,20 @@ pub enum State {
2423
Unavailable
2524
}
2625

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.
2928
///
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.
3635
///
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()`.
3940
///
4041
/// `state()` can be used to determine whether the generator function has returned;
4142
/// the state is `State::Runnable` after creation and suspension, and `State::Unavailable`
@@ -47,28 +48,29 @@ pub enum State {
4748
/// use fringe::{OsStack, Generator};
4849
///
4950
/// 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)
5355
/// }
5456
/// });
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
5960
/// ```
6061
#[derive(Debug)]
61-
pub struct Generator<Item: Send, Stack: stack::Stack> {
62+
pub struct Generator<Input: Send, Output: Send, Stack: stack::Stack> {
6263
state: State,
63-
context: context::Context<Stack>,
64-
phantom: PhantomData<*const Item>
64+
context: Context<Stack>,
65+
phantom: (PhantomData<*const Input>, PhantomData<*const Output>)
6566
}
6667

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 {
6970
/// 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 {
7274
unsafe { Generator::unsafe_new(stack, f) }
7375
}
7476

@@ -77,36 +79,70 @@ impl<Item, Stack> Generator<Item, Stack>
7779
/// This function is unsafe because the generator function can easily violate
7880
/// memory safety by overflowing the stack. It is useful in environments where
7981
/// 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) {
8487
// 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>;
8993
// Run the body of the generator.
90-
f(&mut yielder);
94+
f(&mut yielder, input);
9195
// Past this point, the generator has dropped everything it has held.
92-
loop { yielder.return_(None) }
96+
loop { yielder.generate_bare(None); }
9397
}
9498

9599
let mut generator = Generator {
96100
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)
99103
};
100104

101105
// 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);
106110

107111
generator
108112
}
109113

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+
110146
/// Returns the state of the generator.
111147
#[inline]
112148
pub fn state(&self) -> State { self.state }
@@ -125,72 +161,41 @@ impl<Item, Stack> Generator<Item, Stack>
125161
/// Yielder is an interface provided to every generator through which it
126162
/// returns a value.
127163
#[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>)
131167
}
132168

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> {
136172
Yielder {
137173
context: context,
138-
phantom: PhantomData
174+
phantom: (PhantomData, PhantomData)
139175
}
140176
}
141177

142178
#[inline(always)]
143-
fn return_(&mut self, mut val: Option<Item>) {
179+
fn generate_bare(&mut self, mut val: Option<Output>) -> Input {
144180
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));
147184
// The generator can be moved (and with it, the context).
148185
// This changes the address of the context.
149186
// 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
152189
// the generator cannot be moved, as a &mut Generator is necessary
153190
// to resume the generator function.
191+
input
154192
}
155193
}
156194

157-
/// Suspends the generator and returns `Some(item)` from the `next()`
195+
/// Suspends the generator and returns `Some(item)` from the `resume()`
158196
/// invocation that resumed the generator.
159197
#[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))
195200
}
196201
}

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
//!
1414
//! It provides the following safe abstractions:
1515
//!
16-
//! * an implementation of internal iterators, also known as generators,
16+
//! * an implementation of generators,
1717
//! [Generator](generator/struct.Generator.html).
1818
//!
1919
//! It also provides the necessary low-level building blocks:

tests/fpe.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ extern {
2020
#[ignore]
2121
fn fpe() {
2222
let stack = OsStack::new(0).unwrap();
23-
let mut gen = Generator::new(stack, move |yielder| {
23+
let mut gen = Generator::new(stack, move |yielder, ()| {
2424
yielder.generate(1.0 / black_box(0.0));
2525
});
2626

2727
unsafe { feenableexcept(FE_DIVBYZERO); }
28-
println!("{:?}", gen.next());
28+
println!("{:?}", gen.resume(()));
2929
}

tests/generator.rs

Lines changed: 25 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,57 +6,56 @@ extern crate fringe;
66
use fringe::OsStack;
77
use fringe::generator::Generator;
88

9-
#[test]
10-
fn generator() {
9+
fn new_add_one() -> Generator<i32, i32, OsStack> {
1110
let stack = OsStack::new(0).unwrap();
12-
let mut gen = Generator::new(stack, move |yielder| {
13-
for i in 1..4 {
14-
yielder.generate(i);
11+
Generator::new(stack, move |yielder, mut input| {
12+
loop {
13+
if input == 0 { break }
14+
input = yielder.generate(input + 1)
1515
}
16-
});
17-
assert_eq!(gen.next(), Some(1));
18-
assert_eq!(gen.next(), Some(2));
19-
assert_eq!(gen.next(), Some(3));
20-
assert_eq!(gen.next(), None);
16+
})
17+
}
18+
19+
#[test]
20+
fn generator() {
21+
let mut add_one = new_add_one();
22+
assert_eq!(add_one.resume(1), Some(2));
23+
assert_eq!(add_one.resume(2), Some(3));
24+
assert_eq!(add_one.resume(0), None);
2125
}
2226

2327
#[test]
2428
fn move_after_new() {
25-
let stack = OsStack::new(0).unwrap();
26-
let mut gen = Generator::new(stack, move |yielder| {
27-
for i in 1..4 {
28-
yielder.generate(i);
29-
}
30-
});
31-
assert_eq!(gen.next(), Some(1));
29+
let mut add_one = new_add_one();
30+
assert_eq!(add_one.resume(1), Some(2));
3231

3332
#[inline(never)]
34-
fn rest(mut gen: Generator<u32, OsStack>) {
35-
assert_eq!(gen.next(), Some(2));
36-
assert_eq!(gen.next(), Some(3));
37-
assert_eq!(gen.next(), None);
33+
fn run_moved(mut add_one: Generator<i32, i32, OsStack>) {
34+
assert_eq!(add_one.resume(2), Some(3));
35+
assert_eq!(add_one.resume(3), Some(4));
36+
assert_eq!(add_one.resume(0), None);
3837
}
39-
rest(gen);
38+
run_moved(add_one);
4039
}
4140

4241
#[test]
4342
#[should_panic]
4443
fn panic_safety() {
4544
struct Wrapper {
46-
gen: Generator<u32, OsStack>
45+
gen: Generator<(), (), OsStack>
4746
}
4847

4948
impl Drop for Wrapper {
5049
fn drop(&mut self) {
51-
self.gen.next();
50+
self.gen.resume(());
5251
}
5352
}
5453

5554
let stack = OsStack::new(4 << 20).unwrap();
56-
let gen = Generator::new(stack, move |_yielder| {
55+
let gen = Generator::new(stack, move |_yielder, ()| {
5756
panic!("foo")
5857
});
5958

6059
let mut wrapper = Wrapper { gen: gen };
61-
wrapper.gen.next();
60+
wrapper.gen.resume(());
6261
}

0 commit comments

Comments
 (0)