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}