Skip to content

Reject Session.Close from Terminated handlers - #42144

Merged
Kevin Vega (kvega005) merged 1 commit into
microsoft:masterfrom
kvega005:fix/session-close-from-terminated
Nov 6, 2026
Merged

Kevin Vega (kvega005) merged 1 commit into
microsoft:masterfrom
kvega005:fix/session-close-from-terminated

Conversation

@kvega005

Copy link
Copy Markdown
Contributor

Summary of the Pull Request

Session.Close() now rejects calls from a Terminated handler with E_ILLEGAL_METHOD_CALL instead of deadlocking. Calls from outside the handler still wait for active termination handlers to finish.

PR Checklist

  • Closes: Link to issue #xxx
  • Communication: I've discussed this with core contributors already. If work hasn't been agreed, this work might be rejected
  • Tests: Added/updated if needed and all pass
  • Localization: All end user facing strings can be localized
  • Dev docs: Added/updated if needed
  • Documentation updated: If checked, please file a pull request on our docs repo and link it here: #xxx

Detailed Description of the Pull Request / Additional comments

Problem. Close() drains the threadpool wait that invokes Terminated. Calling it synchronously from a handler therefore waits for that same handler to return. A handler can also call through COM into another apartment, where checking only the physical thread ID would miss the same dependency.

Change. OnTerminated() records its physical and COM logical thread IDs under a short-held wil::srwlock. Close() checks those IDs and throws winrt::hresult_illegal_method_call before releasing any resources when called from the handler or its synchronous COM call chain. Logical-ID lookup errors propagate through winrt::check_hresult.

The lock is not held while invoking application handlers. A wil::scope_exit clears the IDs after event dispatch finishes, including during exception unwinding. The existing wait and resource-release order in Close() are unchanged.

Validation Steps Performed

  • WslcSdkWinRtTests::TerminationHandlerCloseIsRejected checks rejection from two handlers, verifies the session remains open after rejection, and closes it from outside the handler.
  • WslcSdkWinRtTests::TerminationHandlerCloseFromStaCallIsRejected checks rejection through a synchronous STA context call with different physical thread IDs and matching non-null logical IDs. A direct lambda invocation alone would not exercise logical-thread matching.
  • WslcSdkWinRtTests::CloseWaitsForTerminationHandler checks that an outside Close() waits for an active handler to finish.
  • WSLC-RunTests.ps1 -RepoRoot 'Q:\WSL2' -TestName '*WslcSdkWinRtTests::*Termination*' — passed all four selected tests, including the existing termination-notification test.

Co-authored-by: Copilot <[email protected]>
Copilot-Session: 99ebbb28-3fda-402d-8b3e-85813bfa8dbc
Copilot AI balanced review requested due to automatic review settings November 2, 2026 21:59
@kvega005
Kevin Vega (kvega005) requested review from a team as code owners November 2, 2026 21:59

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

No active review issues remain for the termination-handler deadlock fix.

No open issues 🎉

What changed in this PR

Prevents Session.Close() from deadlocking when called from a Terminated handler or its synchronous COM call chain, while preserving the wait for active handlers when called externally.

Changes:

  • Tracks physical and COM logical thread IDs during termination-event dispatch.
  • Rejects handler-originated Close() calls with E_ILLEGAL_METHOD_CALL before releasing resources.
  • Adds regression tests for direct calls, cross-apartment calls, and external close waiting.
File Description
test/​windows/​wslc/​WslcSdkWinRTTests.cpp Adds three tests covering close rejection and handler-draining behavior.
src/​windows/​WslcSDK/​winrt/​Session.h Declares the handler-context check and lock-protected thread IDs.
src/​windows/​WslcSDK/​winrt/​Session.cpp Tracks handler execution context and rejects close calls that would wait on themselves.

Review effort: Balanced

// Close waits for in-flight Terminated handlers, so calling it from one would wait on itself.
if (IsInTerminatedHandler())
{
throw winrt::hresult_illegal_method_call(L"Session cannot be closed from its Terminated handler");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should localize this message

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We typically don't localize winrt exceptions so I think english here is OK since this isn't designed to be shown to the end user

@dkbennett David Bennett (dkbennett) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good overall, just a minor localization comment.

@ranm-msft ranm-msft left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please cover final-reference release in the termination callback before approval.

void Session::Close()
{
// Close waits for in-flight Terminated handlers, so calling it from one would wait on itself.
if (IsInTerminatedHandler())

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Releasing the last external Session wrapper from a Terminated handler also reaches this guard through final_release(), which calls Close(). That throws through C++/WinRT's noexcept Release path and terminates the process; please make callback/final-release lifetime safe and add a test that clears the last wrapper reference inside the handler.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually discussed this with Blue (@OneBlue) before I sent out the PR. The consensus was "this might be the kind of thing that we document as not supported, and an app crashing because of this would be a user error."

@kvega005
Kevin Vega (kvega005) merged commit 7608afe into microsoft:master Nov 6, 2026
9 checks passed
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.

5 participants