#thread-local #async-context #worker-thread #lifetime #await #points #run-time

async-local

For using thread locals within an async context and across await points

16 releases (7 stable)

2.0.1 Mar 24, 2024
2.0.0 Oct 19, 2023
1.2.0 Mar 27, 2023
0.15.4 Feb 20, 2023
0.3.0 Nov 30, 2022

#163 in Concurrency

Download history 24/week @ 2024-02-08 201/week @ 2024-02-15 122/week @ 2024-02-22 93/week @ 2024-02-29 100/week @ 2024-03-07 113/week @ 2024-03-14 224/week @ 2024-03-21 74/week @ 2024-03-28 61/week @ 2024-04-04 52/week @ 2024-04-11 44/week @ 2024-04-18

239 downloads per month
Used in 4 crates (3 directly)

MIT license

19KB
332 lines

Async Local

License Cargo Documentation

Unlocking the potential of thread-locals in an async context

This crate enables references to thread locals to be used in an async context and accross await points or within blocking threads managed by the Tokio runtime

How it works

By using a barrier to rendezvous worker threads during runtime shutdown, it can be gauranteed that no task will outlive thread local data belonging to worker threads. With this, pointers to thread locals constrained by invariant lifetimes are guaranteed to be of a valid lifetime suitable for use accross await points.

Runtime Configuration (optional)

For best performance, use the Tokio runtime as configured via the tokio::main or tokio::test macro with the crate attribute set to async_local while the barrier-protected-runtime feature is enabled on async-local. Doing so configures the Tokio runtime with a barrier that rendezvous runtime worker threads during shutdown in a way that ensures tasks never outlive thread local data owned by runtime worker threads and obviates the need for Box::leak as a fallback means of lifetime extension.

#[cfg(test)]*
mod tests {
  use std::sync::atomic::{AtomicUsize, Ordering};

  use async_local::{AsyncLocal, Context};
  use generativity::make_guard;
  use tokio::task::yield_now;

  thread_local! {
      static COUNTER: Context<AtomicUsize> = Context::new(AtomicUsize::new(0));
  }

  #[tokio::test(crate = "async_local", flavor = "multi_thread")]
  async fn it_increments() {
    make_guard!(guard);
    let counter = COUNTER.local_ref(guard);
    yield_now().await;
    counter.fetch_add(1, Ordering::SeqCst);
  }
}

Dependencies

~0.3–28MB
~389K SLoC