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

document a bunch of stuff

parent 32f592df
...@@ -2,7 +2,7 @@ use std::time::Duration; ...@@ -2,7 +2,7 @@ use std::time::Duration;
use actors::{Actor, LockAction}; use actors::{Actor, LockAction};
use input::Event; use input::Event;
use util::wakeable::{spawn_wakeable, prepare_wakeable}; use util::wakeable::Wakeable;
use brain::environment::*; use brain::environment::*;
use brain::handlers; use brain::handlers;
use brain::{Actors, State}; use brain::{Actors, State};
...@@ -23,7 +23,7 @@ impl State for UnLockingState { ...@@ -23,7 +23,7 @@ impl State for UnLockingState {
// TODO: abort if already locked // TODO: abort if already locked
let locking = self.locking; let locking = self.locking;
for _ in 0..3 { for _ in 0..3 {
let locker = spawn_wakeable(actors.lock, (), move |t, mut lock_actor, _| { let locker = Wakeable::spawn(actors.lock, (), move |t, mut lock_actor, _| {
lock_actor.act(if locking { lock_actor.act(if locking {
LockAction::PressLock LockAction::PressLock
} else { } else {
...@@ -65,7 +65,7 @@ impl OpenState { ...@@ -65,7 +65,7 @@ impl OpenState {
impl State for OpenState { impl State for OpenState {
fn run(&mut self, mut actors: Actors, env: &mut Environment) -> Option<Box<State>> { fn run(&mut self, mut actors: Actors, env: &mut Environment) -> Option<Box<State>> {
let mut buzzer = prepare_wakeable(actors.buzz, move |t, mut buzz_actor, d| { let mut buzzer = Wakeable::new(actors.buzz, move |t, mut buzz_actor, d| {
buzz_actor.act(true); buzz_actor.act(true);
t.sleep(d); t.sleep(d);
buzz_actor.act(false); buzz_actor.act(false);
...@@ -74,7 +74,7 @@ impl State for OpenState { ...@@ -74,7 +74,7 @@ impl State for OpenState {
let r = handle_events!(env, let r = handle_events!(env,
EventHandler::new_safe(|ev| { EventHandler::new_safe(|ev| {
if let Event::Bell(true) = ev { if let Event::Bell(true) = ev {
buzzer.spawn(Duration::from_secs(2)); buzzer.respawn(Duration::from_secs(2));
} }
})); }));
actors.buzz = buzzer.terminate(); actors.buzz = buzzer.terminate();
......
//! This module implements *wakeable*, *ownership-threading* threads.
//! "Wakeable" here means that that thread can be "woken up" if it uses
//! the special sleep function provided to it, so that it terminates earlier.
//! "Onwership-threading" means that a piece of exclusive ownership T is handed
//! to the thread when it is created -- but before it is even run -- which will
//! be passed to the actual closure that runs in the new thread. The closure
//! has to return that ownership again.
//! Because of this threading, a weakeable thread can at any time be asked
//! to give back the T that is has. If necessary, it will stop the thread to
//! get hold of the T faster.
use std::thread; use std::thread;
use std::time::Duration; use std::time::Duration;
use std::sync::{Arc, Mutex, Condvar}; use std::sync::{Arc, Mutex, Condvar};
use take_mut; use take_mut;
#[derive(PartialEq,Eq,Debug,Copy,Clone)]
pub enum ShouldThreadRun {
Yes,
No,
}
struct ThreadState { /// Represents a Wakeable thread govering T, running closure F.
mutex: Mutex<ShouldThreadRun>, pub struct Wakeable<T, F> {
cond: Condvar, w_state: WakeableState<T>,
fun: Arc<F>,
} }
/// The state of a Wakeable: Either the thread has not been spawned yet,
/// or we have a handle to get the T when returned by the thread, plus
/// a way to talk to it (the ThreadState).
enum WakeableState<T> { enum WakeableState<T> {
Lazy(T), Lazy(T),
Running(thread::JoinHandle<T>, Arc<ThreadState>) Running(thread::JoinHandle<T>, Arc<ThreadState>)
} }
pub struct WakeableHandle<T, F> { impl<T, F> Wakeable<T, F> {
w_state: WakeableState<T>, // invariant: This is never empty when control is to the user /// This constructor creates a new Wakeable with the given owned data T
fun: Arc<F>, /// and closure F. No thread is started, the wakeable is only prepared.
} /// The type D describes additional data that can be passed to F whenever
/// it is triggered.
pub struct WakeableThread { pub fn new<D>(t: T, f: F) -> Wakeable<T, F>
state: Arc<ThreadState>,
}
pub fn prepare_wakeable<D, F, T>(t: T, f: F) -> WakeableHandle<T, F>
where D: Send + 'static, where D: Send + 'static,
T: Send + 'static, T: Send + 'static,
F: Fn(WakeableThread, T, D) -> T + Send + Sync + 'static F: Fn(WakeableThread, T, D) -> T + Send + Sync + 'static
{ {
WakeableHandle { Wakeable {
w_state: WakeableState::Lazy(t), w_state: WakeableState::Lazy(t),
fun: Arc::new(f), fun: Arc::new(f),
} }
} }
pub fn spawn_wakeable<D, F, T>(t: T, d: D, f: F) -> WakeableHandle<T, F> /// Like [new], but immediately spawns the thread
pub fn spawn<D>(t: T, d: D, f: F) -> Wakeable<T, F>
where D: Send + 'static, where D: Send + 'static,
T: Send + 'static, T: Send + 'static,
F: Fn(WakeableThread, T, D) -> T + Send + Sync + 'static F: Fn(WakeableThread, T, D) -> T + Send + Sync + 'static
{ {
let mut handle = prepare_wakeable(t, f); let mut handle = Wakeable::new(t, f);
handle.spawn(d); handle.respawn(d);
handle handle
}
impl<T> WakeableState<T> {
fn get_t(self) -> T {
match self {
WakeableState::Lazy(t) => t,
WakeableState::Running(handle, state) => {
state.wakeup();
handle.join().unwrap() // panics only if the thread paniced
},
}
} }
}
impl<T, F> WakeableHandle<T, F> { /// (Re-)spawns the wakeable thread, potentially stopping the still running
pub fn spawn<D>(&mut self, d: D) /// current rhead.
pub fn respawn<D>(&mut self, d: D)
where D: Send + 'static, where D: Send + 'static,
T: Send + 'static, T: Send + 'static,
F: Fn(WakeableThread, T, D) -> T + Send + Sync + 'static F: Fn(WakeableThread, T, D) -> T + Send + Sync + 'static
...@@ -84,12 +80,64 @@ impl<T, F> WakeableHandle<T, F> { ...@@ -84,12 +80,64 @@ impl<T, F> WakeableHandle<T, F> {
}); });
} }
/// Returns the T managed by the Wakeable, potentially termianting a still
/// running thread.
pub fn terminate(self) -> T { pub fn terminate(self) -> T {
self.w_state.get_t() self.w_state.get_t()
} }
} }
impl<T> WakeableState<T> {
/// Extracts the T from this state, by terminating the thread if necessary.
fn get_t(self) -> T {
match self {
WakeableState::Lazy(t) => t,
WakeableState::Running(handle, state) => {
state.wakeup();
handle.join().unwrap() // panics only if the thread paniced
},
}
}
}
/// This structure is provided to thrads spawned by Wakeable, It provides
/// the thread-side API of this module.
pub struct WakeableThread {
state: Arc<ThreadState>,
}
/// Defines whether the thread should be running right now, or whether it
/// should try to terminate as quickly as possible.
#[derive(PartialEq,Eq,Debug,Copy,Clone)]
pub enum ShouldThreadRun {
Yes,
No,
}
impl WakeableThread {
/// Sleeps for the given duration, making sure that we immediately wake
/// up if the thread should terminate.
/// Returns whether the thread should terminate, i.e., whether we slept
/// for the full lngth.
pub fn sleep(&self, d: Duration) -> ShouldThreadRun {
self.state.sleep(d)
}
/// Returns the current "should we terminate" state.
pub fn should_terminate(&self) -> bool {
self.state.should_terminate()
}
}
/// Manages the state of a thread spawned by Wakeable. used to signal
/// "wake up" to sleeping threads.
struct ThreadState {
mutex: Mutex<ShouldThreadRun>,
cond: Condvar,
}
impl ThreadState { impl ThreadState {
/// Signals the thread to "wake up".
fn wakeup(&self) { fn wakeup(&self) {
// 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.mutex.lock().unwrap(); let mut should_run = self.mutex.lock().unwrap();
...@@ -97,7 +145,8 @@ impl ThreadState { ...@@ -97,7 +145,8 @@ impl ThreadState {
self.cond.notify_one(); self.cond.notify_one();
} }
pub fn sleep(&self, d: Duration) -> ShouldThreadRun { /// Sleeps for the given duration, waking up earlier if signalled.
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.mutex.lock().unwrap(); let should_run = self.mutex.lock().unwrap();
// check if we have to terminate early // check if we have to terminate early
...@@ -108,10 +157,11 @@ impl ThreadState { ...@@ -108,10 +157,11 @@ impl ThreadState {
let (should_run, _) = self.cond.wait_timeout(should_run, d).unwrap(); let (should_run, _) = self.cond.wait_timeout(should_run, d).unwrap();
return *should_run; return *should_run;
} }
}
impl WakeableThread { /// Returns whether the thread should terminate.
pub fn sleep(&self, d: Duration) -> ShouldThreadRun { fn should_terminate(&self) -> bool {
self.state.sleep(d) // We panic if the lock is poisened, which only happens if someone paniced
let should_run = self.mutex.lock().unwrap();
return *should_run == ShouldThreadRun::No
} }
} }
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