Add bounded-wait timeout support to hosting API#27027
Open
SufficientDaikon wants to merge 1 commit intoPowerShell:masterfrom
Open
Add bounded-wait timeout support to hosting API#27027SufficientDaikon wants to merge 1 commit intoPowerShell:masterfrom
SufficientDaikon wants to merge 1 commit intoPowerShell:masterfrom
Conversation
f659713 to
5d18264
Compare
Add bounded alternatives to critical unbounded WaitOne()/Wait() calls in the hosting API (engine/hostifaces/). Add two opt-in public API members: PSInvocationSettings.Timeout (TimeSpan, defaults to InfiniteTimeSpan) and PowerShell.Stop(TimeSpan). Internal waits (runspace open, pipeline stop, runspace close, dispose) bounded to 30 seconds. Default InfiniteTimeSpan preserves existing behavior with zero overhead. 34 tests added (19 xUnit C#, 15 Pester).
d079276 to
8ca0cd9
Compare
This was referenced Mar 24, 2026
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.
PR Summary
Hosting applications can now set a timeout on
PowerShell.Invoke()andPowerShell.Stop()so that a runaway script cannot hang the host process indefinitely.Important
This PR adds new public API surface. An RFC has been filed at PowerShell-RFC#409. Not ready to merge until the RFC is accepted.
This PR adds bounded alternatives to the critical unbounded
WaitOne()/Wait()calls in the hosting API and introduces two opt-in public API members:PSInvocationSettings.TimeoutTimeSpan { get; set; }Timeout.InfiniteTimeSpanPowerShell.Stop(TimeSpan)void Stop(TimeSpan timeout)Backwards compatibility: The default
InfiniteTimeSpanpreserves existing behavior — code that does not setTimeouttakes the original same-thread code path with no extra allocations or thread switches.Fixes #26594. Addresses #24289. Foundation for #19685.
What Changed
PowerShell.csPSInvocationSettings.Timeout,Stop(TimeSpan), boundedInvokeviaTask.Run+Wait(timeout), pool acquisition + batch timeoutsConnectionBase.csStopPipelines(TimeSpan)viaTask.Run+Task.WaitAll, 30s runspace-open waitLocalConnection.csDispose()catchesTimeoutException→ forcesBrokenstateLocalPipeline.csPipelineFinishedEvent.WaitOnePowerShellStrings.resxOperationTimedOut,StopTimedOutresource stringsRunspaceStrings.resxStopPipelinesTimedOutresource stringExecution Flow
When
Timeoutis set to a finite value,Invoke()dispatches execution to a thread pool thread and joins with a bounded wait:flowchart TD A["ps.Invoke()"] --> B{"Timeout\nset?"} B -->|"InfiniteTimeSpan\n(default)"| C["Same-thread path\n— original code, unchanged"] B -->|"Finite timeout"| D["Task.Run(worker)"] D --> E{"invokeTask\n.Wait(timeout)"} E -->|Completed| F["Return results"] E -->|Expired| G["CoreStop()"] G --> H["throw TimeoutException"]Runspace Lifecycle
Internal waits (
Close(),StopPipelines(),Dispose()) are now bounded to 30 seconds. If cleanup times out, the runspace transitions toBrokenstate to release resources:stateDiagram-v2 [*] --> Open Open --> Closing : Close() Closing --> Closed : Completes within 30s Closing --> Broken : TimeoutException Open --> Broken : Dispose() timeout Closed --> [*] Broken --> [*] : Resources releasedTests
Test inventory
test/xUnit/csharp/test_Timeout.cstest/powershell/engine/Api/Timeout.Tests.ps1Coverage: REQ-01 (basic timeout) through REQ-10 (pool exhaustion), including edge cases for double-dispose, broken runspace recovery, and nested timeout propagation.
Caution
STA COM caveat: When
Timeoutis finite,Invoke()dispatches work to a ThreadPool (MTA) thread viaTask.Run. Scripts that depend on STA COM apartment state should leaveTimeoutat its defaultInfiniteTimeSpan, which uses the original same-thread path unchanged.PR Context
The PowerShell hosting API (
System.Management.Automation.PowerShell) is used by VS Code, Azure Functions, Azure Automation, Jupyter notebooks, and thousands of custom C# applications. When a script hangs or deadlocks, everyWaitOne()call blocks indefinitely — the host has no way to recover short of killing the process.Process.Kill()when the integrated console hangsTimeoutExceptionand recover gracefullyThread.Abortdoes not exist in .NET Core — no timeout mechanismPSInvocationSettings.TimeoutsupportWhy not experimental feature gating? The feature is inherently opt-in. Code that does not set
Timeouttakes the identical code path as before. The[Experimental]attribute system targets cmdlet parameters, not POCO properties. Happy to add aPSHostingAPITimeoutfeature flag if the Committee prefers.RFC: PowerShell-RFC#409
Docs issue: MicrosoftDocs/PowerShell-Docs#12852
Prefer a rendered documentation site?
Bounded-Wait API Documentation — full specification, test matrix, scenario walkthroughs, and annotated source diffs in a browsable format.
PR Checklist
.h,.cpp,.cs,.ps1and.psm1files have the correct copyright header