| File: Process\RunningProject.cs | Web Access |
| Project: src\sdk\src\Dotnet.Watch\Watch\Microsoft.DotNet.HotReload.Watch.csproj (Microsoft.DotNet.HotReload.Watch) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Immutable; using Microsoft.Build.Graph; using Microsoft.DotNet.HotReload; using Microsoft.Extensions.Logging; namespace Microsoft.DotNet.Watch; internal delegate ValueTask RestartOperation(CancellationToken cancellationToken); internal sealed class RunningProject( ProjectGraphNode projectNode, ProjectOptions options, HotReloadClients clients, ILogger clientLogger, RunningProcess process, RestartOperation restartOperation, ImmutableArray<string> managedCodeUpdateCapabilities) : IAsyncDisposable { private volatile int _isRestarting; public ProjectGraphNode ProjectNode => projectNode; public ProjectOptions Options => options; public HotReloadClients Clients => clients; public ILogger ClientLogger => clientLogger; public ImmutableArray<string> ManagedCodeUpdateCapabilities => managedCodeUpdateCapabilities; public RunningProcess Process => process; public string GetTargetFramework() => projectNode.ProjectInstance.GetTargetFramework(); /// <summary> /// Set to true when the process termination is being requested so that it can be auto-restarted. /// </summary> public bool IsRestarting => _isRestarting != 0; /// <summary> /// Disposes the project. Can occur unexpectedly whenever the process exits. /// Must only be called once per project. /// </summary> /// <param name="isExiting">When invoked in <see cref="ProcessSpec.OnExit"/> handler.</param> public async ValueTask DisposeAsync(bool isExiting) { // disposes communication channels: clients.Dispose(); await process.DisposeAsync(isExiting); } ValueTask IAsyncDisposable.DisposeAsync() => DisposeAsync(isExiting: false); /// <summary> /// Marks the <see cref="RunningProject"/> as restarting. /// Subsequent process termination will be treated as a restart. /// </summary> /// <returns>True if the project hasn't been int restarting state prior the call.</returns> public bool InitiateRestart() => Interlocked.Exchange(ref _isRestarting, 1) == 0; /// <summary> /// Terminates the process in preparation for a restart. /// </summary> public Task TerminateForRestartAsync() { InitiateRestart(); return process.TerminateAsync(); } public async Task CompleteApplyOperationAsync(Task applyTask) { try { await applyTask; } catch (OperationCanceledException) { // Do not report error. } catch (Exception e) { // Handle all exceptions. If one process is terminated or fails to apply changes // it shouldn't prevent applying updates to other processes. ClientLogger.LogError("Failed to apply updates to process {Process}: {Exception}", process.Id, e.ToString()); } } /// <summary> /// Triggers restart operation. /// </summary> public async ValueTask RestartAsync(CancellationToken cancellationToken) { ClientLogger.Log(MessageDescriptor.ProjectRestarting); await restartOperation(cancellationToken); ClientLogger.Log(MessageDescriptor.ProjectRestarted); } public RestartOperation GetRelaunchOperation() => new(async cancellationToken => { ClientLogger.Log(MessageDescriptor.ProjectRelaunching); await restartOperation(cancellationToken); ClientLogger.Log(MessageDescriptor.ProjectRelaunched); }); }