2016-07-17 11:11:33 +08:00
|
|
|
// This file is part of libfringe, a low-level green threading library.
|
|
|
|
// Copyright (c) whitequark <whitequark@whitequark.org>
|
2016-08-21 05:45:01 +08:00
|
|
|
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
|
|
|
|
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
|
|
|
|
// http://opensource.org/licenses/MIT>, at your option. This file may not be
|
|
|
|
// copied, modified, or distributed except according to those terms.
|
2016-07-17 11:11:33 +08:00
|
|
|
|
|
|
|
//! Generators.
|
|
|
|
//!
|
|
|
|
//! Generators allow repeatedly suspending the execution of a function,
|
|
|
|
//! returning a value to the caller, and resuming the suspended function
|
|
|
|
//! afterwards.
|
|
|
|
|
|
|
|
use core::marker::PhantomData;
|
|
|
|
use core::{ptr, mem};
|
2016-09-07 10:58:37 +08:00
|
|
|
use core::cell::Cell;
|
2016-07-17 11:11:33 +08:00
|
|
|
|
|
|
|
use stack;
|
2016-09-07 20:56:32 +08:00
|
|
|
use debug;
|
|
|
|
use arch::{self, StackPointer};
|
2016-07-17 11:11:33 +08:00
|
|
|
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
|
|
pub enum State {
|
|
|
|
/// Generator can be resumed. This is the initial state.
|
2016-08-11 10:16:13 +08:00
|
|
|
Runnable,
|
2016-07-17 11:11:33 +08:00
|
|
|
/// Generator cannot be resumed. This is the state of the generator after
|
2016-08-11 10:16:13 +08:00
|
|
|
/// the generator function has returned or panicked.
|
|
|
|
Unavailable
|
2016-07-17 11:11:33 +08:00
|
|
|
}
|
|
|
|
|
2016-08-12 05:04:40 +08:00
|
|
|
/// Generator wraps a function and allows suspending its execution more than once, returning
|
|
|
|
/// a value each time.
|
2016-07-17 11:11:33 +08:00
|
|
|
///
|
2016-08-12 05:04:40 +08:00
|
|
|
/// The first time `resume(input0)` is called, the function is called as `f(yielder, input0)`.
|
2016-08-30 18:37:15 +08:00
|
|
|
/// It runs until it suspends its execution through `yielder.suspend(output0)`, after which
|
2016-08-12 05:04:40 +08:00
|
|
|
/// `resume(input0)` returns `output0`. The function can be resumed again using `resume(input1)`,
|
2016-08-30 18:37:15 +08:00
|
|
|
/// after which `yielder.suspend(output0)` returns `input1`, and so on. Once the function returns,
|
2016-08-12 05:04:40 +08:00
|
|
|
/// the `resume()` call will return `None`, and it will return `None` every time it is called
|
|
|
|
/// after that.
|
2016-07-17 11:11:33 +08:00
|
|
|
///
|
2016-08-12 05:04:40 +08:00
|
|
|
/// If the generator function panics, the panic is propagated through the `resume()` call as usual.
|
|
|
|
///
|
|
|
|
/// After the generator function returns or panics, it is safe to reclaim the generator stack
|
|
|
|
/// using `unwrap()`.
|
2016-07-17 11:11:33 +08:00
|
|
|
///
|
|
|
|
/// `state()` can be used to determine whether the generator function has returned;
|
2016-08-11 10:16:13 +08:00
|
|
|
/// the state is `State::Runnable` after creation and suspension, and `State::Unavailable`
|
|
|
|
/// once the generator function returns or panics.
|
2016-07-17 11:11:33 +08:00
|
|
|
///
|
2016-08-13 08:07:08 +08:00
|
|
|
/// When the input type is `()`, a generator implements the Iterator trait.
|
|
|
|
///
|
2016-07-17 11:11:33 +08:00
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// use fringe::{OsStack, Generator};
|
|
|
|
///
|
|
|
|
/// let stack = OsStack::new(0).unwrap();
|
2016-08-12 05:04:40 +08:00
|
|
|
/// let mut add_one = Generator::new(stack, move |yielder, mut input| {
|
|
|
|
/// loop {
|
|
|
|
/// if input == 0 { break }
|
2016-08-30 18:37:15 +08:00
|
|
|
/// input = yielder.suspend(input + 1)
|
2016-07-17 11:11:33 +08:00
|
|
|
/// }
|
|
|
|
/// });
|
2016-08-12 05:04:40 +08:00
|
|
|
/// println!("{:?}", add_one.resume(2)); // prints Some(3)
|
|
|
|
/// println!("{:?}", add_one.resume(3)); // prints Some(4)
|
|
|
|
/// println!("{:?}", add_one.resume(0)); // prints None
|
2016-07-17 11:11:33 +08:00
|
|
|
/// ```
|
2016-08-13 08:07:08 +08:00
|
|
|
///
|
|
|
|
/// # Iterator example
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// use fringe::{OsStack, Generator};
|
|
|
|
///
|
|
|
|
/// let stack = OsStack::new(0).unwrap();
|
|
|
|
/// let mut nat = Generator::new(stack, move |yielder, ()| {
|
2016-08-30 18:37:15 +08:00
|
|
|
/// for i in 1.. { yielder.suspend(i) }
|
2016-08-13 08:07:08 +08:00
|
|
|
/// });
|
|
|
|
/// println!("{:?}", nat.next()); // prints Some(0)
|
|
|
|
/// println!("{:?}", nat.next()); // prints Some(1)
|
|
|
|
/// println!("{:?}", nat.next()); // prints Some(2)
|
|
|
|
/// ```
|
2016-07-17 11:11:33 +08:00
|
|
|
#[derive(Debug)]
|
2016-09-25 18:02:17 +08:00
|
|
|
pub struct Generator<'a, Input: 'a, Output: 'a, Stack: stack::Stack> {
|
2016-09-07 20:56:32 +08:00
|
|
|
state: State,
|
|
|
|
stack: Stack,
|
|
|
|
stack_id: debug::StackId,
|
|
|
|
stack_ptr: arch::StackPointer,
|
2016-09-15 03:18:31 +08:00
|
|
|
phantom: PhantomData<(&'a (), *mut Input, *const Output)>
|
2016-07-17 11:11:33 +08:00
|
|
|
}
|
|
|
|
|
2016-09-16 17:11:10 +08:00
|
|
|
unsafe impl<'a, Input, Output, Stack> Send for Generator<'a, Input, Output, Stack>
|
|
|
|
where Input: Send + 'a, Output: Send + 'a, Stack: stack::Stack + Send {}
|
|
|
|
|
2016-09-15 03:18:31 +08:00
|
|
|
impl<'a, Input, Output, Stack> Generator<'a, Input, Output, Stack>
|
2016-09-25 18:02:17 +08:00
|
|
|
where Input: 'a, Output: 'a, Stack: stack::Stack {
|
2016-07-17 11:11:33 +08:00
|
|
|
/// Creates a new generator.
|
2016-09-03 00:00:11 +08:00
|
|
|
///
|
|
|
|
/// See also the [contract](../trait.GuardedStack.html) that needs to be fulfilled by `stack`.
|
2016-09-15 03:18:31 +08:00
|
|
|
pub fn new<F>(stack: Stack, f: F) -> Generator<'a, Input, Output, Stack>
|
2016-08-12 05:04:40 +08:00
|
|
|
where Stack: stack::GuardedStack,
|
2016-09-16 16:34:49 +08:00
|
|
|
F: FnOnce(&Yielder<Input, Output>, Input) + Send + 'a {
|
2016-07-17 11:45:50 +08:00
|
|
|
unsafe { Generator::unsafe_new(stack, f) }
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Same as `new`, but does not require `stack` to have a guard page.
|
|
|
|
///
|
|
|
|
/// This function is unsafe because the generator function can easily violate
|
|
|
|
/// memory safety by overflowing the stack. It is useful in environments where
|
|
|
|
/// guarded stacks do not exist, e.g. in absence of an MMU.
|
2016-09-03 00:00:11 +08:00
|
|
|
///
|
|
|
|
/// See also the [contract](../trait.Stack.html) that needs to be fulfilled by `stack`.
|
2016-09-15 03:18:31 +08:00
|
|
|
pub unsafe fn unsafe_new<F>(stack: Stack, f: F) -> Generator<'a, Input, Output, Stack>
|
2016-09-16 16:34:49 +08:00
|
|
|
where F: FnOnce(&Yielder<Input, Output>, Input) + Send + 'a {
|
2016-09-07 20:56:32 +08:00
|
|
|
unsafe extern "C" fn generator_wrapper<Input, Output, Stack, F>(env: usize, stack_ptr: StackPointer) -> !
|
2016-09-25 18:02:17 +08:00
|
|
|
where Stack: stack::Stack, F: FnOnce(&Yielder<Input, Output>, Input) {
|
2016-07-17 11:11:33 +08:00
|
|
|
// Retrieve our environment from the callee and return control to it.
|
2016-09-07 20:56:32 +08:00
|
|
|
let f = ptr::read(env as *const F);
|
|
|
|
let (data, stack_ptr) = arch::swap(0, stack_ptr, None);
|
2016-08-30 18:37:15 +08:00
|
|
|
// See the second half of Yielder::suspend_bare.
|
2016-09-07 20:56:32 +08:00
|
|
|
let input = ptr::read(data as *const Input);
|
2016-07-17 11:11:33 +08:00
|
|
|
// Run the body of the generator.
|
2016-09-16 16:34:49 +08:00
|
|
|
let yielder = Yielder::new(stack_ptr);
|
|
|
|
f(&yielder, input);
|
2016-07-17 11:11:33 +08:00
|
|
|
// Past this point, the generator has dropped everything it has held.
|
2016-08-30 18:37:15 +08:00
|
|
|
loop { yielder.suspend_bare(None); }
|
2016-07-17 11:11:33 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 20:56:32 +08:00
|
|
|
let stack_id = debug::StackId::register(&stack);
|
|
|
|
let stack_ptr = arch::init(&stack, generator_wrapper::<Input, Output, Stack, F>);
|
2016-07-17 11:11:33 +08:00
|
|
|
|
2016-07-17 11:45:50 +08:00
|
|
|
// Transfer environment to the callee.
|
2016-09-07 20:56:32 +08:00
|
|
|
let stack_ptr = arch::swap(&f as *const F as usize, stack_ptr, Some(&stack)).1;
|
|
|
|
mem::forget(f);
|
|
|
|
|
|
|
|
Generator {
|
|
|
|
state: State::Runnable,
|
|
|
|
stack: stack,
|
|
|
|
stack_id: stack_id,
|
|
|
|
stack_ptr: stack_ptr,
|
2016-09-16 16:57:56 +08:00
|
|
|
phantom: PhantomData
|
2016-09-07 20:56:32 +08:00
|
|
|
}
|
2016-07-17 11:11:33 +08:00
|
|
|
}
|
|
|
|
|
2016-08-12 05:04:40 +08:00
|
|
|
/// Resumes the generator and return the next value it yields.
|
|
|
|
/// If the generator function has returned, returns `None`.
|
|
|
|
#[inline]
|
|
|
|
pub fn resume(&mut self, input: Input) -> Option<Output> {
|
|
|
|
match self.state {
|
|
|
|
State::Runnable => {
|
|
|
|
// Set the state to Unavailable. Since we have exclusive access to the generator,
|
|
|
|
// the only case where this matters is the generator function panics, after which
|
|
|
|
// it must not be invocable again.
|
|
|
|
self.state = State::Unavailable;
|
|
|
|
|
|
|
|
// Switch to the generator function, and retrieve the yielded value.
|
|
|
|
let val = unsafe {
|
2016-09-07 20:56:32 +08:00
|
|
|
let (data_out, stack_ptr) = arch::swap(&input as *const Input as usize, self.stack_ptr, Some(&self.stack));
|
|
|
|
self.stack_ptr = stack_ptr;
|
|
|
|
mem::forget(input);
|
|
|
|
ptr::read(data_out as *const Option<Output>)
|
2016-08-12 05:04:40 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
// Unless the generator function has returned, it can be switched to again, so
|
|
|
|
// set the state to Runnable.
|
|
|
|
if val.is_some() { self.state = State::Runnable }
|
|
|
|
|
|
|
|
val
|
|
|
|
}
|
|
|
|
State::Unavailable => None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-17 11:11:33 +08:00
|
|
|
/// Returns the state of the generator.
|
2016-08-13 18:52:12 +08:00
|
|
|
#[inline]
|
2016-07-17 11:11:33 +08:00
|
|
|
pub fn state(&self) -> State { self.state }
|
|
|
|
|
|
|
|
/// Extracts the stack from a generator when the generator function has returned.
|
|
|
|
/// If the generator function has not returned
|
2016-08-11 10:16:13 +08:00
|
|
|
/// (i.e. `self.state() == State::Runnable`), panics.
|
2016-07-17 11:11:33 +08:00
|
|
|
pub fn unwrap(self) -> Stack {
|
|
|
|
match self.state {
|
2016-08-11 10:16:13 +08:00
|
|
|
State::Runnable => panic!("Argh! Bastard! Don't touch that!"),
|
2016-09-07 20:56:32 +08:00
|
|
|
State::Unavailable => self.stack
|
2016-07-17 11:11:33 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Yielder is an interface provided to every generator through which it
|
|
|
|
/// returns a value.
|
|
|
|
#[derive(Debug)]
|
2016-09-25 18:02:17 +08:00
|
|
|
pub struct Yielder<Input, Output> {
|
2016-09-07 20:56:32 +08:00
|
|
|
stack_ptr: Cell<StackPointer>,
|
2016-09-16 16:59:47 +08:00
|
|
|
phantom: PhantomData<(*const Input, *mut Output)>
|
2016-07-17 11:11:33 +08:00
|
|
|
}
|
|
|
|
|
2016-09-25 18:02:17 +08:00
|
|
|
impl<Input, Output> Yielder<Input, Output> {
|
2016-09-07 20:56:32 +08:00
|
|
|
fn new(stack_ptr: StackPointer) -> Yielder<Input, Output> {
|
2016-07-17 11:11:33 +08:00
|
|
|
Yielder {
|
2016-09-07 20:56:32 +08:00
|
|
|
stack_ptr: Cell::new(stack_ptr),
|
2016-09-16 16:57:56 +08:00
|
|
|
phantom: PhantomData
|
2016-07-17 11:11:33 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline(always)]
|
2016-09-07 20:56:32 +08:00
|
|
|
fn suspend_bare(&self, val: Option<Output>) -> Input {
|
2016-07-17 11:11:33 +08:00
|
|
|
unsafe {
|
2016-09-07 20:56:32 +08:00
|
|
|
let (data, stack_ptr) = arch::swap(&val as *const Option<Output> as usize, self.stack_ptr.get(), None);
|
|
|
|
self.stack_ptr.set(stack_ptr);
|
2016-08-31 06:17:52 +08:00
|
|
|
mem::forget(val);
|
2016-09-07 20:56:32 +08:00
|
|
|
ptr::read(data as *const Input)
|
2016-07-17 11:11:33 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-12 05:04:40 +08:00
|
|
|
/// Suspends the generator and returns `Some(item)` from the `resume()`
|
2016-07-17 11:11:33 +08:00
|
|
|
/// invocation that resumed the generator.
|
|
|
|
#[inline(always)]
|
2016-09-07 10:58:37 +08:00
|
|
|
pub fn suspend(&self, item: Output) -> Input {
|
2016-08-30 18:37:15 +08:00
|
|
|
self.suspend_bare(Some(item))
|
2016-07-17 11:11:33 +08:00
|
|
|
}
|
|
|
|
}
|
2016-08-13 08:07:08 +08:00
|
|
|
|
2016-09-15 03:18:31 +08:00
|
|
|
impl<'a, Output, Stack> Iterator for Generator<'a, (), Output, Stack>
|
2016-09-25 18:02:17 +08:00
|
|
|
where Output: 'a, Stack: stack::Stack {
|
2016-08-13 08:07:08 +08:00
|
|
|
type Item = Output;
|
|
|
|
|
|
|
|
fn next(&mut self) -> Option<Self::Item> { self.resume(()) }
|
|
|
|
}
|