Skip to content

Fix ServiceChannel auto-close when server initiates session shutdown #5946

Open
afifi-ins wants to merge 1 commit into
dotnet:mainfrom
afifi-ins:service-channel-auto-close-stuck-5803
Open

Fix ServiceChannel auto-close when server initiates session shutdown #5946
afifi-ins wants to merge 1 commit into
dotnet:mainfrom
afifi-ins:service-channel-auto-close-stuck-5803

Conversation

@afifi-ins

Copy link
Copy Markdown
Contributor

…(#5803)

When a server gracefully closes its duplex session while the client is idle between calls, the duplex receive pump processes the EndRecord and invokes ServiceChannel.DecrementActivity. Previously this only half-closed the inner session via CloseOutputSession and never transitioned the outer ServiceChannel out of Opened, leaving the channel stuck open indefinitely.

DecrementActivity now sends our EndRecord inline (CloseOutputSession) and then schedules a CompleteAutoClose continuation via ActionItem.Schedule so the outer ServiceChannel.Close runs off the receive pump thread (closing inline would deadlock on the SynchronizedMessageSource semaphore). CompleteAutoClose falls back to Abort on Communication/Timeout/InvalidOperation exceptions and swallows ObjectDisposedException.

Adds an outerloop integration test (NetTcp duplex) that hosts a server which stops its host after replying to RequestServerShutdown and asserts the client channel transitions to Closed within 10s, with Closing/Closed events firing and no Faulted event.

@afifi-ins
afifi-ins force-pushed the service-channel-auto-close-stuck-5803 branch from bb43bf7 to 73bccb1 Compare May 20, 2026 00:06
@afifi-ins afifi-ins closed this May 20, 2026
@afifi-ins
afifi-ins force-pushed the service-channel-auto-close-stuck-5803 branch from 0344418 to e2ef244 Compare May 20, 2026 02:06
@afifi-ins afifi-ins reopened this May 20, 2026
@afifi-ins
afifi-ins force-pushed the service-channel-auto-close-stuck-5803 branch from d32d746 to 01a3181 Compare May 26, 2026 12:44
@mconnew
mconnew force-pushed the service-channel-auto-close-stuck-5803 branch 2 times, most recently from fafcb7e to 29a595d Compare July 20, 2026 21:19
@afifi-ins
afifi-ins force-pushed the service-channel-auto-close-stuck-5803 branch from 29a595d to d768d34 Compare July 22, 2026 13:14
@mconnew
mconnew requested a review from Copilot July 22, 2026 17:29

Copilot AI 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.

Pull request overview

Fixes a WCF client-side duplex session edge case where a server-initiated graceful session shutdown could leave the outer ServiceChannel stuck in Opened, by ensuring the client schedules an outer close off the receive-pump thread and transitions the channel through Closing -> Closed. Adds an outerloop NetTcp regression scenario to validate the client closes in response to a server EndRecord.

Changes:

  • Updates ServiceChannel.DecrementActivity to (client-side) half-close the output session, then schedule a deferred outer Close to avoid receive-pump deadlocks and ensure Closed is raised.
  • Adds an IIS-hosted duplex test service + host definition that can close the current session output on demand (server-initiated shutdown).
  • Adds an outerloop scenario test plus endpoint/contract wiring for the new regression coverage.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/System.ServiceModel.Primitives/src/System/ServiceModel/Channels/ServiceChannel.cs Schedules outer ServiceChannel.Close after sending EndRecord so the channel transitions to Closed without deadlocking the receive pump.
src/System.Private.ServiceModel/tools/IISHostedWcfService/App_code/WcfDuplexService.cs Adds server-side service + dispatch inspector behavior to capture the per-session channel and trigger session output shutdown.
src/System.Private.ServiceModel/tools/IISHostedWcfService/App_code/testhosts/DuplexTestServiceHosts.cs Registers a new NetTcp test host for the server-initiated shutdown service and wires the capture behavior at configuration time.
src/System.Private.ServiceModel/tools/IISHostedWcfService/App_code/IWcfDuplexService.cs Introduces the service/callback contract for the server-initiated shutdown scenario in the IIS-hosted test service.
src/System.Private.ServiceModel/tests/Scenarios/Client/ChannelLayer/ServerInitiatedSessionShutdownTests.cs Adds an outerloop regression test asserting the client channel closes after the server closes its session.
src/System.Private.ServiceModel/tests/Common/Scenarios/ServiceInterfaces.cs Adds client-side mirror contract/callback used by the new scenario test.
src/System.Private.ServiceModel/tests/Common/Scenarios/Endpoints.cs Adds a new endpoint helper for the server-initiated shutdown NetTcp service.

@mconnew

mconnew commented Jul 22, 2026

Copy link
Copy Markdown
Member

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 2 pipeline(s).

@mconnew

mconnew commented Jul 23, 2026

Copy link
Copy Markdown
Member

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 2 pipeline(s).

@afifi-ins
afifi-ins force-pushed the service-channel-auto-close-stuck-5803 branch from dd1bf2d to ea4ad9e Compare July 24, 2026 03:47
…otnet#5803)

When a server closes its duplex session while the client is idle between
calls, the client's receive pump processes the EndRecord and calls
ServiceChannel.DecrementActivity. Previously that path only half-closed
the inner session; the outer ServiceChannel stayed in Opened, the Closed
event never fired, and subsequent user calls silently reused a torn-down
session.

Product fix in src/System.ServiceModel.Primitives/.../ServiceChannel.cs:
- Rewrite DecrementActivity's client-only auto-close path to send our
  EndRecord (Session.CloseOutputSession), then hop off the receive-pump
  thread via ActionItem.Schedule to complete the outer Close. Closing
  inline can deadlock against the SynchronizedMessageSource semaphore
  that OnClose -> EnsureInputClosedAsync re-acquires.
- New CompleteAutoClose helper drives Opened -> Closing -> Closed, and
  Aborts on Communication/Timeout/InvalidOperation failures so the
  Closed/Faulted event still fires.
- If our half-close itself fails, Abort the channel so subscribers see a
  state change instead of a stale Opened.

Regression test (skipped under the CoreWCF host, see below):
- New ServerInitiatedSessionShutdownTests.ServerInitiatedShutdown_
  ClientChannelTransitionsToClosed opens a duplex NetTcpBinding session,
  warms the receive pump with an Echo, asks the server to shut down the
  session, and asserts the client transitions through Closing to Closed
  within the binding's CloseTimeout.
- Server-side helper (IServerInitiatedShutdownService + service impl in
  WcfDuplexService.cs) captures the current IClientChannel via a
  CaptureChannelServiceBehavior IDispatchMessageInspector, then closes
  the session's output side after replying.
- CaptureChannelServiceBehavior is registered from ApplyConfiguration,
  not the ctor: on the CoreWCF host shim, ServiceHost.Description
  returns a throwaway ServiceDescription until ApplyConfig wires up the
  real ServiceHostBase, so a ctor-time Behaviors.Add silently no-ops.
- Test gated with [Condition(nameof(Skip_CoreWCFService_FailedTest))]:
  no public CoreWCF API exposes the underlying ISessionChannel<
  IDuplexSession> for the current operation, so the server-side helper
  can't close the session on the CoreWCF host leg. The client-side
  product fix is still exercised on all client TFMs.

Co-authored-by: Matt Connew <mconnew@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot-Session: be4b665b-dccf-459f-9605-ef6e089a5ba9
@afifi-ins
afifi-ins force-pushed the service-channel-auto-close-stuck-5803 branch from ea4ad9e to a2300ba Compare July 24, 2026 03:49
@afifi-ins

Copy link
Copy Markdown
Contributor Author

/azp run dotnet-wcf-ci

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).

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.

3 participants