Skip to content

fix(isotp): report the owned actor's shutdown timeout before unsubscribing - #450

Merged
dborgards merged 1 commit into
mainfrom
fix/isotp-dispose-order-#449
Oct 25, 2026
Merged

dborgards merged 1 commit into
mainfrom
fix/isotp-dispose-order-#449

Conversation

@dborgards

Copy link
Copy Markdown
Owner

What does this change?

IsoTpChannel.Dispose detached its handler from the actor before disposing the actor it owns. That Dispose is where the actor reports a callback that outlived its shutdown bound — a TimeoutException through its BackgroundExceptionOccurred — so the channel's subscribers never heard of it. The handler now comes off after the owned actor's Dispose; for an injected actor nothing runs between the two, so it is detached as before. Same ordering as #437 for the J1939-TP channel (#421) and #414 for the node; #448 already did it for the constructor's catch.

An internal ownsActor constructor seam (as on J1939TpChannel since #437) hands an injected actor to the channel to dispose, so the test can give the channel's own actor a 50 ms shutdown bound. The test holds the actor's loop with a callback released only in finally, so the bound is always exceeded — nothing is asserted against the clock — and awaits the report on a signal, since #418 it is dispatched on the thread pool.

  • Test: IsoTpChannelIntegrationTests.The_Shutdown_Timeout_Of_The_Channels_Own_Actor_Reaches_Its_Subscribers
  • Mutation: unsubscribe before _actor.Dispose() (the old order) → the test fails (signal never set, TimeoutException from the wait); restored → green.

Closes #449

Type of change

  • feat — new behaviour (minor release)
  • fix / perf — bug or performance fix (patch release)
  • docs / test / refactor / chore / ci — no release
  • Breaking change (! in the title, plus a BREAKING CHANGE: footer explaining the migration)

Checklist

  • Build with -p:CI=true, test (net10.0), dotnet format --verify-no-changes, and pack +
    eng/verify-packages.py all pass locally (CONTRIBUTING.md § "Before you push") — full net10.0 suite green
  • Public API changes are documented with XML comments — no public API change (the seam is an internal constructor parameter, documented in its remarks)
  • New behaviour is covered by a test
  • The requirement or ADR this relates to is referenced (e.g. FR-RAW-031, ADR-7), if any

Generated by Claude Code

…ibing

IsoTpChannel.Dispose detached its handler from the actor before disposing
the actor it owns. That Dispose is where the actor reports a callback that
outlived its shutdown bound -- a TimeoutException through its
BackgroundExceptionOccurred -- so the channel's subscribers never heard of
it. The handler now comes off after the owned actor's Dispose; for an
injected actor nothing runs between the two, so the order is unchanged.
Same ordering as #437 for the J1939-TP channel and #414 for the node.

An internal ownsActor constructor seam hands an injected actor to the
channel to dispose, so the test can give the channel's own actor a short
shutdown bound. The test holds the actor's loop until its finally, so the
bound is always exceeded, and awaits the report on a signal.

Closes #449

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011AJKME1N1ZvnY3yYcPwNoF
@cursor

cursor Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

PR Summary

Low Risk
Narrow dispose-ordering fix with integration test coverage; behavior change only when an owned actor hits shutdown timeout during channel disposal.

Overview
IsoTpChannel.Dispose no longer unsubscribes from the actor’s BackgroundExceptionOccurred before disposing an owned actor. Shutdown now runs _actor.Dispose() first so a TimeoutException from a callback that outlived the actor’s shutdown bound is still forwarded to the channel’s subscribers; the handler is detached afterward (same ordering as the J1939-TP fix in #437).

An internal ownsActor constructor flag lets tests inject an actor the channel will dispose (without creating its own), enabling The_Shutdown_Timeout_Of_The_Channels_Own_Actor_Reaches_Its_Subscribers: it holds the actor loop until after dispose and asserts the shutdown timeout surfaces as TimeoutException on BackgroundExceptionOccurred.

Reviewed by Cursor Bugbot for commit a7daeeb. Bugbot is set up for automated code reviews on this repo. Configure here.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-25T09:53:30.342750Z a7daeeb PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@codecov

codecov Bot commented Oct 25, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@dborgards
dborgards merged commit d82935b into main Oct 25, 2026
16 checks passed
@dborgards
dborgards deleted the fix/isotp-dispose-order-#449 branch October 25, 2026 10:10
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.

ISO-TP: Dispose detaches the handler before its owned actor's shutdown, so a shutdown timeout is never reported

1 participant