| File: StaticState.cs | Web Access |
| Project: src\nuget-client\src\NuGet.Core\NuGet.Common\NuGet.Common.csproj (NuGet.Common) |
// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; namespace NuGet.Common { /// <summary> /// Process-global hook for discarding static and lingering process state when an MSBuild-driven build ends. In a /// host that reuses its process across builds (MSBuild Server, multithreaded MSBuild) a task can no longer rely on /// process exit to discard state, so each cache or live resource subscribes a reset - typically from its static /// constructor - and restore raises the event once the build is over, so the next build behaves as if the process /// had started fresh. This is the only public surface for the feature; every contributing cache or resource stays /// internal to its own type. /// </summary> public static class StaticState { /// <summary> /// Raised once when an MSBuild-driven build ends, before the process may be reused for another build. /// Subscribe if your type caches process-global state, or a value derived from it (an environment variable, the /// current directory, machine/user configuration), or holds a live OS resource (a child process, connection, /// timer or file handle) that the per-build "process dies after each build" model relied on process exit to /// reclaim. /// </summary> /// <remarks> /// Two rules bind every handler, and both come from bugs that shipped when they were not followed: /// <list type="bullet"> /// <item><description> /// <b>Invalidate; do not recompute.</b> A handler must null its cache or install a fresh /// <see cref="Lazy{T}" /> so the value is rebuilt on first use in the next build. It must not read the /// environment here: the process still holds the ending build's environment, and the next build's is applied /// only when that build starts, so recomputing now caches the value that is on its way out. /// </description></item> /// <item><description> /// <b>Do not swap a resource that has work in flight.</b> Replacing and disposing a live object - a semaphore, /// an open writer, a child process - breaks callers that captured the previous instance, and for a /// synchronization primitive it also defeats the guarantee it exists for, since holders of the old instance run /// alongside acquirers of the new one. Such state belongs to the operation that owns it and should be scoped /// there rather than shared in a static and periodically replaced. /// </description></item> /// </list> /// </remarks> public static event Action? BuildEnded; /// <summary> /// Raises <see cref="BuildEnded" />. Handlers are expected not to throw; one that tears down an external /// resource is responsible for guarding itself, so a genuine bug in a reset surfaces rather than being silently /// swallowed. /// </summary> public static void RaiseBuildEnded() => BuildEnded?.Invoke(); } }