heddle-runtime-bridge 0.3.1

Sync→async runtime bridge for Heddle: worker thread + private Tokio runtime, safe to call from any caller flavor.
Documentation
  • Coverage
  • 100%
    8 out of 8 items documented0 out of 5 items with examples
  • Size
  • Source code size: 20.17 kB This is the summed size of all the files inside the crates.io package for this release.
  • Documentation size: 457.22 kB This is the summed size of all files generated by rustdoc for all configured targets
  • Ø build duration
  • this release: 9s Average build duration of successful builds.
  • all releases: 9s Average build duration of successful builds in releases after 2024-10-23.
  • Links
  • HeddleCo/heddle
    6 0 80
  • crates.io
  • Dependencies
  • Versions
  • Owners
  • forge-zephyr

Sync→async runtime bridge for Heddle.

[RuntimeBridge] is a worker thread that owns a private current-thread Tokio runtime. Synchronous code hands futures to the worker over an async mpsc channel; the worker tokio::spawns each future on its own runtime and the caller blocks on a per-request reply channel until the task completes. The caller's runtime (if any) is never re-entered.

Why

Heddle has several ObjectStore / OpLogBackend / RefBackend implementations whose trait surface is synchronous but whose underlying I/O is async (aws-sdk-s3, sqlx, etc.). A naive bridge — Handle::current().block_on(...) or tokio::task::block_in_place(|| Handle::current().block_on(...)) — breaks in caller-flavor-dependent ways:

  • Handle::current().block_on(...) panics with "Cannot start a runtime from within a runtime" when the caller is already on a Tokio runtime.
  • block_in_place(...) panics with "can call blocking only when running on the multi-threaded runtime" when the caller is on a current-thread runtime (e.g. #[tokio::test(flavor = "current_thread")]).
  • Neither works at all when the caller is on a non-Tokio thread.

Routing through this bridge sidesteps all three: the future runs on the bridge's private runtime regardless of who calls it, and the caller's thread simply blocks on a reply channel.

Concurrency

The worker dispatches each request via [tokio::spawn] rather than awaiting it inline, so concurrent callers sharing one bridge can progress in parallel on the worker's runtime. This preserves the connection-level parallelism of pools like sqlx::PgPool instead of head-of-line blocking every caller behind the slowest in-flight query.

Error recovery

[RuntimeBridge::block_on] returns [Result<T, BridgeError>] so a dead worker surfaces as a recoverable error in the caller's Result-typed API rather than escalating into a process-level panic. A bridged task that panics aborts only that task: its reply channel is dropped and the waiting caller observes [BridgeError::ResponseLost]; the worker keeps serving other requests.

Shutdown

Dropping the bridge drops the Sender; the worker's Receiver::recv then returns None, the loop breaks, and the runtime is dropped on the worker thread. The JoinHandle is retained on the bridge so the thread isn't reaped before its in-flight requests drain.