BlockingLruCache panics inside Tokio current-thread runtimes

Open 💬 0 comments Opened Jul 18, 2026 by Kevinjohn

BlockingLruCache panics when used inside Tokio's current-thread runtime instead of falling back to disabled-cache behavior.

What issue are you seeing?

  • Observable sequence: construct a BlockingLruCache inside a Tokio current_thread runtime and call insert.
  • Expected: the call completes and the cache behaves as disabled, consistent with calls made outside a Tokio runtime.
  • Actual: tokio::task::block_in_place panics because it only supports the multi-threaded runtime.
  • Before revision: 312caf176a.
  • Fix rebased and validated on current main: 74bfbda9b5.
  • Environment: macOS 26.5.2, Rust 1.95.0.
Actual screenshots

Before — upstream 312caf176a: the current-thread reproduction panics at block_in_place.

!Terminal reproduction showing BlockingLruCache panicking in a Tokio current-thread runtime

After — current main at 74bfbda9b5 with the focused patch: the named regression test passes without a panic.

!Terminal test showing BlockingLruCache safely disabled on a Tokio current-thread runtime

What steps can reproduce the bug?

Run this against codex-utils-cache and Tokio:

use codex_utils_cache::BlockingLruCache;
use std::num::NonZeroUsize;

#[tokio::main(flavor = "current_thread")]
async fn main() {
    let cache = BlockingLruCache::new(NonZeroUsize::new(2).unwrap());
    cache.insert("first", 1);
}

The panic occurs on the insert call:

thread 'main' panicked at utils/cache/src/lib.rs:127:10:
can call blocking only when running on the multi-threaded runtime

What is the expected behavior?

A current-thread runtime should use the existing disabled-cache fallback: cache operations should not panic, and an inserted entry should not be retained.

Additional information

lock_if_runtime currently checks only that a Tokio handle exists, then unconditionally calls block_in_place:

tokio::runtime::Handle::try_current().ok()?;
Some(tokio::task::block_in_place(|| m.blocking_lock()))

Handle::try_current() succeeds on both Tokio runtime flavors, but block_in_place panics on a current-thread runtime.

A focused patch is prepared that:

  • reads the current Tokio runtime flavor before calling block_in_place;
  • returns None on a current-thread runtime, reusing the cache's existing disabled behavior;
  • adds a #[tokio::test(flavor = "current_thread")] regression test proving insert does not panic and the value is not retained.

Validation completed:

  • just test -p codex-utils-cache: 4 passed;
  • just fix -p codex-utils-cache;
  • just fmt.

---

I have a PR ready if issue accepted

View original on GitHub ↗