Struct io_context::Context
[−]
[src]
pub struct Context { /* fields omitted */ }
A context that carries a deadline, cancelation signals and request scoped values across API boundaries and between processes.
A cancelation signal can be added using the add_cancel_signal
method.
Deadlines and timeouts can be added using the add_deadline
and
add_timeout
methods. While add_value
adds a value to the context.
For more information and examples see the crate level documentation.
Child contexts
A child context can be created by using the freeze
method on the parent
context, and then using the create_child
method to create a child
context. This should be done on a per request basis. This way each request
has each own deadline and can be canceled if the connection is closed.
Any cancelation signals, deadlines, timeouts and values from the parent context will be shared between all the child contexts and all it's children, and it's children's children, etc. So if a parent context is canceled so are all it's children.
However adding or changing anything (like adding values or cancelation signals) on a child context will not affect the parent context. See the example below.
fn main() { // First create our parent context. let mut parent_ctx = Context::background(); // We can use this `cancel_all` signal to handle ctrl-c. let parent_cancel_signal = parent_ctx.add_cancel_signal(); // Now we freeze the parent context so it can be used to create child // contexts. let parent_ctx = parent_ctx.freeze(); // Create child context from the parent context. let mut child_ctx = Context::create_child(&parent_ctx); // Add a cancel signal to the child context. let child_cancel_signal = child_ctx.add_cancel_signal(); // Oh no! the connection was closed, now we need to cancel the child // context. This will only cancel the child context. child_cancel_signal.cancel(); assert!(child_ctx.done().is_some()); assert!(parent_ctx.done().is_none()); // Parent context is still going strong. // Now the user pressed ctrl-c and we'll cancel the parent context and // it's child context. parent_cancel_signal.cancel(); assert!(child_ctx.done().is_some()); assert!(parent_ctx.done().is_some()); }
Conventions
The context name is often shortend to ctx
in code as seen in all the
examples throughout the documentation of this crate. In documentation
however the full word "context" is used.
Another convention is that the context is the first parameter of a function
(after self
), so it's easier to see if an API supports the context. See
get_value
for an example of this.
Contexts should not stored in structs, that is anti-pattern. Instead
functions and methods that need a context should accept it as first
parameter. This is also why Context
does not implement common traits like
Debug
and Hash
etc.
Methods
impl Context
[src]
fn background() -> Context
Create an empty background context. It has no deadline or cancelation
signals attached to it. It should be used as the top-level context of
which children should be derived on a per request basis. See the crate documentation
for a detailed example.
fn add_cancel_signal(&mut self) -> CancelSignal
Add cancelation to the context. The signalthat is returned will cancel
the context and it's children once called (see cancel
). A single
context can have multiple cancelation signals, after executing a
cancelation the other signals will have no effect.
fn add_deadline(&mut self, deadline: Instant)
Add a deadline to the context. If the current deadline is sooner then the provided deadline this method does nothing.
See done
for example usage.
fn add_timeout(&mut self, timeout: Duration)
A convience method to add a deadline to the context which is the current
time plus the timeout
.
See done
for example usage.
fn add_value<V>(&mut self, key: &'static str, value: V) where
V: Any + Send + Sync + Sized,
V: Any + Send + Sync + Sized,
Add a value to the context. It overwrites any previously set values with
the same key. Because of this it's advised to keep key
private in a
library or module, see get_value
for more.
fn add_boxed_value<V>(&mut self, key: &'static str, value: Box<V>) where
V: Any + Send + Sync + Sized,
V: Any + Send + Sync + Sized,
This method does the same thing as add_value
, but reuses the Box
.
fn deadline(&self) -> Option<Instant>
Get the deadline for this operation, if any. This is mainly useful for checking if a long job should be started or not, e.g. if a job takes 5 seconds to execute and only 1 second is left on the deadline we're better of skipping the job entirely.
If you only want to check if the deadline was exceeded use done
instead. Because this also checks if the context was canceled.
fn done(&self) -> Option<DoneReason>
Check if the context is done. If it returns None
the operation may
proceed. But if it returns something the operation should be stopped,
this is the case if the context was canceled or if the deadline is
exceeded.
Example
use std::time::Duration; fn main() { let mut ctx = Context::background(); ctx.add_timeout(Duration::from_secs(5)); loop { // Do some work. do_some_work(); // Check if the context deadline is exceeded, or if it was // canceled. if let Some(reason) = ctx.done() { println!("Stopping work because: {}", reason); break; } } }
fn is_done(&self) -> Result<(), DoneReason>
This does the same thing as done
, but returns a Result
instead so
it can be used with the Try
operator (?
). For usage see the
example below.
Example
use std::io; use std::time::Duration; fn main() { let mut ctx = Context::background(); ctx.add_timeout(Duration::from_secs(5)); loop { match do_some_work(&ctx) { Ok(()) => (), Err(err) => { println!("work stopped: {}", err); break; }, } } } fn do_some_work(ctx: &Context) -> Result<(), io::Error> { ctx.is_done()?; // Do some work. Ok(()) }
fn get_value<V>(&self, key: &'static str) -> Option<&V> where
V: Any + Send + Sync + Sized,
V: Any + Send + Sync + Sized,
Get a value from the context. If no value is stored in the Context
under the provided key
, or if the stored value doesn't have type V
,
this will return None
.
Rather then letting the user of a library retrieve a value from the
Context
manually, a library or module should define add_to_context
and get_from_context
functions, like in the example below.
Example
In a library or module.
// The key used in `Context`. This should be private. const REQUEST_ID_KEY: &'static str = "MY_LIBRARY_REQUEST_ID_KEY"; /// Add a `RequestId` to the provided `Context`. pub fn add_request_id(ctx: &mut Context, request_id: RequestId) { ctx.add_value(REQUEST_ID_KEY, request_id) } /// Retrieve a `RequestId` from the provided `Context`. pub fn get_request_id(ctx: &Context) -> Option<&RequestId> { ctx.get_value(REQUEST_ID_KEY) }
In the application code.
fn main() { // Create our new context. let mut ctx = Context::background(); // Add our `RequestId` to it. add_request_id(&mut ctx, 123); // Retrieve our `RequestId` later on. assert_eq!(get_request_id(&ctx), Some(123)); }
fn freeze(self) -> Arc<Context>
Freeze the Context
so it can be used to create child contexts, see
create_child
. The parent context can no longer be modified after
it's frozen and can only be used to create children.
See the Context
documentation for an example.