nunit: Infinite loop in nunit 3.9
I am running a test suite with nunit 3.9 using nunit3-console, that randomly blocks at the end of the run. All of the tests derive from a base class that is declared with [Parallelizable(ParallelScope.All)] attribute.
I managed to break into nunit and the problem is that sometimes the NonParallel worker is stuck inside the OnEndOfShift() method of ParallelWorkItemDispatcher. The _topLevelWorkItem state is WorkItemState.Complete, and after all of the shifts have been run their queues are empty. No other worker is changing the state of the _topLevelWorkItem. Given this situation, the following code inside OnEndOfShift() is an endless loop:
// Shift has ended but all work may not yet be done
while (_topLevelWorkItem.State != WorkItemState.Complete)
{
// This will return null if all queues are empty.
nextShift = SelectNextShift();
if (nextShift != null)
{
ShiftStarting?.Invoke(nextShift);
nextShift.Start();
return;
}
}
The name of the _topLevelWorkItem is the name of the dll containing all the tests.
I was not able to recreate the problem with a small set of tests but I can make investigations about variable values, execution stacks, or order of events, if you need info.
About this issue
- Original URL
- State: closed
- Created 6 years ago
- Reactions: 1
- Comments: 19 (14 by maintainers)
Commits related to this issue
- #2761 executing AfterTest actions under ThreadAbort safe zone — committed to dukearena/nunit by deleted user 6 years ago
@dukearena OK, I see the point now. In any case, I think your fix takes care of the problem. Thanks!
I managed to reproduce the issue @algol-fi wrote here. Note that it can be reproduced also with Nunit 3.10.1.
The problem lies in the fact that some of the ParallelWorkers die during execution and before completion, and this seems to block the roll-up process he mentioned. This is also proved by logs reading this in the middle of the file:
Looking at the code, I noticed this block: https://github.com/nunit/nunit/blob/0e3ad2bc2c7695b605654fa56c1d456cd52a9bb9/src/NUnitFramework/framework/Internal/Commands/BeforeAndAfterTestCommand.cs#L55-L73
According to MSDN, ThreadAbortException is a special exception that always rethrows even if caught, unless
Thread.ResetAbort()is called. (reference: https://msdn.microsoft.com/en-us/library/system.threading.threadabortexception.aspx#Remarks).So, if a test has a
TimeoutAttributethat expires during execution, it is caught by thecatchblock and theResetAbort()method is called, but if the timeout expires during aTearDown, the exception is thrown during theAfterTestmethod execution, and nobody cancels the ThreadAbort, so eventually the thread is closed.You can find an example reproducing the issue here: https://github.com/dukearena/nunit-issue-2761-confirm Instruction to recreate the issue can be found into the repository home page.
Ah, this sounds similar to https://github.com/nunit/nunit/issues/2328!