I am experiencing some temporary dead lock in my code and can't wrap my head around it.
Simple code (I cannot create a simple call chain to reproduce the code in InvokeChangeEvent
)
[Test]
public async void Test()
{
sut.InvokeChangeEvent("./foo.file");
// Event is handled by an async handler chaining multiple await resulting in a file write
// await Task.Delay(3000);
Assert.That(() => Directory.GetFiles("some dir").Count(), Is.EqualTo(3).After(15000, 300));
}
I am aware that y'all (:D) want executable code but I wasn't able to slice it down therefore I hope for some insight by explanation.
What happens: sut.InvokeChangeEvent
calls an event handler that later calls an async
event handler which then calls some async
. The end of the chain results in an Task.Run
that boils down to write 3 files.
The Assert above is implemented as a delegate with After
that returns a DelayedConstraint
and has a very large max time (15 secs) and a small polling interval.
Now when I debug the code the InvokeChangeEvent call is entirely executed to the last Task.Run but when the Task.Run returns, the execution is yielded back to the main thread and the Assert is executed entering the "wait with polling".
However the assert never succeeds. When I debug the issue the return of the Task.Run is always handled after the Assert delegate has run (and failed).
I've figured out, that when I place an await Task.Delay(3000);
before the Assert, then the code executes properly.
As mentioned the system under test has plenty await and Task.Runs chained and I was unable to reproduce the issue with some easy runnable code.
I've been googling around for a while and I cannot figure out why the Task.Run (which is executed on a different thread) yield in a (temporary) deadlock even though the DelayedConstraint
has an explicit polling interval to allow the main thread to progress.
It looks like the DelayedConstraint
locks the main thread by some sort of Thread.Sleep
. await Task.Delay
does not, I am aware of that. What confuses me is I have checked that I always do an await
(and never Task.Result
, etc) and therefore would expect that the file has been written before the Assert has executed.
(Note: Thread.Sleep
instead of await Task.Delay
does not work.)
Usually the DelayedConstraint
is used to ensure that file system has properly written all files as I have experienced some delays of the file system dealing with files.
I have some feeling that async void
event handler may create a situation which I do not understand.
If I manage to create a simple sample, I will update the thread.