Fix ServiceChannel auto-close when server initiates session shutdown #5946
Open
afifi-ins wants to merge 1 commit into
Open
Fix ServiceChannel auto-close when server initiates session shutdown #5946afifi-ins wants to merge 1 commit into
afifi-ins wants to merge 1 commit into
Conversation
afifi-ins
force-pushed
the
service-channel-auto-close-stuck-5803
branch
from
May 20, 2026 00:06
bb43bf7 to
73bccb1
Compare
afifi-ins
force-pushed
the
service-channel-auto-close-stuck-5803
branch
from
May 20, 2026 02:06
0344418 to
e2ef244
Compare
afifi-ins
force-pushed
the
service-channel-auto-close-stuck-5803
branch
from
May 26, 2026 12:44
d32d746 to
01a3181
Compare
mconnew
force-pushed
the
service-channel-auto-close-stuck-5803
branch
2 times, most recently
from
July 20, 2026 21:19
fafcb7e to
29a595d
Compare
afifi-ins
force-pushed
the
service-channel-auto-close-stuck-5803
branch
from
July 22, 2026 13:14
29a595d to
d768d34
Compare
There was a problem hiding this comment.
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.DecrementActivityto (client-side) half-close the output session, then schedule a deferred outerCloseto avoid receive-pump deadlocks and ensureClosedis 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. |
Member
|
/azp run |
|
Azure Pipelines: Successfully started running 2 pipeline(s). |
Member
|
/azp run |
|
Azure Pipelines: Successfully started running 2 pipeline(s). |
afifi-ins
force-pushed
the
service-channel-auto-close-stuck-5803
branch
from
July 24, 2026 03:47
dd1bf2d to
ea4ad9e
Compare
…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
force-pushed
the
service-channel-auto-close-stuck-5803
branch
from
July 24, 2026 03:49
ea4ad9e to
a2300ba
Compare
Contributor
Author
|
/azp run dotnet-wcf-ci |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
…(#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.