Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Jan 27, 2026

Description

Multiple StackGuard implementations existed in the repo (System.Linq.Expressions and Microsoft.Extensions.DependencyInjection) with nearly identical code. This PR consolidates them into a single shared implementation.

Changes

  • Created src/libraries/Common/src/System/Threading/StackGuard.cs with the unified implementation
  • Updated System.Linq.Expressions and Microsoft.Extensions.DependencyInjection to use the shared file via $(CommonPath) include
  • Deleted the duplicate local StackGuard.cs files
  • Added using System.Threading; to files referencing the type
  • Added System.ValueTuple package reference to Microsoft.Extensions.DependencyInjection for net462 support

ILLink.RoslynAnalyzer StackGuard (NOT consolidated)

The src/tools/illink/src/ILLink.RoslynAnalyzer/DataFlow/StackGuard.cs is intentionally not consolidated. It has a fundamentally different design:

  • Static class with simple check-and-throw pattern (no "run on empty stack" capability)
  • Caller-managed recursion depth counter
  • Copied from Roslyn for use with analyzers where deep recursion should fail rather than spawn threads

Implementation notes

The shared StackGuard uses:

  • Conditional compilation for TryEnterOnCurrentStack(): Uses try/catch with EnsureSufficientExecutionStack() on NETFRAMEWORK || NETSTANDARD2_0, and TryEnsureSufficientExecutionStack() on newer frameworks
  • ValueTuple-based code for all RunOnEmptyStack methods on all targets (with System.ValueTuple package reference for older frameworks that need it)
Original prompt

We have multiple "stack guard" implementations in the repo, e.g. I believe we have one in dependency injection, another in linq expressions, maybe one in il link, etc. Please consolidate them all to a single implementation, likely under src/Common somewhere, that all of the consumers can then depend on rather than each having their own unique copy of the source. If there are any meaningful / impactful differences between the implementations, please call those out so that we can decide how to proceed.


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

…nd Microsoft.Extensions.DependencyInjection into Common

Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
Copilot AI changed the title [WIP] Consolidate stack guard implementations into a single source Consolidate StackGuard implementations into Common Jan 27, 2026
Copilot AI requested a review from stephentoub January 27, 2026 18:27
var t = (Tuple<Func<T1, T2, T3, R>, T1, T2, T3>)s;
return t.Item1(t.Item2, t.Item3, t.Item4);
}, Tuple.Create(action, arg1, arg2, arg3));
#else
Copy link
Member

Choose a reason for hiding this comment

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

@copilot, can we not use the valuetuple-based code (the else block) on .NET Framework and .NET Standard?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done in 146267b. Removed the Tuple-based conditional compilation and now use ValueTuple on all targets. Added System.ValueTuple package reference to Microsoft.Extensions.DependencyInjection.csproj for net462.

<ItemGroup>
<Compile Include="**\*.cs" />
<Compile Remove="ServiceLookup\ILEmit\**\*.cs" />
<Compile Remove="ServiceLookup\StackGuard.cs" />
Copy link
Member

Choose a reason for hiding this comment

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

@copilot why do we need both this StackGuard.cs and the Common StackGuard.cs? The goal here is to consolidate to just one.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're right - the <Compile Remove> line was leftover from when I excluded the file before deleting it. Since the file is deleted, that line is unnecessary. Removed it in 840c01d.

Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
@dotnet-policy-service
Copy link
Contributor

Tagging subscribers to this area: @agocke, @VSadov
See info in area-owners.md if you want to be subscribed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants