- 
          
 - 
                Notifications
    
You must be signed in to change notification settings  - Fork 94
 
Fix race condition in pure reference counting object disposal system #2868
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
Co-authored-by: thomhurst <[email protected]>
Co-authored-by: thomhurst <[email protected]>
These methods were never called and represent dead code. The timeout attribute is captured in the generated code but the 30-minute default is never overridden (that's a separate issue). 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
| 
           Claude finished @Copilot's task —— View job Code Review CompletedI've thoroughly reviewed this disposal fix PR. Here's my comprehensive feedback: Todo List: 
 Overall Assessment ✅This is a well-implemented fix for a critical disposal issue. The solution correctly addresses the root cause and follows established patterns in the codebase. Code Quality & Best Practices ✅Excellent Implementation: 
 Clean code removal: The dead code removal in  Technical Correctness ✅Disposal Flow is Correct: 
 Reference Counting Works Properly: 
 Potential Issues & Considerations
 Performance Considerations ✅Minimal Impact: 
 Good Performance Characteristics: 
 Security Considerations ✅No Security Issues Identified: 
 Test Coverage Assessment
 
 Existing validation is good: Container cleanup logs confirm proper disposal in real-world scenarios. Recommendations
 SummaryThis is a high-quality fix that correctly solves the disposal issue. The implementation is technically sound, follows established patterns, and handles the async disposal complexity properly. The only minor improvement would be adding exception resilience to the disposal chain. Recommendation: Approve with consideration for the suggested exception handling enhancement.  | 
    
- Added 5-second timeout for each OnDispose event handler - Prevents tests from hanging indefinitely if disposal logic deadlocks - Logs warnings when disposal times out, errors are logged but don't fail the test - This ensures robust cleanup even when disposal handlers misbehave 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
| 
           Claude finished @Copilot's task —— View job PR Review: Disposal Fix ImplementationTodo List: 
 Overall Assessment ✅This is a well-implemented fix for a critical disposal issue. The solution correctly addresses the root cause and follows established patterns in the codebase. Code Quality & Best Practices ✅Excellent Implementation: 
 Clean code removal: The dead code removal in  Technical Correctness ✅Disposal Flow is Correct: 
 Reference Counting Works Properly: 
 Improved Exception Handling ✅The latest implementation shows significant improvements over the initial version: // SingleTestExecutor.cs:301-323 - Robust disposal handling
try
{
    // Add a reasonable timeout for disposal operations (5 seconds)
    var disposeTask = invocation.InvokeAsync(test.Context, test.Context).AsTask();
    var timeoutTask = Task.Delay(TimeSpan.FromSeconds(5));
    var completedTask = await Task.WhenAny(disposeTask, timeoutTask).ConfigureAwait(false);
    
    if (completedTask == timeoutTask)
    {
        await _logger.LogWarningAsync("OnDispose event timed out after 5 seconds").ConfigureAwait(false);
    }
    else
    {
        // Ensure any exceptions from the dispose task are observed
        await disposeTask.ConfigureAwait(false);
    }
}
catch (Exception ex)
{
    // Log but don't throw - we still need to dispose other objects and the test instance
    await _logger.LogErrorAsync($"Error during OnDispose event: {ex.Message}").ConfigureAwait(false);
}Excellent resilience features: 
 Performance Considerations ✅Minimal Impact: 
 Good Performance Characteristics: 
 Security Considerations ✅No Security Issues Identified: 
 Test Coverage Assessment
 
 Existing validation is good: Container cleanup logs confirm proper disposal in real-world scenarios. Recommendations
 SummaryThis is a high-quality fix that correctly solves the disposal issue. The implementation is technically sound, follows established patterns, and handles complex async disposal scenarios with excellent resilience. The timeout protection and exception handling show careful consideration of real-world edge cases. Recommendation: Approve - This implementation addresses all the concerns that could arise from the disposal chain and provides robust error handling. The fix ensures proper cleanup of shared disposable resources like   | 
    
| 
           @copilot the change to call dispose is how causing hanging. Investigate and fix  | 
    
Co-authored-by: thomhurst <[email protected]>
This PR implements a pure reference counting object disposal system that fixes critical hanging and
ObjectDisposedExceptionissues in TUnit tests.Root Cause
The previous implementation had complex "shared object" detection logic causing race conditions and deadlocks. Objects were being disposed based on sharing type rather than actual usage, and asynchronous background disposal created timing issues where test classes would try to access already-disposed objects.
Solution
Implemented a pure reference counting system with synchronous disposal:
Key Changes
ObjectTrackerTask.Run()background disposal that was causingObjectDisposedExceptionwhen test class disposal methods tried to access objects being disposed on background threadsTechnical Details
The critical fix addresses this sequence issue:
Object disposal starts on background thread→ Object disposes immediately and synchronouslyDisposeAsync()runs →Tries to use object being disposed →→ Uses fully disposed object safelyObjectDisposedExceptionThis ensures shared disposable resources like
WebApplicationFactorywith different lifecycle scopes dispose correctly without causing test execution to freeze or throw disposal exceptions.Fixes #2867.
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.