Introduce the per-run BuildContext foundation (FT-2)#451
Open
ChrisonSimtian wants to merge 2 commits into
Open
Introduce the per-run BuildContext foundation (FT-2)#451ChrisonSimtian wants to merge 2 commits into
ChrisonSimtian wants to merge 2 commits into
Conversation
…ion leaks) BuildManager.Execute leaked process-global state across invocations, so a second build in the same process (tests, hosted/reentrant scenarios) inherited the first's residue: - Console.CancelKeyPress / ToolOptions.Created handlers were re-subscribed every call and never removed (accumulating); - the CancellationHandler list kept each run's Finish (capturing a stale build); - Logging.InMemorySink.Instance carried log events over; - ValueInjectionUtility's value cache and the NuGet/Npm tool-path resolver config persisted. Fix (non-breaking): the two global handlers are now held in locals and the finally undoes exactly what the run set — unsubscribes both, removes Finish from the cancellation list, and resets the carried-over state (InMemorySink.Clear, ValueInjectionUtility.ClearCache, NuGet/NpmToolPathResolver.Reset). Build creation moved ahead of the subscriptions so a failed `new T()` leaks nothing. Groundwork for the BuildContext foundation (FT-2). Full build + test suite green; reentrancy is verified by FT-9.
Adds BuildContext — an internal, per-run, AsyncLocal-ambient scope activated at the top of BuildManager.Execute (`using var context = BuildContext.Activate()`) and disposed at method exit. It owns the per-run global-state lifecycle: the Console.CancelKeyPress / ToolOptions.Created subscriptions, the cancellation-handler list, and the teardown FT-1 centralised (now run on dispose). BuildManager.CancellationHandler becomes a thin facade over BuildContext.Current — the first "static surface over the context" seam, and the rail subsequent steps ride (FT-4 ParameterService, FT-5 tool-path config, FT-6 logging scope move their state onto the context). Internal-only; not a public contract until the SDK (milestone #7). Disposing on method exit means a failed `new T()` also leaks nothing. Full build + test suite green.
This was referenced Jun 30, 2026
Collaborator
Author
|
@dennisdoomen this one here, start of the de-static work |
ChrisonSimtian
commented
Jul 4, 2026
| /// </remarks> | ||
| internal sealed class BuildContext : IDisposable | ||
| { | ||
| private static readonly AsyncLocal<BuildContext> s_current = new(); |
Collaborator
Author
There was a problem hiding this comment.
Suggested change
| private static readonly AsyncLocal<BuildContext> s_current = new(); | |
| private static readonly AsyncLocal<BuildContext> currentInstance = new(); |
probably predating the rules change we made this week, so wrong naming convention...
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.
⏳ Stacked on #450 (FT-1) — until that merges, this PR's diff includes FT-1's commit; once #450 lands I'll rebase onto
mainso this narrows to just the FT-2 change. Draft until then.Engine de-statification ([Foundation] epic #315) — FT-2 / #307. The foundation the rest of the de-static work rides.
Adds
BuildContext— an internal, per-run,AsyncLocal-ambient scope:BuildManager.Execute(using var context = BuildContext.Activate()), disposed at method exit;Console.CancelKeyPress/ToolOptions.Createdsubscriptions, the cancellation-handler list (moved offBuildManager), and the FT-1 teardown (now run on dispose);BuildManager.CancellationHandlerbecomes a thin facade overBuildContext.Current— the first "static surface over the context" seam.Subsequent steps move the per-run services onto the context: FT-4
ParameterService, FT-5 tool-path config, FT-6 logging scope. Internal-only — not a public contract until the SDK (milestone #7). Disposing on method exit means a failednew T()also leaks nothing. Full build + test suite green.🤖 Generated with Claude Code