Skip to content

Avoiding Footguns

Mark Lauter edited this page Jun 15, 2026 · 1 revision

Avoiding footguns

The lease/release contract is a balanced pair, and the pool trusts you to honor it. Like ArrayPool<T>.Return, it does not police misuse — it favors a tight, fast path over ownership tracking. These are the sharp edges and how to stay clear of them. A scoped lease closes the first two by construction; reach for it before the raw LeaseAsync/Release pair.

Release exactly once

The pool tracks no ownership. Releasing the same item twice — or releasing an item the pool never handed you — enqueues a duplicate. After that, two callers can lease the same instance and corrupt each other's work. A double release also over-counts the capacity gate, which lets the pool create past MaxSize, or throws SemaphoreFullException when the gate is already full.

Release each leased item once, from a finally. Better, use LeaseScopeAsync: its return is idempotent, so a double dispose is a safe no-op.

Never forget to release

A lease that never returns holds its capacity permit for the life of the pool. Leak MaxSize permits and the pool saturates: every later lease waits out the full LeaseTimeout and then throws. The try/finally is not optional. A scoped lease makes the return automatic, and a lease that is garbage-collected without being disposed is counted on the pool.leases.leaked counter — so even a slipped-through leak stays visible. See Metrics.

Dispose reclaims idle items only

Disposing the pool disposes the items sitting idle in it. Items leased out at that moment belong to the caller — the pool cannot reclaim them, so dispose them yourself. After the pool is disposed, releasing an item to it throws ObjectDisposedException, as does leasing from it. A scoped lease handles the race for you: if the pool is disposed while an item is out on lease, disposing the lease disposes the orphaned item instead of returning it.

Preparation failure discards the item

When a registered preparation strategy throws, the pool disposes that item rather than recirculate a broken one, frees the permit, and rethrows. The permit is not leaked — only the item is gone. Catch the exception at the call site and retry the lease for a fresh item.

Next

  • Leasing and releasing — the contract these edges come from, and the scoped-lease style that smooths most of them.
  • The capacity gate — why a stray release breaches MaxSize, explained from the design.

Clone this wiki locally