Commit 2abf701b authored by Ralf's avatar Ralf
Browse files

change API around to embrace scopes, and avoid pitfalls

parent c5972091
...@@ -12,7 +12,7 @@ use self::environment::*; ...@@ -12,7 +12,7 @@ use self::environment::*;
use self::event_handler::*; use self::event_handler::*;
use actors::Actor; use actors::Actor;
use input::Event; use input::Event;
use util::Wakeable; use util::wakeable::SpawnWakeable;
#[derive(Copy,Clone,Debug,PartialEq,Eq)] #[derive(Copy,Clone,Debug,PartialEq,Eq)]
enum LockAction { enum LockAction {
...@@ -46,7 +46,7 @@ impl State for UnLockingState { ...@@ -46,7 +46,7 @@ impl State for UnLockingState {
let cancel = scope(|scope| { let cancel = scope(|scope| {
// it is important that we keep this thread alife, so remember the return value // it is important that we keep this thread alife, so remember the return value
// TODO: Change the API around so that this pitfall no longer exists // TODO: Change the API around so that this pitfall no longer exists
let _ = Wakeable::new(scope, move |t| { scope.spawn_wakeable(move |t| {
lock_actor.act(if locking { lock_actor.act(if locking {
LockAction::PressLock LockAction::PressLock
} else { } else {
...@@ -54,7 +54,6 @@ impl State for UnLockingState { ...@@ -54,7 +54,6 @@ impl State for UnLockingState {
}); });
t.sleep(Duration::from_millis(100)); t.sleep(Duration::from_millis(100));
lock_actor.act(LockAction::Release); lock_actor.act(LockAction::Release);
lock_actor
}); });
let r = handle_events_timeout!(env, let r = handle_events_timeout!(env,
Duration::from_secs(1), Duration::from_secs(1),
......
...@@ -2,8 +2,7 @@ use std::time::Duration; ...@@ -2,8 +2,7 @@ use std::time::Duration;
use std::thread; use std::thread;
use chan; use chan;
mod wakeable; pub mod wakeable;
pub use self::wakeable::*;
pub fn timeout_chan(d: Duration) -> chan::Receiver<()> { pub fn timeout_chan(d: Duration) -> chan::Receiver<()> {
let (s, r) = chan::sync(0); let (s, r) = chan::sync(0);
......
use std::time::Duration; use std::time::Duration;
use std::sync::{Arc, Mutex, Condvar}; use std::sync::{Arc, Mutex, Condvar};
use crossbeam::{Scope, ScopedJoinHandle}; use crossbeam::Scope;
#[derive(PartialEq,Eq,Debug,Copy,Clone)] #[derive(PartialEq,Eq,Debug,Copy,Clone)]
pub enum ShouldThreadRun { pub enum ShouldThreadRun {
...@@ -8,53 +8,45 @@ pub enum ShouldThreadRun { ...@@ -8,53 +8,45 @@ pub enum ShouldThreadRun {
No, No,
} }
pub struct Wakeable<T: Send> { pub struct WakeableThread {
join: Option<ScopedJoinHandle<T>>, // invariant: This will always be a Some outside of the destructor
state: Arc<ThreadState>,
}
pub struct Thread {
state: Arc<ThreadState>, state: Arc<ThreadState>,
} }
struct ThreadState { struct ThreadState {
mutex: Mutex<ShouldThreadRun>, mutex: Mutex<ShouldThreadRun>,
cond: Condvar, cond: Condvar,
} }
impl<T: Send> Wakeable<T> { pub trait SpawnWakeable<'a> {
pub fn new<'a, F>(scope: &Scope<'a>, f: F) -> Self fn spawn_wakeable<F>(&self, f: F) where
where F: FnOnce(Thread) -> T + Send + 'a, T: 'a F: FnOnce(WakeableThread) + Send + 'a;
}
impl<'a> SpawnWakeable<'a> for Scope<'a> {
fn spawn_wakeable<F>(&self, f: F) where
F: FnOnce(WakeableThread) + Send + 'a
{ {
let state = Arc::new(ThreadState { let state = Arc::new(ThreadState {
mutex: Mutex::new(ShouldThreadRun::Yes), mutex: Mutex::new(ShouldThreadRun::Yes),
cond: Condvar::new(), cond: Condvar::new(),
}); });
let thread = Thread { state: state.clone() }; let thread = WakeableThread { state: state.clone() };
Wakeable {
join: Some(scope.spawn(move || f(thread))), self.spawn(move || f(thread) );
state: state,
} // Now defer the action that we want to wake up this thread.
} // This will be executed *before* the join deferred by spawn.
fn make_terminate(&self) { self.defer(move || {
// We panic if the lock is poisened, which only happens if someone paniced // We panic if the lock is poisened, which only happens if someone paniced
let mut should_run = self.state.mutex.lock().unwrap(); let mut should_run = state.mutex.lock().unwrap();
*should_run = ShouldThreadRun::No; *should_run = ShouldThreadRun::No;
self.state.cond.notify_one(); state.cond.notify_one();
} });
pub fn terminate(mut self) -> T {
self.make_terminate();
self.join.take().unwrap().join() // this asserts that there was a "Some" in the join handle, which will always be the case
}
}
impl<T: Send> Drop for Wakeable<T> {
fn drop(&mut self) {
self.make_terminate();
} }
} }
impl Thread { impl WakeableThread {
pub fn sleep(&self, d: Duration) -> ShouldThreadRun { pub fn sleep(&self, d: Duration) -> ShouldThreadRun {
// We panic if the lock is poisened, which only happens if someone paniced // We panic if the lock is poisened, which only happens if someone paniced
let should_run = self.state.mutex.lock().unwrap(); let should_run = self.state.mutex.lock().unwrap();
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment