From a0b9c8ffd938a92facc1006339885df3429ea018 Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Sat, 24 Jan 2026 13:26:15 +0000 Subject: [PATCH] fix: ensure thread-safe initialization of console line buffers Addresses post-merge feedback from PR #4549 regarding thread-safety concerns in console buffer lazy initialization. Replaced nullable StringBuilder fields with Lazy to guarantee thread-safe initialization when multiple threads access GetConsoleStdOutLineBuffer() or GetConsoleStdErrLineBuffer() concurrently. The previous implementation using ??= was not thread-safe and could result in multiple StringBuilder instances being created before assignment completes, undermining per-context buffer isolation. Benefits: - Thread-safe by default with Lazy - No manual double-checked locking needed - Cleaner, more idiomatic C# code - AOT-compatible Co-Authored-By: Claude Sonnet 4.5 --- TUnit.Core/Context.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/TUnit.Core/Context.cs b/TUnit.Core/Context.cs index feea90df11..6f49de6e97 100644 --- a/TUnit.Core/Context.cs +++ b/TUnit.Core/Context.cs @@ -29,8 +29,9 @@ TestContext.Current as Context // Console interceptor line buffers for partial writes (Console.Write without newline) // These are stored per-context to prevent output mixing between parallel tests - private StringBuilder? _consoleStdOutLineBuffer; - private StringBuilder? _consoleStdErrLineBuffer; + // Using Lazy for thread-safe initialization + private readonly Lazy _consoleStdOutLineBuffer = new(() => new StringBuilder()); + private readonly Lazy _consoleStdErrLineBuffer = new(() => new StringBuilder()); private readonly object _consoleStdOutBufferLock = new(); private readonly object _consoleStdErrBufferLock = new(); @@ -43,12 +44,12 @@ TestContext.Current as Context // Internal accessors for console interceptor line buffers with thread safety internal (StringBuilder Buffer, object Lock) GetConsoleStdOutLineBuffer() { - return (_consoleStdOutLineBuffer ??= new StringBuilder(), _consoleStdOutBufferLock); + return (_consoleStdOutLineBuffer.Value, _consoleStdOutBufferLock); } internal (StringBuilder Buffer, object Lock) GetConsoleStdErrLineBuffer() { - return (_consoleStdErrLineBuffer ??= new StringBuilder(), _consoleStdErrBufferLock); + return (_consoleStdErrLineBuffer.Value, _consoleStdErrBufferLock); } internal Context(Context? parent)