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::*;
use self::event_handler::*;
use actors::Actor;
use input::Event;
use util::Wakeable;
use util::wakeable::SpawnWakeable;
#[derive(Copy,Clone,Debug,PartialEq,Eq)]
enum LockAction {
......@@ -46,7 +46,7 @@ impl State for UnLockingState {
let cancel = scope(|scope| {
// 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
let _ = Wakeable::new(scope, move |t| {
scope.spawn_wakeable(move |t| {
lock_actor.act(if locking {
LockAction::PressLock
} else {
......@@ -54,7 +54,6 @@ impl State for UnLockingState {
});
t.sleep(Duration::from_millis(100));
lock_actor.act(LockAction::Release);
lock_actor
});
let r = handle_events_timeout!(env,
Duration::from_secs(1),
......
......@@ -2,8 +2,7 @@ use std::time::Duration;
use std::thread;
use chan;
mod wakeable;
pub use self::wakeable::*;
pub mod wakeable;
pub fn timeout_chan(d: Duration) -> chan::Receiver<()> {
let (s, r) = chan::sync(0);
......
use std::time::Duration;
use std::sync::{Arc, Mutex, Condvar};
use crossbeam::{Scope, ScopedJoinHandle};
use crossbeam::Scope;
#[derive(PartialEq,Eq,Debug,Copy,Clone)]
pub enum ShouldThreadRun {
......@@ -8,53 +8,45 @@ pub enum ShouldThreadRun {
No,
}
pub struct Wakeable<T: Send> {
join: Option<ScopedJoinHandle<T>>, // invariant: This will always be a Some outside of the destructor
state: Arc<ThreadState>,
}
pub struct Thread {
pub struct WakeableThread {
state: Arc<ThreadState>,
}
struct ThreadState {
mutex: Mutex<ShouldThreadRun>,
cond: Condvar,
}
impl<T: Send> Wakeable<T> {
pub fn new<'a, F>(scope: &Scope<'a>, f: F) -> Self
where F: FnOnce(Thread) -> T + Send + 'a, T: 'a
pub trait SpawnWakeable<'a> {
fn spawn_wakeable<F>(&self, f: F) where
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 {
mutex: Mutex::new(ShouldThreadRun::Yes),
cond: Condvar::new(),
});
let thread = Thread { state: state.clone() };
Wakeable {
join: Some(scope.spawn(move || f(thread))),
state: state,
}
}
let thread = WakeableThread { state: state.clone() };
fn make_terminate(&self) {
// We panic if the lock is poisened, which only happens if someone paniced
let mut should_run = self.state.mutex.lock().unwrap();
*should_run = ShouldThreadRun::No;
self.state.cond.notify_one();
}
self.spawn(move || f(thread) );
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
}
}
// Now defer the action that we want to wake up this thread.
// This will be executed *before* the join deferred by spawn.
impl<T: Send> Drop for Wakeable<T> {
fn drop(&mut self) {
self.make_terminate();
self.defer(move || {
// We panic if the lock is poisened, which only happens if someone paniced
let mut should_run = state.mutex.lock().unwrap();
*should_run = ShouldThreadRun::No;
state.cond.notify_one();
});
}
}
impl Thread {
impl WakeableThread {
pub fn sleep(&self, d: Duration) -> ShouldThreadRun {
// We panic if the lock is poisened, which only happens if someone paniced
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