Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 65 additions & 6 deletions TUnit.Engine/Logging/BufferedTextWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -282,17 +282,29 @@ public override void Flush()
// Flush all thread-local buffers
FlushAllThreadBuffers();

// Collect content to write without holding the lock
var contentToWrite = new List<string>();

_lock.EnterWriteLock();
try
{
// Process any queued content
ProcessFlushQueue();
_target.Flush();
// Dequeue all content while holding the lock
while (_flushQueue.TryDequeue(out var content))
{
contentToWrite.Add(content);
}
}
finally
{
_lock.ExitWriteLock();
}

// Write content and flush outside the lock to avoid deadlock
foreach (var content in contentToWrite)
{
_target.Write(content);
}
_target.Flush();
}

public override async Task FlushAsync()
Expand Down Expand Up @@ -350,7 +362,28 @@ private void FlushBuffer(StringBuilder buffer)
// Process queue if it's getting large
if (_flushQueue.Count > 10)
{
ProcessFlushQueue();
// Collect content to write without holding the lock
var contentToWrite = new List<string>();

_lock.EnterWriteLock();
try
{
// Dequeue content while holding the lock
while (_flushQueue.TryDequeue(out var queuedContent) && contentToWrite.Count < 20)
{
contentToWrite.Add(queuedContent);
}
}
finally
{
_lock.ExitWriteLock();
}

// Write content outside the lock to avoid deadlock
foreach (var contentItem in contentToWrite)
{
_target.Write(contentItem);
}
}
}

Expand Down Expand Up @@ -385,15 +418,28 @@ private void AutoFlush(object? state)
{
FlushAllThreadBuffers();

// Collect content to write without holding the lock
var contentToWrite = new List<string>();

_lock.EnterWriteLock();
try
{
ProcessFlushQueue();
// Dequeue all content while holding the lock
while (_flushQueue.TryDequeue(out var content))
{
contentToWrite.Add(content);
}
}
finally
{
_lock.ExitWriteLock();
}

// Write content outside the lock to avoid deadlock
foreach (var content in contentToWrite)
{
_target.Write(content);
}
}
catch
{
Expand All @@ -408,17 +454,30 @@ protected override void Dispose(bool disposing)
_flushTimer?.Dispose();
FlushAllThreadBuffers();

// Collect content to write without holding the lock
var contentToWrite = new List<string>();

_lock.EnterWriteLock();
try
{
ProcessFlushQueue();
// Dequeue all content while holding the lock
while (_flushQueue.TryDequeue(out var content))
{
contentToWrite.Add(content);
}
_disposed = true;
}
finally
{
_lock.ExitWriteLock();
}

// Write content outside the lock
foreach (var content in contentToWrite)
{
_target.Write(content);
}

_threadLocalBuffer?.Dispose();
_lock?.Dispose();
}
Expand Down
7 changes: 6 additions & 1 deletion TUnit.Engine/Logging/StandardErrorConsoleInterceptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@ internal class StandardErrorConsoleInterceptor : OptimizedConsoleInterceptor

static StandardErrorConsoleInterceptor()
{
DefaultError = Console.Error;
// Get the raw stream without SyncTextWriter synchronization wrapper
// BufferedTextWriter already provides thread safety, so we avoid double-locking
DefaultError = new StreamWriter(Console.OpenStandardError())
{
AutoFlush = true
};
}

public StandardErrorConsoleInterceptor(VerbosityService verbosityService) : base(verbosityService)
Expand Down
7 changes: 6 additions & 1 deletion TUnit.Engine/Logging/StandardOutConsoleInterceptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@ internal class StandardOutConsoleInterceptor : OptimizedConsoleInterceptor

static StandardOutConsoleInterceptor()
{
DefaultOut = Console.Out;
// Get the raw stream without SyncTextWriter synchronization wrapper
// BufferedTextWriter already provides thread safety, so we avoid double-locking
DefaultOut = new StreamWriter(Console.OpenStandardOutput())
{
AutoFlush = true
};
}

public StandardOutConsoleInterceptor(VerbosityService verbosityService) : base(verbosityService)
Expand Down
Loading