Skip to content

Let a cancelled RDLImageLoader call stop waiting at once when others share its download - #434

Merged
o-nnerb merged 1 commit into
mainfrom
image-loader-waiters
Oct 19, 2026
Merged

o-nnerb merged 1 commit into
mainfrom
image-loader-waiters

Conversation

@o-nnerb

@o-nnerb o-nnerb commented Oct 19, 2026

Copy link
Copy Markdown
Member

What

A call to RDLImageLoader that is cancelled stops waiting at once, with a CancellationError, even when other calls share its download. (Audit E18, first item.)

RDLImageLoader coalesces concurrent loads of the same id: the first call starts the download, the others join it, and the download is cancelled when the last call that waits for it is cancelled. That part is unchanged. What was not prompt was the call that left while others stayed: it awaited the shared Task, and awaiting a Task is not interrupted by the cancellation of the caller, so it returned only once the image had arrived (the sole call of a download was prompt, since it was the last and cancelled the download itself). A row scrolled off screen while the same image was still wanted elsewhere kept waiting.

The change

  • Each call has its own continuation, kept in InFlight.waiters by the actor. The download's task no longer returns the image to its waiters: when it ends it calls complete(id:generation:with:), which removes the download and resumes every continuation still waiting with the result.
  • A cancelled call runs leave(id:generation:waiter:): its continuation is resumed with CancellationError, and the download is cancelled only if it was the last one. generation still keeps a late cancellation of a call of an earlier download from touching a newer one.
  • The call's place is taken in the same turn of the actor that found or made the download, so the download cannot finish before there is a continuation to hand its result to, and a continuation is resumed exactly once (by complete, by leave, never by both: whichever runs first removes it).
  • No API change. A call that is cancelled used to be able to return an image if the download finished first; it now throws CancellationError unless the result was already delivered.

Tests (RDLImageLoaderTests, 14 in all)

  • cancellingOneOfTwoWaiters_returnsTheCancelledOneAtOnce_whileTheRequestStillRuns: the cancelled call throws CancellationError while the shared request is still held (a gate the test opens later), the other call then gets its image, and the request was started once and never cancelled.
  • cancellingEveryWaiterOfASharedRequest_cancelsIt: the request is cancelled only after the second and last call leaves, and both calls throw.
  • aCallerThatJoinsAfterAnotherLeft_getsTheSameRequest: a call that arrives after another left, while the request runs, joins it.
  • The earlier tests (one waiter, two waiters, a new request after the only waiter left, dedupe, decoding) pass unchanged.
  • Checked by not resuming the cancelled call's continuation: the first test fails at its 30 s bound.
  • waiterCountForTesting(id:) is how the tests wait for a call to have joined or left, in place of a sleep.
  • The same suite passes on macOS (AppKit) and on the iPad Air 11-inch (M4) simulator (UIKit).

macOS full suite and the URLSession-only build pass locally. swift format lint --strict is clean.

🤖 Generated with Claude Code

…share its download

Each call has its own continuation, resumed by the actor with the result when the
download ends or with a CancellationError when its caller is cancelled. The download
is cancelled only when no call is left.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@codecov

codecov Bot commented Oct 19, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.23810% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 94.80%. Comparing base (6199fdc) to head (ef24b0a).

Files with missing lines Patch % Lines
Sources/RequestDL/Image/Core/RDLImageLoader.swift 95.23% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #434      +/-   ##
==========================================
+ Coverage   94.75%   94.80%   +0.04%     
==========================================
  Files         465      465              
  Lines       19457    19477      +20     
==========================================
+ Hits        18437    18465      +28     
+ Misses       1020     1012       -8     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@o-nnerb
o-nnerb merged commit 96f124e into main Oct 19, 2026
20 checks passed
@o-nnerb
o-nnerb deleted the image-loader-waiters branch October 19, 2026 10:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant