Make Generator safe in presence of destructors.
This commit is contained in:
parent
23cf17865d
commit
a5d3430e63
|
@ -18,10 +18,10 @@ use context;
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum State {
|
pub enum State {
|
||||||
/// Generator can be resumed. This is the initial state.
|
/// Generator can be resumed. This is the initial state.
|
||||||
Suspended,
|
Runnable,
|
||||||
/// Generator cannot be resumed. This is the state of the generator after
|
/// Generator cannot be resumed. This is the state of the generator after
|
||||||
/// the generator function has returned.
|
/// the generator function has returned or panicked.
|
||||||
Finished
|
Unavailable
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generator wraps a function and allows suspending its execution more than
|
/// Generator wraps a function and allows suspending its execution more than
|
||||||
|
@ -38,8 +38,8 @@ pub enum State {
|
||||||
/// stack using `unwrap()`.
|
/// stack using `unwrap()`.
|
||||||
///
|
///
|
||||||
/// `state()` can be used to determine whether the generator function has returned;
|
/// `state()` can be used to determine whether the generator function has returned;
|
||||||
/// the state is `State::Suspended` after creation and suspension, and `State::Finished`
|
/// the state is `State::Runnable` after creation and suspension, and `State::Unavailable`
|
||||||
/// once the generator function returns.
|
/// once the generator function returns or panics.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
|
@ -93,7 +93,7 @@ impl<Item, Stack> Generator<Item, Stack>
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut generator = Generator {
|
let mut generator = Generator {
|
||||||
state: State::Suspended,
|
state: State::Runnable,
|
||||||
context: context::Context::new(stack, generator_wrapper::<Item, Stack, F>),
|
context: context::Context::new(stack, generator_wrapper::<Item, Stack, F>),
|
||||||
phantom: PhantomData
|
phantom: PhantomData
|
||||||
};
|
};
|
||||||
|
@ -113,11 +113,11 @@ impl<Item, Stack> Generator<Item, Stack>
|
||||||
|
|
||||||
/// Extracts the stack from a generator when the generator function has returned.
|
/// Extracts the stack from a generator when the generator function has returned.
|
||||||
/// If the generator function has not returned
|
/// If the generator function has not returned
|
||||||
/// (i.e. `self.state() == State::Suspended`), panics.
|
/// (i.e. `self.state() == State::Runnable`), panics.
|
||||||
pub fn unwrap(self) -> Stack {
|
pub fn unwrap(self) -> Stack {
|
||||||
match self.state {
|
match self.state {
|
||||||
State::Suspended => panic!("Argh! Bastard! Don't touch that!"),
|
State::Runnable => panic!("Argh! Bastard! Don't touch that!"),
|
||||||
State::Finished => unsafe { self.context.unwrap() }
|
State::Unavailable => unsafe { self.context.unwrap() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -171,16 +171,26 @@ impl<Item, Stack> Iterator for Generator<Item, Stack>
|
||||||
#[inline]
|
#[inline]
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
match self.state {
|
match self.state {
|
||||||
State::Suspended => {
|
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.
|
||||||
let new_context = &mut self.context as *mut context::Context<Stack> as usize;
|
let new_context = &mut self.context as *mut context::Context<Stack> as usize;
|
||||||
let val = unsafe {
|
let val = unsafe {
|
||||||
ptr::read(context::Context::swap(&mut self.context, &self.context, new_context)
|
ptr::read(context::Context::swap(&mut self.context, &self.context, new_context)
|
||||||
as *mut Option<Item>)
|
as *mut Option<Item>)
|
||||||
};
|
};
|
||||||
if let None = val { self.state = State::Finished }
|
|
||||||
|
// 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
|
val
|
||||||
}
|
}
|
||||||
State::Finished => None
|
State::Unavailable => None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,8 +22,6 @@
|
||||||
//! [Stack](struct.Stack.html);
|
//! [Stack](struct.Stack.html);
|
||||||
//! * a stack allocator based on anonymous memory mappings with guard pages,
|
//! * a stack allocator based on anonymous memory mappings with guard pages,
|
||||||
//! [OsStack](struct.OsStack.html).
|
//! [OsStack](struct.OsStack.html).
|
||||||
//!
|
|
||||||
//! **FIXME:** not actually safe yet in presence of unwinding
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
|
|
|
@ -38,3 +38,25 @@ fn move_after_new() {
|
||||||
}
|
}
|
||||||
rest(gen);
|
rest(gen);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic]
|
||||||
|
fn panic_safety() {
|
||||||
|
struct Wrapper {
|
||||||
|
gen: Generator<u32, OsStack>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Wrapper {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.gen.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let stack = OsStack::new(4 << 20).unwrap();
|
||||||
|
let gen = Generator::new(stack, move |_yielder| {
|
||||||
|
panic!("foo")
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut wrapper = Wrapper { gen: gen };
|
||||||
|
wrapper.gen.next();
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue