#local #points #run-time

async-local

For using thread locals within an async context across await points

24 releases (15 stable)

Uses new Rust 2024

new 6.0.1 Mar 25, 2025
5.0.1 Mar 18, 2025
4.0.0 Mar 3, 2025
3.0.2 Feb 23, 2025
0.3.0 Nov 30, 2022

#174 in Concurrency

Download history 114/week @ 2024-12-05 130/week @ 2024-12-12 78/week @ 2024-12-19 37/week @ 2024-12-26 60/week @ 2025-01-02 86/week @ 2025-01-09 92/week @ 2025-01-16 38/week @ 2025-01-23 206/week @ 2025-01-30 136/week @ 2025-02-06 97/week @ 2025-02-13 598/week @ 2025-02-20 404/week @ 2025-02-27 169/week @ 2025-03-06 216/week @ 2025-03-13 270/week @ 2025-03-20

1,093 downloads per month
Used in 3 crates (2 directly)

MIT license

24KB
471 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 across await points or within blocking threads managed by the Tokio runtime

How it works

By configuring Tokio with a barrier to rendezvous worker threads during 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

The optimization that this crate provides require that the async_local::main or async_local::test macro be used to configure the Tokio runtime. This is enforced by a pre-main check that asserts async_local::main has been used.

Compatibility Mode

Enabling the compat feature flag will allow this crate to be used with any runtime configuration by disabling the performance optimization this crate provides and instead internally using std::sync::Arc

Example usage

#[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));
  }

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

Dependencies

~0.4–24MB
~327K SLoC