| File: BackEnd\BuildManager\CoordinatorClient.HeartbeatTimer.cs | Web Access |
| Project: src\msbuild\src\Build\Microsoft.Build.csproj (Microsoft.Build) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.IO; using System.Threading; using Microsoft.Build.Framework.Coordinator; namespace Microsoft.Build.BackEnd; internal sealed partial class CoordinatorClient { /// <summary> /// Sends coordinator heartbeats and waits for in-flight callbacks during disposal. /// </summary> private sealed class HeartbeatTimer : IDisposable { private const int NotDisposed = 0; private const int Disposing = 1; private const int Disposed = 2; private readonly Connection _connection; private readonly ICoordinatorDebugOutput _output; private readonly Timer _timer; private int _disposeState; private bool IsDisposingOrDisposed => Volatile.Read(ref _disposeState) != NotDisposed; public HeartbeatTimer(Connection connection, int intervalMs, ICoordinatorDebugOutput output) { _connection = connection; _output = output; _timer = new Timer( static state => ((HeartbeatTimer)state!).SendHeartbeat(), state: this, dueTime: intervalMs, period: intervalMs); } public void Dispose() { if (Interlocked.CompareExchange(ref _disposeState, Disposing, NotDisposed) != NotDisposed) { return; } try { using var disposeEvent = new ManualResetEvent(false); if (_timer.Dispose(disposeEvent)) { disposeEvent.WaitOne(); } } finally { Volatile.Write(ref _disposeState, Disposed); } } private void SendHeartbeat() { if (IsDisposingOrDisposed) { return; } try { _connection.WriteClientMessage(HeartbeatMessage.Instance); } catch (IOException) { _output.WriteLine("CoordinatorClient: Heartbeat failed (pipe broken)"); // Pipe broken -- nothing we can do. The build continues // with whatever nodes were already granted. } } } }