use std::sync::{Arc};
use latch::Latch;
use std::sync::mpsc::{channel,Sender,Receiver};
use std::thread::{Thread};
use std::thread;
use std::cell::{UnsafeCell};
use std::mem;
#[derive(Clone)]
pub struct Promise<T: Send+'static> {
pub data: Arc<UnsafeCell<Option<T>>>,
pub init: Latch,
pub commit: Latch,
}
#[derive(Clone)]
pub struct Promisee<T: Send+'static> {
pub p: Promise<T>,
sink: Sender<Thread>,
}
pub struct Promiser<T: Send+'static> {
p: Promise<T>,
sink: Receiver<Thread>,
}
unsafe impl<T: Send> Send for Promise<T> {}
unsafe impl<T: Sync + Send> Sync for Promise<T> {}
impl<T: Send+'static> Promise<T> {
pub fn new () -> (Promiser<T>,Promisee<T>) {
let (t,r) = channel();
let d: Option<T> = None;
let p = Promise { data: Arc::new(UnsafeCell::new(d)),
init: Latch::new(),
commit: Latch::new()};
let p2 = p.clone();
let pt = Promiser { p: p,
sink: r };
let pr = Promisee { p: p2,
sink: t };
(pt,pr)
}
pub fn clone (&self) -> Promise<T> {
Promise { data: self.data.clone(),
init: self.init.clone(),
commit: self.commit.clone(),}
}
fn _deliver (&self, d:Option<T>) -> bool {
if self.init.close() {
let w = self.data.get();
unsafe{ *w = d; }
self.commit.close();
return true
}
return false
}
pub fn deliver (&self, d:T) -> bool {
self._deliver(Some(d))
}
pub fn _with<W,F:FnMut(&T)->W> (&self, mut f:F) -> Result<W,String> {
let v = self.data.get();
unsafe {
match *v {
Some(ref r) => Ok(f(&*r)),
None => Err("promise signaled early, value not present!".to_string()),
}
}
}
pub fn destroy (&self) -> Result<String,String> {
if self._deliver(None) {
Ok("Promise signaled early".to_string())
}
else { Err("promise already delivered".to_string()) }
}
}
impl<T: Send+'static> Drop for Promise<T> {
fn drop (&mut self) {
if Arc::strong_count(&self.data) < 3 {
let _ =self.destroy();
}
}
}
impl<T: Send+'static> Promiser<T> {
pub fn deliver (&self, d:T) -> bool {
let r = self.p.deliver(d);
self.wakeup();
r
}
pub fn wakeup (&self) {
let mut s = self.sink.try_recv();
while s.is_ok() {
s.unwrap().unpark();
s = self.sink.try_recv();
}
}
}
impl<T: Send+'static> Drop for Promiser<T> {
fn drop (&mut self) {
let _ = self.p.destroy();
self.wakeup();
}
}
impl<T: Send+'static> Promisee<T> {
pub fn with<W,F:FnMut(&T)->W> (&self,f:F) -> Result<W,String> {
match self.wait() {
Ok(_) => self.p._with(f),
Err(er) => Err(er),
}
}
pub fn wait(&self) -> Result<(),String> {
if !self.p.commit.latched() { if !self.p.init.latched() { if Arc::strong_count(&self.p.data) < 2 {
return Err("safety hatch, promise not capable".to_string());
}
let _ = self.sink.send(thread::current()); if !self.p.commit.latched() { thread::park();
}
}
}
Ok(())
}
pub fn get(&self) -> Result<Option<&T>,String> {
if !self.p.init.latched() { if Arc::strong_count(&self.p.data) < 2 {
return Err("safety hatch, promise not capable".to_string());
}
else { Ok(None) } }
else { if self.p.commit.latched() { let d = self.p.data.get();
unsafe {
let r = match *d {
Some(_) => true,
None => false,
};
if r { Ok(mem::transmute(&*d)) }
else { Err("promise signaled early, value not present!".to_string()) }
}
}
else { Ok(None) } }
}
pub fn clone(&self) -> Promisee<T> {
Promisee { p: self.p.clone(),
sink: self.sink.clone(), }
}
}
#[cfg(test)]
mod tests {
extern crate rand;
use Promise;
use std::thread;
#[test]
fn test_promise_linear() {
let (pt,pr) = Promise::new();
assert_eq!(pt.deliver(1),true);
assert_eq!(pr.get(),Ok(Some(&1)));
assert_eq!(pt.deliver(2),false);
assert_eq!(pr.with(|x| *x).unwrap(),1);
let pr2 = pr.clone();
assert_eq!(pr2.with(|x| *x).unwrap(),1);
}
#[test]
fn test_promise_threaded() {
let (pt,pr) = Promise::new();
thread::spawn(move || {
assert_eq!(pt.deliver(1),true);
});
assert_eq!(pr.with(|x| *x).unwrap(),1); }
#[test]
#[should_panic]
fn test_promise_threaded_panic_safely() {
let (pt,pr) = Promise::new();
thread::spawn (move || {
if true {
panic!("proc dead"); }
let _ = pt.deliver(1);
});
pr.with(|x| *x).unwrap();
}
#[test]
fn test_promise_threaded_panic_safely2() {
let (pt,pr) = Promise::new();
thread::spawn (move || {
if true {
panic!("proc dead"); }
assert!(pt.deliver(1));
});
pr.get().ok();
}
}