use std::time::{Duration, Instant};
use std::any::Any;
use {Context, InnerContext, ContextError, with_cancel};
use futures::{Future, Poll, Async};
use tokio_timer::{Timer, Sleep};
pub struct WithDeadline {
parent: Context,
when: Instant,
deadline: Sleep,
}
impl InnerContext for WithDeadline {
fn deadline(&self) -> Option<Instant> {
Some(self.when)
}
fn value(&self) -> Option<&Any> {
None
}
fn parent(&self) -> Option<Context> {
self.parent.0.borrow().parent()
}
}
impl Future for WithDeadline {
type Item = ();
type Error = ContextError;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
match self.deadline.poll() {
Ok(Async::Ready(_)) => Err(ContextError::DeadlineExceeded),
Ok(Async::NotReady) => self.parent.poll(),
Err(_) => Err(ContextError::DeadlineTooLong),
}
}
}
pub fn with_deadline(parent: Context,
deadline: Instant)
-> (Context, Box<Fn() + Send>) {
with_timeout(parent, deadline - Instant::now())
}
pub fn with_timeout(parent: Context, timeout: Duration) -> (Context, Box<Fn() + Send>) {
let timer = Timer::default();
let (parent, cancel) = with_cancel(parent);
let ctx = WithDeadline {
parent: parent,
when: Instant::now() + timeout,
deadline: timer.sleep(timeout),
};
(Context::new(ctx), cancel)
}
#[cfg(test)]
mod test {
use std::time::{Instant, Duration};
use std::thread;
use tokio_timer::Timer;
use with_deadline::with_timeout;
use {background, ContextError};
use futures::Future;
#[test]
fn cancel_test() {
let (ctx, cancel) = with_timeout(background(), Duration::new(2, 0));
cancel();
assert_eq!(ctx.wait().unwrap_err(), ContextError::Canceled);
}
#[test]
fn deadline_test() {
let duration = Duration::new(0, 50);
let when = Instant::now() + duration;
let (ctx, _) = with_timeout(background(), duration);
assert!(ctx.deadline().unwrap() - when < Duration::from_millis(10));
thread::sleep(Duration::from_millis(100));
assert_eq!(ctx.wait().unwrap_err(), ContextError::DeadlineExceeded);
}
#[test]
fn deadline_nested_test() {
let (parent, _) = with_timeout(background(), Duration::from_millis(50));
let (ctx, _) = with_timeout(parent, Duration::from_secs(10));
thread::sleep(Duration::from_millis(100));
assert_eq!(ctx.wait().unwrap_err(), ContextError::DeadlineExceeded);
}
#[test]
fn example_test() {
let timer = Timer::default();
let long_running_process = timer.sleep(Duration::from_secs(2));
let (ctx, _) = with_timeout(background(), Duration::new(0, 100));
let first = long_running_process
.map_err(|_| ContextError::Canceled)
.select(ctx);
let result = first.wait();
assert!(result.is_err());
match result {
Err((err, _)) => assert_eq!(err, ContextError::DeadlineExceeded),
_ => assert!(false),
}
}
#[test]
fn clone_test() {
let duration = Duration::new(0, 50);
let when = Instant::now() + duration;
let (ctx, _) = with_timeout(background(), duration);
let clone = ctx.clone();
assert!(clone.deadline().unwrap() - when < Duration::from_millis(10));
thread::sleep(Duration::from_millis(100));
assert_eq!(ctx.wait().unwrap_err(), ContextError::DeadlineExceeded);
assert_eq!(clone.wait().unwrap_err(), ContextError::DeadlineExceeded);
}
}