Skip to content

Thread request cancellation through get_store_host (#4203) - #4412

Merged
erikdarlingdata merged 1 commit into
devfrom
fix/4203-web-read-cancellation-16
Sep 26, 2026
Merged

erikdarlingdata merged 1 commit into
devfrom
fix/4203-web-read-cancellation-16

Conversation

@erikdarlingdata

@erikdarlingdata erikdarlingdata commented Sep 26, 2026 •

Copy link
Copy Markdown
Owner

Refs #4203.

Why

get_store_host was still on CancellationAllowlist: its read goes through StoreHostProfileCache.GetOrGatherAsync, whose gather delegate ran inside its own fixed-deadline CancellationTokenSource with no link to the caller's request token, so an abandoned or superseded web/MCP request left the gather running to that deadline instead of stopping when the caller went away.

What changes

  • One tool: get_store_host (DarlingMcpStoreHostTools.cs). It now takes a trailing CancellationToken cancellationToken = default, links it with the cache's fixed deadline via CancellationTokenSource.CreateLinkedTokenSource(cancellationToken) + CancelAfter(...) (either side can now stop the gather), and narrows its catch to when (ex is not OperationCanceledException).
  • StoreHostProfileCache.GetOrGatherAsync now checks cancellationToken.ThrowIfCancellationRequested() ahead of its semaphore gate, so a cancellation before the gate throws OperationCanceledException directly rather than the semaphore's own TaskCanceledException subtype (the ratchet test asserts the exact base type).
  • DarlingWebEndpoints.cs: removed get_store_host from CancellationAllowlist; its dispatch entry now passes cancellationToken: c.RequestAborted.
  • A cancelled gather is never cached — same guarantee StoreHostProfileCacheTests.AFailedGather_IsNotCached already proved for any other exception; added ACancelledGather_IsNotCached alongside it.
  • WebReadCancellationPinTests: the existing ratchet (AConvertedReadEndpoint_ObservesCancellation_InsteadOfDialingTheStore, EveryStoreReadingToolMethod_TakesACancellationToken_UnlessAllowlisted, CancellationAllowlist_HasNoStaleEntry) now enforces this tool for free — no new test needed there. Its dispatch-table theory previously called BuildReadDispatch() with no PostgresConfig, which made get_store_host short-circuit to an "unavailable" envelope before touching the store; that call now passes a dead-store PostgresConfig so the theory actually reaches the gather path for this tool, same as every other entry.

Test plan

In-process Darling.Tests.dll run on macOS (xunit v3 in-process runner):

  • WebReadCancellationPinTests: 123/123 passed.
  • StoreHostProfileCacheTests: 5/5 passed (includes the new ACancelledGather_IsNotCached).
  • RED case confirmed: on the pre-fix code (origin/dev at bd8a2661c) with the new test files copied in via a detached worktree, AConvertedReadEndpoint_ObservesCancellation_InsteadOfDialingTheStore(name: "get_store_host") failed ("No exception was thrown") and EveryStoreReadingToolMethod_TakesACancellationToken_UnlessAllowlisted failed, naming get_store_host as an offender — both green after the fix.
  • Build: Darling.Tests.csproj and Lite.Tests.csproj both 0 errors with EnableWindowsTargeting=true.
  • Full Darling.Tests.dll suite run once: 15105 total, 226 failed, 966 skipped. All 226 failures are the pre-existing macOS/WPF-platform set (Viewer*, install-location ACL tests, deploy-script tests, etc. — types touching PresentationFramework or Windows-only filesystem/ACL APIs) unrelated to this change; none touch StoreHostProfileCache, DarlingMcpStoreHostTools, or DarlingWebEndpoints's dispatch/allowlist.
  • Darling.Tests/Lite.Tests (net10.0-windows targets): build 0/0 here; cannot run on macOS, CI decides them — listed unchecked.

CHANGELOG entry

SECTION: Fixed
ENTRY:

Links the request cancellation token into StoreHostProfileCache's
gather delegate (CreateLinkedTokenSource plus the existing fixed
deadline), removes get_store_host from CancellationAllowlist, and
passes RequestAborted at its dispatch entry. A cancelled gather is
never cached, and StoreHostProfileCache.GetOrGatherAsync now checks
cancellation ahead of its semaphore gate so cancellation surfaces as
OperationCanceledException rather than the semaphore's own
TaskCanceledException subtype.
@erikdarlingdata
erikdarlingdata marked this pull request as ready for review September 26, 2026 12:54
@erikdarlingdata
erikdarlingdata merged commit 07df25b into dev Sep 26, 2026
16 of 18 checks passed
@erikdarlingdata
erikdarlingdata deleted the fix/4203-web-read-cancellation-16 branch September 26, 2026 12:54
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