Implements the Queue shutdown improvements requested in #9844.

Problem

When a worker fiber processing queue messages fails, it needs to propagate the failure to:

  1. Fibers currently blocked on take or offer
  2. Future callers attempting to interact with the queue
  3. Buffered items already in the queue (for cleanup)

The existing shutdown only sends a generic interrupt, making it impossible for callers to distinguish why the queue shut down.

API

// Shutdown with a specific cause — returns buffered items for cleanup
val buffered: UIO[Chunk[Request]] = queue.shutdownCause(Cause.fail(ConnectionClosed("host unreachable")))
// Convenience: wrap a value in Cause.fail
val buffered: UIO[Chunk[Request]] = queue.shutdownWith(ConnectionClosed("host unreachable"))
// Inspect the shutdown cause
val maybeReason: UIO[Option[Cause[Any]]] = queue.shutdownCauseOption
// Fibers blocked on take() receive the cause:
queue.take.catchAll { e: ConnectionClosed => handleError(e) }

Implementation

  • Queue.shutdownCause(cause: Cause[Any]): UIO[Chunk[A]] — atomic shutdown with cause propagation
  • Queue.shutdownWith[E](error: E): UIO[Chunk[A]] — convenience wrapper: shutdownCause(Cause.fail(e))
  • Queue.shutdownCauseOption: UIO[Option[Cause[Any]]] — inspect the shutdown cause
  • Atomic: uses same AtomicBoolean CAS as shutdown; concurrent callers get idempotent empty result
  • BackPressure strategy extended to fail blocked offerAll putters with the typed cause
  • All waiting takers completed with Exit.failCause(cause) via the unsafe Promise API
  • awaitShutdown works correctly with shutdownCause (same shutdownHook Promise)

Tests (13 new)

  • Blocked take fiber receives typed cause
  • Blocked back-pressured offer fiber receives typed cause
  • Buffered items returned correctly
  • Empty queue returns empty chunk
  • Future take/offer after shutdown fail appropriately
  • isShutdown set to true
  • shutdownCauseOption before/after shutdown
  • shutdownCauseOption is None for regular shutdown
  • awaitShutdown triggered by shutdownCause
  • shutdownWith convenience method
  • Idempotency: second shutdownCause returns empty chunk
  • shutdownCause after shutdown is a no-op
  • Defect cause (Cause.die) propagation

Comparison to #10551

This PR goes further than the competitor:

  • Adds shutdownCauseOption to inspect the cause post-shutdown
  • Fails back-pressured offer putters with the typed cause (not just interrupt)
  • Returns buffered items from shutdownCause for caller cleanup
  • 13 comprehensive tests vs minimal testing in #10551
  • Works with Cause.die (defects), not just Cause.fail

Closes #9844

/claim #9844

Claim

Total prize pool $1,100
Total paid $0
Status Pending
Submitted March 11, 2026
Last updated March 11, 2026

Contributors

CH

CharlesWong

@CharlesWong

100%

Sponsors

SU

Supreme Labs

@supreme2580

$1,000
ZI

ZIO

@ZIO

$100