#flow #reference-counting #logicalcallcontext #callcontext

execution-context

An experimental .NET inspired execution context

1 unstable release

Uses old Rust 2015

0.1.0 Jun 8, 2018

#77 in #reference-counting

Apache-2.0

20KB
254 lines

Execution Context for Rust

This implements a .NET inspired execution context. The idea is that something like this could become a core language concept if it can be shown to be reasonably performant.

What are execution contexts?

An execution context is a container for a logical call flow. The idea is that any code that follows the same flow of execution can access flow-local data. An example where this is useful is security relevant data that code might want to carry from operation to operation without accidentally dropping it.

This gives you the most trivial example:

flow_local!(static TEST: u32 = 42);

assert_eq!(*TEST.get(), 42);
let ec = ExecutionContext::capture();
TEST.set(23);

assert_eq!(*TEST.get(), 23);
ec.run(|| {
    assert_eq!(*TEST.get(), 42);
});

Execution contexts can be forwarded to other threads and an API is provided to temporarily or permanently suppress the flow propagation.


lib.rs:

This crate implements an execution context for Rust. An execution context carries data through a logical flow of execution.

This is heavily inspired by the .NET ExecutionContext system as an experiment if such an API would make sense in Rust.

The main differences to the system in .NET:

  • This purely implements an execution context and not a synchronization context. As such the implementation is simplified.
  • This provides the ability to permanently disable the flow propagation.
  • This crate refers to "ambient data" and "async locals" as "flow-local data".
  • Capturing always returns an execution context even if the flow is suppressed. Consequently the run method is no longer static but stored on the instance.
  • This uses atomic reference counting underneath the hood instead of using a garbage collector. This also means that performance characteristics will be different.
  • FlowLocal data have a initialization expression that is invoked if no local data is stored in the current flow.

Example Usage

#[macro_use]
extern crate execution_context;

use execution_context::ExecutionContext;
use std::env;
use std::thread;

flow_local!(static LOCALE: String = env::var("LANG").unwrap_or_else(|_| "en_US".into()));

fn main() {
    println!("the current locale is {}", LOCALE.get());
    LOCALE.set("de_DE".into());
    println!("changing locale to {}", LOCALE.get());

    let ec = ExecutionContext::capture();
    thread::spawn(move || {
        ec.run(|| {
            println!("the locale in the child thread is {}", LOCALE.get());
            LOCALE.set("fr_FR".into());
            println!("the new locale in the child thread is {}", LOCALE.get());
        });
    }).join().unwrap();

    println!("the locale of the parent thread is again {}", LOCALE.get());
}

This example will give the following output assuming no default language was set as environment variable (or the environment variable is en_US):

the current locale is en_US
changing locale to de_DE
the locale in the child thread is de_DE
the new locale in the child thread is fr_FR
the locale of the parent thread is again de_DE

Dependencies

~560KB
~12K SLoC