typelevel / typelevel/keypool

take can silently drop a pooled resource when cancelled

Open
#708 4 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Scala
Stars
39
Forks
18
Avg merge
8h 26m
Merged PRs (30d)
4

Description

KeyPool.take can lose a pooled resource entirely if the caller is cancelled at the wrong moment. The resource is neither destroyed nor returned to the pool, so whatever it wraps leaks.

The resource comes out of the pool map here:

optR <- Resource.eval(kp.kpVar.modify(go))
releasedState <- Resource.eval(Ref[F].of[Reusable](kp.kpDefaultReuseState))
resource <- Resource.makeFull[F, (B, F[Unit])] { poll => ... }

Resource.fold interprets Eval as a plain fa.flatMap(...), no bracket and no mask, and all of this sits in the permit's polled use position. So between modify(go) committing and makeFull entering its uncancelable region, the resource exists only in a flatMap continuation. Cancel there and it's gone.

The annoying part is that nothing looks broken afterwards. The permit is released correctly and the idle count is decremented correctly, so pool state stays consistent. You just lose the resource without the destroy hook ever running, which is why this shows up as leaked file descriptors with nothing in the pool metrics to explain it.

Repro: https://gist.github.com/stasimus/3d9b0d6eedcae885b410cac0a7e040dd#file-takecancelspec-scala

Not reachable under TestControl. take is straight-line between the modify and the bracket, so a deterministic single-threaded scheduler never interleaves a cancel there. 500 replications, zero hits.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at KeyPool.take, particularly the kpVar.modify(go), Resource.makeFull, and Resource.fold sequence described in the issue. Run the linked takecancelspec.scala reproduction and verify that cancellation cannot lose the pooled resource or skip its destroy hook, while permit release and idle-count behavior remain correct.

Written by the indexing model from the issue text.

Assessment

Tech stack
scala
Domain
backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.