Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
adjust double check pattern
Browse files Browse the repository at this point in the history
YuliiaKovalova committed Jan 24, 2025
1 parent 26ab658 commit 3611f9a
Showing 1 changed file with 33 additions and 45 deletions.
78 changes: 33 additions & 45 deletions src/Build/BackEnd/Components/Logging/LoggingService.cs
Original file line number Diff line number Diff line change
@@ -249,31 +249,16 @@ internal partial class LoggingService : ILoggingService, INodePacketHandler
/// </summary>
private AutoResetEvent _dequeueEvent;

/// <summary>
/// Local copy of dequeue event to avoid race condition on shutdown operation.
/// </summary>
private AutoResetEvent _dequeueEventDoubleCheckCopy;

/// <summary>
/// Event set when queue become empty.
/// </summary>
private ManualResetEvent _emptyQueueEvent;

/// <summary>
/// Local copy of empty queue event to avoid race condition on shutdown operation.
/// </summary>
private ManualResetEvent _emptyQueueEventDoubleCheckCopy;

/// <summary>
/// Event set when message is added into queue.
/// </summary>
private AutoResetEvent _enqueueEvent;

/// <summary>
/// Local copy of enqueue event to avoid race condition on shutdown operation.
/// </summary>
private AutoResetEvent _enqueueEventDoubleCheckCopy;

/// <summary>
/// CTS for stopping logging event processing.
/// </summary>
@@ -1319,11 +1304,11 @@ protected internal virtual void ProcessLoggingEvent(object buildEvent)
while (_eventQueue.Count >= _queueCapacity)
{
// Block and wait for dequeue event.
_dequeueEventDoubleCheckCopy.WaitOne();
_dequeueEvent.WaitOne();
}

_eventQueue.Enqueue(buildEvent);
_enqueueEventDoubleCheckCopy.Set();
_enqueueEvent.Set();
}
else
{
@@ -1344,13 +1329,13 @@ public void WaitForLoggingToProcessEvents()
{
while (_eventQueue?.IsEmpty == false)
{
_emptyQueueEventDoubleCheckCopy?.WaitOne();
_emptyQueueEvent?.WaitOne();
}

// To avoid race condition when last message has been removed from queue but
// not yet fully processed (handled by loggers), we need to make sure _emptyQueueEvent
// is set as it is guaranteed to be in set state no sooner than after event has been processed.
_emptyQueueEventDoubleCheckCopy?.WaitOne();
_emptyQueueEvent?.WaitOne();
}

/// <summary>
@@ -1403,48 +1388,54 @@ private static WarningsConfigKey GetWarningsConfigKey(BuildEventArgs buildEventA
private void StartLoggingEventProcessing()
{
_eventQueue = new ConcurrentQueue<object>();

_dequeueEvent = new AutoResetEvent(false);
_dequeueEventDoubleCheckCopy = _dequeueEvent;

_emptyQueueEvent = new ManualResetEvent(false);
_emptyQueueEventDoubleCheckCopy = _emptyQueueEvent;

_enqueueEvent = new AutoResetEvent(false);
_enqueueEventDoubleCheckCopy = _enqueueEvent;

_loggingEventProcessingCancellation = new CancellationTokenSource();
_loggingEventProcessingThread = new Thread(LoggingEventProc);
_loggingEventProcessingThread.Name = $"MSBuild LoggingService events queue pump: {this.GetHashCode()}";
_loggingEventProcessingThread.IsBackground = true;
_loggingEventProcessingThread.Start();

void LoggingEventProc()
{
// Capture local copies of the event handles at start
// to avoid null reference exceptions if the logging service is destroyed by ShutdownComponent.
AutoResetEvent localDequeueEvent = _dequeueEvent;
ManualResetEvent localEmptyQueueEvent = _emptyQueueEvent;
AutoResetEvent localEnqueueEvent = _enqueueEvent;
var completeAdding = _loggingEventProcessingCancellation.Token;
WaitHandle[] waitHandlesForNextEvent = { completeAdding.WaitHandle, _enqueueEventDoubleCheckCopy };

do
WaitHandle[] waitHandlesForNextEvent = [completeAdding.WaitHandle, localEnqueueEvent];

try
{
if (_eventQueue.TryDequeue(out object ev))
do
{
LoggingEventProcessor(ev);
_dequeueEventDoubleCheckCopy?.Set();
}
else
{
_emptyQueueEventDoubleCheckCopy?.Set();

if (!completeAdding.IsCancellationRequested && _eventQueue.IsEmpty)
if (_eventQueue.TryDequeue(out object ev))
{
WaitHandle.WaitAny(waitHandlesForNextEvent);
LoggingEventProcessor(ev);
localDequeueEvent.Set();
}
else
{
localEmptyQueueEvent.Set();

_emptyQueueEventDoubleCheckCopy?.Reset();
}
} while (!_eventQueue.IsEmpty || !completeAdding.IsCancellationRequested);
if (!completeAdding.IsCancellationRequested && _eventQueue.IsEmpty)
{
WaitHandle.WaitAny(waitHandlesForNextEvent);
}
}

localEmptyQueueEvent.Reset();
} while (!_eventQueue.IsEmpty || !completeAdding.IsCancellationRequested);

_emptyQueueEventDoubleCheckCopy?.Set();
localEmptyQueueEvent.Set();
}
catch (Exception)
{
// Exit if the wait handles have been destroyed
}
}
}

@@ -1461,11 +1452,8 @@ private void CleanLoggingEventProcessing()

_eventQueue = null;
_dequeueEvent = null;
_dequeueEventDoubleCheckCopy = null;
_enqueueEvent = null;
_enqueueEventDoubleCheckCopy = null;
_emptyQueueEvent = null;
_emptyQueueEventDoubleCheckCopy = null;
_loggingEventProcessingCancellation = null;
_loggingEventProcessingThread = null;
}

0 comments on commit 3611f9a

Please sign in to comment.