io_context/
lib.rs

1// Copyright 2017 Thomas de Zeeuw
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT
5// or http://opensource.org/licenses/MIT>, at your option. This file may not be
6// used, copied, modified, or distributed except according to those terms.
7
8//! This crate defines a [`Context`], which carries deadlines, cancelations
9//! signals and request scoped values across API boundaries.
10//!
11//! Incoming server requests should create a request specific `Context` from the
12//! [`background context`]. While outgoing requests to servers should accept a
13//! `Context` in there methods to allow for cancelation and deadlines. A chain
14//! of funtions calls for handling a request should propagate the `Context`,
15//! optionally adding their own deadlines or cancelation signals. As demostrated
16//! in the example below.
17//!
18//! [`Context`]: struct.Context.html
19//! [`background context`]: struct.Context.html#method.background
20//!
21//! ```
22//! # extern crate io_context;
23//! # use io_context::Context;
24//! # use std::io;
25//! fn main() {
26//!     // First create our background context. To this context we could add
27//!     // signal handling, e.g. when the user pressed ctrl-c.
28//!     let mut ctx = Context::background();
29//!     // This signal should be canceled once ctrl-c is pressed.
30//!     let cancel_signal = ctx.add_cancel_signal();
31//!     let ctx = ctx.freeze();
32//!     loop {
33//!         // Create a context for our request. This will copy any deadlines
34//!         // and cancelation signals from the background context into the
35//!         // request specific one.
36//!         //
37//!         // However adding a deadline or cancelation signal to the client
38//!         // will not after the background context.
39//!         let request_ctx = Context::create_child(&ctx);
40//!
41//!         // Read a request.
42//!         let request = read_request();
43//!         // Pass the request context along with the request to the request
44//!         // handler.
45//!         let response = handle_request(request_ctx, request).unwrap();
46//!         println!("got response: {:?}", response);
47//!         # break; // Don't loop forever while testing.
48//!     }
49//! }
50//! #
51//! # #[derive(Debug)]
52//! # struct Request;
53//! # #[derive(Debug)]
54//! # struct Response;
55//! #
56//! # fn read_request() -> Request {
57//! #     Request
58//! # }
59//!
60//! fn handle_request(ctx: Context, request: Request) -> io::Result<Response> {
61//!     // Check if the context's deadline was exceeded, or if the context was
62//!     // canceled.
63//!     if let Some(reason) = ctx.done() {
64//!         // For convience `DoneReason`, returned by `Context.done`, can be
65//!         // converted into an `io::Error`.
66//!         return Err(reason.into());
67//!     }
68//!
69//!     // Make request to an external server, passing our request context.
70//!     make_http_request(ctx, "https://api.example.com".to_owned())
71//! }
72//!
73//! // An outgoing request should accept a `Context` as first parameter.
74//! fn make_http_request(ctx: Context, url: String) -> io::Result<Response> {
75//!     // Again checking the context is done.
76//!     if let Some(reason) = ctx.done() {
77//!         return Err(reason.into());
78//!     }
79//!     // Here any deadlines should be added to the HTTP request, using
80//!     // `context.deadline` method to retrieve it.
81//!
82//!     // But we'll fake the response here, for the sake of simplicity.
83//!     Ok(Response)
84//! }
85//! ```
86//!
87//! Contexts should not stored in structs. Instead functions and methods that
88//! need a context should accept it as first parameter. For more conventions see
89//! the [`Context convention`] documentation. A possible exception for this is a
90//! struct that implements a [`Future`], which represents the state of that
91//! future.
92//!
93//! [`Context convention`]: struct.Context.html#conventions
94//! [`Future`]: https://docs.rs/futures/*/futures/future/trait.Future.html
95//!
96//! # Usage
97//!
98//! In the `main` function of the program a background context should be
99//! created, which is used in creating all child (request specific) contexts.
100//!
101//! Request deadlines can be added using the [`add_deadline`] and [`add_timeout`]
102//! methods defined on [`Context`]. While cancelation signals can be added using
103//! the [`add_cancel_signal`] method. Request scoped values, e.g. a request id,
104//! can be added using the [`add_value`] method. For examples see below and the
105//! documentation of different linked methods.
106//!
107//! [`add_deadline`]: struct.Context.html#method.add_deadline
108//! [`add_timeout`]: struct.Context.html#method.add_timeout
109//! [`add_cancel_signal`]: struct.Context.html#method.add_cancel_signal
110//! [`add_value`]: struct.Context.html#method.add_value
111//!
112//! ```
113//! # extern crate io_context;
114//! # use io_context::Context;
115//! fn main() {
116//!     // Create a background context.
117//!     let mut ctx = Context::background();
118//!
119//!     // Add cancelation to the context. We can use this to respond to a
120//!     // kill signal from the user, e.g. when pressing ctrl-c.
121//!     let cancel_signal = ctx.add_cancel_signal();
122//!
123//!     // ... Some time later when we received a kill signal.
124//!     cancel_signal.cancel(); // Now the context and it's children will be canceled.
125//! }
126//! ```
127//!
128//! ## In libraries
129//!
130//! Libraries should accept contexts in their API, the convention is to accept
131//! it as first parameter in all methods and functions that need it. Do not
132//! store a context in a struct. For more conventions see the [`Context
133//! convention`] documentation.
134//!
135//! ```
136//! # extern crate io_context;
137//! # use io_context::Context;
138//! use std::io;
139//! use std::time::Duration;
140//!
141//! struct Connection { /* Some fields. */ }
142//!
143//! impl Connection {
144//!     // This executes some long runnning operation.
145//!     fn execute_operation(&self, ctx: Context, input: &[u8]) -> io::Result<()> {
146//!         // Do step 1 of the work.
147//!         work_step_1(input);
148//!
149//!         // Check if the context is done, e.g. if the deadline has exceeded.
150//!         if let Some(reason) = ctx.done() {
151//!             return Err(reason.into());
152//!         }
153//!
154//!         // Do some more work.
155//!         work_step_2();
156//!
157//!         // Check the context again.
158//!         if let Some(reason) = ctx.done() {
159//!             rollback();
160//!             return Err(reason.into());
161//!         }
162//!
163//!         // More work, etc.
164//!         work_step_3();
165//!
166//!         // Of course we can have even more preciese control if we pass the
167//!         // context to our work functions as well, that is omitted in this
168//!         // example.
169//!
170//!         Ok(())
171//!     }
172//! }
173//!
174//! fn main() {
175//!     let mut ctx = Context::background();
176//!     ctx.add_timeout(Duration::from_secs(5));
177//!     let ctx = ctx.freeze();
178//!
179//!     // We create a new child context just for the specific operation. Any
180//!     // deadlines or cancelation signals added to the parent will be added
181//!     // to the child context as well. However any deadlines or cancelation
182//!     // signals added to the child will not affect the parent.
183//!     let mut child_ctx = Context::create_child(&ctx);
184//!     child_ctx.add_timeout(Duration::from_secs(1));
185//!
186//!     // Now we execute the operation, while controlling it's deadline. We
187//!     // could also add a cancelation signal so we could stop it from another
188//!     // thread (or future), see `Context.add_cancel_signal`.
189//!     let connection = Connection{};
190//!     connection.execute_operation(child_ctx, b"some input").unwrap();
191//! }
192//! # fn work_step_1(_: &[u8]) {}
193//! # fn work_step_2() {}
194//! # fn work_step_3() {}
195//! # fn rollback() {}
196//! ```
197//!
198//! ## When should a Context be used?
199//!
200//! A `Context` is mainly useful if an operation is long running. As a rule of
201//! thumb; if an operation is less then 10 milliseconds it is not worth it to
202//! add a `Context` to the operation.
203//!
204//! Examples of when adding a `Context` is worth it:
205//!
206//! * Any operation which involve any kind of I/O (network, disk or otherwise).
207//! * Any operation which support cancelation or timeouts already.
208//! * Long looping operations which process input in batches.
209//!
210//! The common theme in the example operations above is the fact that they all
211//! could be or are long running. If you, both as a developer and user of your
212//! application, would like more control over these kind of operations the use
213//! of a `Context` is a good fit.
214
215#[cfg(feature = "context-future")]
216extern crate futures;
217
218use std::{fmt, io};
219use std::any::Any;
220use std::collections::HashMap;
221use std::error::Error;
222use std::sync::Arc;
223use std::sync::atomic::{AtomicBool, Ordering};
224use std::time::{Duration, Instant};
225
226#[cfg(feature = "context-future")]
227mod future;
228#[cfg(feature = "context-future")]
229pub use future::ContextFuture;
230
231/// A context that carries a deadline, cancelation signals and request scoped
232/// values across API boundaries and between processes.
233///
234/// A cancelation signal can be added using the [`add_cancel_signal`] method.
235/// Deadlines and timeouts can be added using the [`add_deadline`] and
236/// [`add_timeout`] methods. While [`add_value`] adds a value to the context.
237///
238/// For more information and examples see the crate level documentation.
239///
240/// [`add_cancel_signal`]: struct.Context.html#method.add_cancel_signal
241/// [`add_deadline`]: struct.Context.html#method.add_deadline
242/// [`add_timeout`]: struct.Context.html#method.add_timeout
243/// [`add_value`]: struct.Context.html#method.add_value
244///
245/// # Child contexts
246///
247/// A child context can be created by using the [`freeze`] method on the parent
248/// context, and then using the [`create_child`] method to create a child
249/// context. This should be done on a per request basis. This way each request
250/// has each own deadline and can be canceled if the connection is closed.
251///
252/// Any cancelation signals, deadlines, timeouts and values from the parent
253/// context will be shared between all the child contexts and all it's children,
254/// and it's children's children, etc. So if a parent context is canceled so
255/// are all it's children.
256///
257/// However adding or changing anything (like adding values or cancelation
258/// signals) on a child context will not affect the parent context. See the
259/// example below.
260///
261/// [`freeze`]: struct.Context.html#method.freeze
262/// [`create_child`]: struct.Context.html#method.create_child
263///
264/// ```
265/// # extern crate io_context;
266/// # use io_context::Context;
267/// fn main() {
268///     // First create our parent context.
269///     let mut parent_ctx = Context::background();
270///     // We can use this `cancel_all` signal to handle ctrl-c.
271///     let parent_cancel_signal = parent_ctx.add_cancel_signal();
272///     // Now we freeze the parent context so it can be used to create child
273///     // contexts.
274///     let parent_ctx = parent_ctx.freeze();
275///
276///     // Create child context from the parent context.
277///     let mut child_ctx = Context::create_child(&parent_ctx);
278///     // Add a cancel signal to the child context.
279///     let child_cancel_signal = child_ctx.add_cancel_signal();
280///
281///     // Oh no! the connection was closed, now we need to cancel the child
282///     // context. This will only cancel the child context.
283///     child_cancel_signal.cancel();
284///     assert!(child_ctx.done().is_some());
285///     assert!(parent_ctx.done().is_none()); // Parent context is still going strong.
286///
287///     // Now the user pressed ctrl-c and we'll cancel the parent context and
288///     // it's child context.
289///     parent_cancel_signal.cancel();
290///     assert!(child_ctx.done().is_some());
291///     assert!(parent_ctx.done().is_some());
292/// }
293/// ```
294///
295/// # Conventions
296///
297/// The context name is often shortend to `ctx` in code as seen in all the
298/// examples throughout the documentation of this crate. In documentation
299/// however the full word "context" is used.
300///
301/// Another convention is that the context is the first parameter of a function
302/// (after `self`), so it's easier to see if an API supports the context. See
303/// [`get_value`] for an example of this.
304///
305/// Contexts should not stored in structs, that is anti-pattern. Instead
306/// functions and methods that need a context should accept it as first
307/// parameter. This is also why `Context` does not implement common traits like
308/// [`Debug`] and [`Hash`] etc.
309///
310/// [`get_value`]: struct.Context.html#method.get_value
311/// [`Debug`]: https://doc.rust-lang.org/nightly/core/fmt/trait.Debug.html
312/// [`Hash`]: https://doc.rust-lang.org/nightly/core/hash/trait.Hash.html
313pub struct Context {
314    /// An optional parent context. Only a context created with
315    /// `Context::background` will have this set to `None`.
316    parent: Option<Arc<Context>>,
317    /// Wether or not this context is canceled. After this is set to false it
318    /// can't be set to true after. An `Arc` is needed because the context can
319    /// create multiple `CancelSignal`s.
320    canceled: Arc<AtomicBool>,
321    /// An optional deadline.
322    deadline: Option<Instant>,
323    /// A collection of read only values stored in this context.
324    values: Option<HashMap<&'static str, Box<Any + Send + Sync>>>,
325}
326
327impl Context {
328    /// Create an empty background context. It has no deadline or cancelation
329    /// signals attached to it. It should be used as the top-level context of
330    /// which children should be derived on a per request basis. See the [`crate
331    /// documentation`] for a detailed example.
332    ///
333    /// [`crate documentation`]: index.html
334    pub fn background() -> Context {
335        Context {
336            parent: None,
337            canceled: Arc::new(AtomicBool::new(false)),
338            deadline: None,
339            values: None,
340        }
341    }
342
343    /// Add cancelation to the context. The signalthat is returned will cancel
344    /// the context and it's children once called (see [`cancel`]). A single
345    /// context can have multiple cancelation signals, after executing a
346    /// cancelation the other signals will have no effect.
347    ///
348    /// [`cancel`]: struct.CancelSignal.html#method.cancel
349    pub fn add_cancel_signal(&mut self) -> CancelSignal {
350        let canceled = Arc::clone(&self.canceled);
351        CancelSignal::new(canceled)
352    }
353
354    /// Add a deadline to the context. If the current deadline is sooner then
355    /// the provided deadline this method does nothing.
356    ///
357    /// See [`done`] for example usage.
358    ///
359    /// [`done`]: struct.Context.html#method.done
360    pub fn add_deadline(&mut self, deadline: Instant) {
361        match self.deadline {
362            Some(current_deadline) if current_deadline < deadline => (),
363            _ => self.deadline = Some(deadline),
364        }
365    }
366
367    /// A convience method to add a deadline to the context which is the current
368    /// time plus the `timeout`.
369    ///
370    /// See [`done`] for example usage.
371    ///
372    /// [`done`]: struct.Context.html#method.done
373    pub fn add_timeout(&mut self, timeout: Duration) {
374        self.add_deadline(Instant::now() + timeout)
375    }
376
377    /// Add a value to the context. It overwrites any previously set values with
378    /// the same key. Because of this it's advised to keep `key` private in a
379    /// library or module, see [`get_value`] for more.
380    ///
381    /// [`get_value`]: struct.Context.html#method.get_value
382    pub fn add_value<V>(&mut self, key: &'static str, value: V)
383        where V: Any + Send + Sync + Sized,
384    {
385        self.add_boxed_value(key, Box::new(value))
386    }
387
388    /// This method does the same thing as [`add_value`], but reuses the `Box`.
389    ///
390    /// [`add_value`]: struct.Context.html#method.add_value
391    pub fn add_boxed_value<V>(&mut self, key: &'static str, value: Box<V>)
392        where V: Any + Send + Sync + Sized,
393    {
394        if let Some(ref mut values) = self.values {
395            values.insert(key, value);
396        } else {
397            self.values = Some(HashMap::new());
398            self.add_boxed_value(key, value)
399        }
400    }
401
402    /// Get the deadline for this operation, if any. This is mainly useful for
403    /// checking if a long job should be started or not, e.g. if a job takes 5
404    /// seconds to execute and only 1 second is left on the deadline we're
405    /// better of skipping the job entirely.
406    ///
407    /// If you only want to check if the deadline was exceeded use [`done`]
408    /// instead. Because this also checks if the context was canceled.
409    ///
410    /// [`done`]: struct.Context.html#method.done
411    pub fn deadline(&self) -> Option<Instant> {
412        self.deadline
413    }
414
415    /// Check if the context is done. If it returns `None` the operation may
416    /// proceed. But if it returns something the operation should be stopped,
417    /// this is the case if the context was canceled or if the deadline is
418    /// exceeded.
419    ///
420    /// ## Example
421    ///
422    /// ```
423    /// # extern crate io_context;
424    /// # use io_context::Context;
425    /// # fn do_some_work() {}
426    /// use std::time::Duration;
427    ///
428    /// fn main() {
429    ///     let mut ctx = Context::background();
430    ///     ctx.add_timeout(Duration::from_secs(5));
431    ///     loop {
432    ///         // Do some work.
433    ///         do_some_work();
434    ///
435    ///         // Check if the context deadline is exceeded, or if it was
436    ///         // canceled.
437    ///         if let Some(reason) = ctx.done() {
438    ///             println!("Stopping work because: {}", reason);
439    ///             break;
440    ///         }
441    ///         # break;
442    ///     }
443    /// }
444    /// ```
445    pub fn done(&self) -> Option<DoneReason> {
446        match self.deadline {
447            Some(deadline) if deadline <= Instant::now() => Some(DoneReason::DeadlineExceeded),
448            // TODO: see if we can relax the ordering.
449            _ if self.canceled.load(Ordering::SeqCst) => Some(DoneReason::Canceled),
450            _ => match self.parent {
451                Some(ref parent_ctx) => parent_ctx.done(),
452                _ => None,
453            },
454        }
455    }
456
457    /// This does the same thing as [`done`], but returns a `Result` instead so
458    /// it can be used with the [`Try`] operator (`?`). For usage see the
459    /// example below.
460    ///
461    /// ## Example
462    ///
463    /// ```
464    /// # extern crate io_context;
465    /// # use io_context::Context;
466    /// use std::io;
467    /// use std::time::Duration;
468    ///
469    /// fn main() {
470    ///     let mut ctx = Context::background();
471    ///     ctx.add_timeout(Duration::from_secs(5));
472    ///     loop {
473    ///         match do_some_work(&ctx) {
474    ///             Ok(()) => (),
475    ///             Err(err) => {
476    ///                 println!("work stopped: {}", err);
477    ///                 break;
478    ///             },
479    ///         }
480    ///         # break;
481    ///     }
482    /// }
483    ///
484    /// fn do_some_work(ctx: &Context) -> Result<(), io::Error> {
485    ///     ctx.is_done()?;
486    ///
487    ///     // Do some work.
488    ///     Ok(())
489    /// }
490    /// ```
491    ///
492    /// [`done`]: struct.Context.html#method.done
493    /// [`Try`]: https://doc.rust-lang.org/nightly/core/ops/trait.Try.html
494    pub fn is_done(&self) -> Result<(), DoneReason> {
495        match self.done() {
496            Some(reason) => Err(reason),
497            None => Ok(()),
498        }
499    }
500
501    /// Get a value from the context. If no value is stored in the `Context`
502    /// under the provided `key`, or if the stored value doesn't have type `V`,
503    /// this will return `None`.
504    ///
505    /// Rather then letting the user of a library retrieve a value from the
506    /// `Context` manually, a library or module should define `add_to_context`
507    /// and `get_from_context` functions, like in the example below.
508    ///
509    /// ## Example
510    ///
511    /// In a library or module.
512    ///
513    /// ```
514    /// # extern crate io_context;
515    /// # use io_context::Context;
516    /// # fn main() {} // To let the example compile.
517    /// # pub type RequestId = u64;
518    /// // The key used in `Context`. This should be private.
519    /// const REQUEST_ID_KEY: &'static str = "MY_LIBRARY_REQUEST_ID_KEY";
520    ///
521    /// /// Add a `RequestId` to the provided `Context`.
522    /// pub fn add_request_id(ctx: &mut Context, request_id: RequestId) {
523    ///     ctx.add_value(REQUEST_ID_KEY, request_id)
524    /// }
525    ///
526    /// /// Retrieve a `RequestId` from the provided `Context`.
527    /// pub fn get_request_id(ctx: &Context) -> Option<&RequestId> {
528    ///     ctx.get_value(REQUEST_ID_KEY)
529    /// }
530    /// ```
531    ///
532    /// In the application code.
533    ///
534    /// ```
535    /// # extern crate io_context;
536    /// # use io_context::Context;
537    /// # pub type RequestId = u64;
538    /// # pub fn add_request_id(_ctx: &mut Context, _request_id: RequestId) {}
539    /// # pub fn get_request_id(_ctx: &Context) -> Option<RequestId> { Some(123) }
540    /// fn main() {
541    ///     // Create our new context.
542    ///     let mut ctx = Context::background();
543    ///     // Add our `RequestId` to it.
544    ///     add_request_id(&mut ctx, 123);
545    ///     // Retrieve our `RequestId` later on.
546    ///     assert_eq!(get_request_id(&ctx), Some(123));
547    /// }
548    /// ```
549    pub fn get_value<V>(&self, key: &'static str) -> Option<&V>
550        where V: Any + Send + Sync + Sized,
551    {
552        if let Some(ref values) = self.values {
553            if let Some(value) = values.get(&key) {
554                let value: &Any = &**value;
555                return value.downcast_ref::<V>();
556            }
557        }
558        match self.parent {
559            Some(ref parent_ctx) => parent_ctx.get_value(key),
560            _ => None,
561        }
562    }
563
564    /// Freeze the `Context` so it can be used to create child contexts, see
565    /// [`create_child`]. The parent context can no longer be modified after
566    /// it's frozen and can only be used to create children.
567    ///
568    /// See the [`Context`] documentation for an example.
569    ///
570    /// [`create_child`]: struct.Context.html#method.create_child
571    /// [`Context`]: struct.Context.html#child-contexts
572    pub fn freeze(mut self) -> Arc<Context> {
573        if let Some(ref mut values) = self.values {
574            values.shrink_to_fit();
575        }
576        Arc::new(self)
577    }
578
579    /// Create a new child from a frozen `Context`, see [`freeze`].
580    ///
581    /// See the [`Context`] documentation for an example.
582    ///
583    /// [`freeze`]: struct.Context.html#method.freeze
584    /// [`Context`]: struct.Context.html#child-contexts
585    pub fn create_child(parent_ctx: &Arc<Context>) -> Context {
586        Context {
587            parent: Some(Arc::clone(parent_ctx)),
588            canceled: Arc::new(AtomicBool::new(false)),
589            deadline: parent_ctx.deadline,
590            values: None,
591        }
592    }
593}
594
595impl fmt::Debug for Context {
596    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
597        f.debug_struct("Context")
598            .field("parent", &self.parent)
599            .field("cancled", &self.canceled)
600            .field("deadline", &self.deadline)
601            .field("values", &self.values.as_ref().map(|_| "values"))
602            .finish()
603    }
604}
605
606/// A cancelation signal, see [`Context.add_cancel_signal`]. See the crate
607/// documentation for an example.
608///
609/// [`Context.add_cancel_signal`]: struct.Context.html#method.add_cancel_signal
610/// [`crate documentation`]: index.html
611pub struct CancelSignal {
612    /// A reference to the canceled value of a context.
613    canceled: Arc<AtomicBool>,
614}
615
616impl CancelSignal {
617    /// Create a new `CancelSignal`.
618    fn new(canceled: Arc<AtomicBool>) -> CancelSignal {
619        CancelSignal {
620            canceled: canceled,
621        }
622    }
623
624    /// Cancel the context.
625    pub fn cancel(self) {
626        // TODO: see if we can relax the ordering.
627        self.canceled.store(true, Ordering::SeqCst);
628    }
629}
630
631/// The reason why a context was stopped, see [`Context.done`]. This "error" can
632/// be turned into an [`io::Error`] by using the [`Into`] trait
633/// ([`From`]`<DoneReason>` is implemented for [`io::Error`]).
634///
635/// [`Context.done`]: struct.Context.html#method.done
636/// [`io::Error`]: https://doc.rust-lang.org/nightly/std/io/struct.Error.html
637/// [`Into`]: https://doc.rust-lang.org/nightly/core/convert/trait.Into.html
638/// [`From`]: https://doc.rust-lang.org/nightly/core/convert/trait.From.html
639#[derive(Copy, Clone, Eq, PartialEq, Debug)]
640pub enum DoneReason {
641    /// The deadline was exceeded.
642    DeadlineExceeded,
643
644    /// The context was canceled.
645    Canceled,
646}
647
648impl DoneReason {
649    /// Convert the `DoneReason` into an error.
650    pub fn into_error<E>(self) -> E where E: From<io::Error> {
651        Into::<io::Error>::into(self).into()
652    }
653}
654
655impl Error for DoneReason {
656    fn description(&self) -> &str {
657        use self::DoneReason::*;
658        match *self {
659            DeadlineExceeded => "context deadline exceeded",
660            Canceled => "context canceled",
661        }
662    }
663}
664
665impl fmt::Display for DoneReason {
666    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
667        f.pad(self.description())
668    }
669}
670
671impl From<DoneReason> for io::Error {
672    fn from(reason: DoneReason) -> Self {
673        use self::DoneReason::*;
674        let kind = match reason {
675            DeadlineExceeded => io::ErrorKind::TimedOut,
676            Canceled => io::ErrorKind::Other,
677        };
678        io::Error::new(kind, reason.description())
679    }
680}