diff --git a/Cargo.toml b/Cargo.toml index f8f5438dc..83021b150 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,7 @@ open = "1.2" structopt = "0.2" foreign-types = "0.3" winit = "0.18" +boxfnonce = "0.1" [target.'cfg(unix)'.dependencies] harfbuzz-sys = "~0.3" diff --git a/src/main.rs b/src/main.rs index 2d35dcafc..f12384a61 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,6 +22,7 @@ mod guicommon; mod guiloop; mod mux; mod opengl; +mod promise; mod server; use crate::guicommon::tabs::Tab; use crate::guiloop::GuiSelection; diff --git a/src/promise.rs b/src/promise.rs new file mode 100644 index 000000000..0dc395d9d --- /dev/null +++ b/src/promise.rs @@ -0,0 +1,238 @@ +use boxfnonce::SendBoxFnOnce; +use failure::Error; +use std::sync::{Arc, Condvar, Mutex}; + +type NextFunc = SendBoxFnOnce<'static, (Result,)>; + +enum PromiseState { + Waiting(Arc>), + Fulfilled, +} + +enum FutureState { + Waiting(Arc>), + Ready(Result), + Done, +} + +struct CoreData { + result: Option>, + propagate: Option>, +} + +struct Core { + data: Mutex>, + cond: Condvar, +} + +pub struct Promise { + state: PromiseState, + future: Option>, +} + +pub struct Future { + state: FutureState, +} + +impl Promise { + pub fn new() -> Self { + let core = Arc::new(Core { + data: Mutex::new(CoreData { + result: None, + propagate: None, + }), + cond: Condvar::new(), + }); + + Self { + state: PromiseState::Waiting(Arc::clone(&core)), + future: Some(Future { + state: FutureState::Waiting(core), + }), + } + } + + pub fn get_future(&mut self) -> Option> { + self.future.take() + } + + pub fn ok(&mut self, value: T) { + self.result(Ok(value)); + } + + pub fn err(&mut self, err: Error) { + self.result(Err(err)); + } + + pub fn result(&mut self, result: Result) { + match std::mem::replace(&mut self.state, PromiseState::Fulfilled) { + PromiseState::Waiting(core) => { + let mut locked = core.data.lock().unwrap(); + match locked.propagate.take() { + Some(func) => func.call(result), + None => locked.result = Some(result), + } + core.cond.notify_one(); + } + PromiseState::Fulfilled => panic!("Promise already fulfilled"), + } + } +} + +impl std::convert::From> for Future { + fn from(result: Result) -> Future { + Future::result(result) + } +} + +impl Future { + /// Create a leaf future which is immediately ready with + /// the provided value + pub fn ok(value: T) -> Self { + Self::result(Ok(value)) + } + + /// Create a leaf future which is immediately ready with + /// the provided error + pub fn err(err: Error) -> Self { + Self::result(Err(err)) + } + + /// Create a leaf future which is immediately ready with + /// the provided result + pub fn result(result: Result) -> Self { + Self { + state: FutureState::Ready(result), + } + } + + fn chain(self, f: NextFunc) { + match self.state { + FutureState::Done => panic!("chaining an already done future"), + FutureState::Ready(result) => { + f.call(result); + } + FutureState::Waiting(core) => { + let mut locked = core.data.lock().unwrap(); + if let Some(result) = locked.result.take() { + f.call(result); + } else { + locked.propagate = Some(f); + } + } + } + } + + /// Blocks until the associated promise is fulfilled + pub fn wait(self) -> Result { + match self.state { + FutureState::Waiting(core) => { + let mut locked = core.data.lock().unwrap(); + loop { + if let Some(result) = locked.result.take() { + return result; + } + locked = core.cond.wait(locked).unwrap(); + } + } + FutureState::Ready(result) => result, + FutureState::Done => bail!("Future is already done"), + } + } + + pub fn then(self, f: F) -> Future + where + F: FnOnce(Result) -> IF, + IF: Into>, + IF: 'static, + F: Send + 'static, + U: Send + 'static, + { + let mut promise = Promise::::new(); + let future = promise.get_future().unwrap(); + let func = SendBoxFnOnce::from(f); + + let promise_chain = NextFunc::from(move |result| promise.result(result)); + + self.chain(SendBoxFnOnce::from(move |result| { + let future = func.call(result).into(); + future.chain(promise_chain); + })); + future + } +} + +#[cfg(test)] +mod test { + use super::*; + #[test] + fn basic_promise() { + let mut p = Promise::new(); + p.ok(true); + assert_eq!(p.get_future().unwrap().wait().unwrap(), true); + } + + #[test] + fn basic_promise_future_first() { + let mut p = Promise::new(); + let f = p.get_future().unwrap(); + p.ok(true); + assert_eq!(f.wait().unwrap(), true); + } + + #[test] + fn promise_chain() { + let mut p = Promise::new(); + let f = p + .get_future() + .unwrap() + .then(|result| Ok(result.unwrap() + 1)) + .then(|result| Ok(result.unwrap() + 3)); + p.ok(1); + assert_eq!(f.wait().unwrap(), 5); + } + + #[test] + fn promise_chain_future() { + let mut p = Promise::new(); + let f = p + .get_future() + .unwrap() + .then(|result| Future::ok(result.unwrap() + 1)) + .then(|result| Ok(result.unwrap() + 3)); + p.ok(1); + assert_eq!(f.wait().unwrap(), 5); + } + + #[test] + fn promise_thread() { + let mut p = Promise::new(); + let f = p.get_future().unwrap(); + + std::thread::spawn(move || { + std::thread::sleep(std::time::Duration::new(0, 500)); + p.ok(123); + }); + + let f2 = f.then(move |result| Ok(result.unwrap() * 2)); + + assert_eq!(f2.wait().unwrap(), 246); + } + + #[test] + fn promise_thread_slow_chain() { + let mut p = Promise::new(); + let f = p.get_future().unwrap(); + + std::thread::spawn(move || { + std::thread::sleep(std::time::Duration::new(0, 500)); + p.ok(123); + }); + + std::thread::sleep(std::time::Duration::new(1, 0)); + + let f2 = f.then(move |result| Ok(result.unwrap() * 2)); + + assert_eq!(f2.wait().unwrap(), 246); + } +}