-
-
Notifications
You must be signed in to change notification settings - Fork 110
fix: ensure thread-safe initialization of console line buffers #4551
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Addresses post-merge feedback from PR #4549 regarding thread-safety concerns in console buffer lazy initialization. Replaced nullable StringBuilder fields with Lazy<StringBuilder> 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<T> - No manual double-checked locking needed - Cleaner, more idiomatic C# code - AOT-compatible Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
SummaryReplaces nullable StringBuilder fields with Lazy for thread-safe initialization of console line buffers. Critical IssuesNone found ✅ AnalysisThe PR correctly addresses a legitimate thread-safety concern in the null-coalescing initialization pattern. However, I have one observation about the implementation: Observation: Lock objects may be redundant The change replaces var (buffer, bufferLock) = GetLineBuffer();
lock (bufferLock)
{
if (buffer.Length > 0)
{
RouteToSinks(buffer.ToString());
buffer.Clear();
}
}The locks are clearly necessary for protecting buffer operations. The Correctness: Performance consideration: Verdict✅ APPROVE - Clean fix that properly addresses the thread-safety issue without introducing new problems. 🤖 Generated with Claude Code |
Problem
PR #4549 introduced per-context console line buffers to prevent output mixing between parallel tests. However, a post-merge review comment identified a thread-safety issue with the lazy initialization in
Context.cs:Race condition: Multiple threads calling
GetConsoleStdOutLineBuffer()simultaneously could each create their ownStringBuilderbefore the null-coalescing assignment completes, undermining the per-context buffer isolation that #4549 was designed to provide.Solution
Replace nullable
StringBuilder?fields withLazy<StringBuilder>for guaranteed thread-safe initialization:TUnit.Core/Context.cs:
StringBuilder? _consoleStdOutLineBuffer→Lazy<StringBuilder> _consoleStdOutLineBuffer = new(() => new StringBuilder())StringBuilder? _consoleStdErrLineBuffer→Lazy<StringBuilder> _consoleStdErrLineBuffer = new(() => new StringBuilder()).Valueinstead of??=operatorBenefits
✅ Thread-safe by default -
Lazy<T>guarantees single initialization even under concurrent access✅ No manual locking - No need for double-checked locking pattern
✅ Idiomatic C# - Standard approach for lazy initialization
✅ AOT-compatible - Works with Native AOT compilation
✅ Zero behavior change - Transparent fix maintaining all existing functionality
Testing
ParallelConsoleOutputTestsfrom fix: prevent console output mixing between parallel tests #4549 continue to validate correct behaviorAddresses feedback from: #4549 (comment)
🤖 Generated with Claude Code