lprun hangs when dumping an unfinished Task
Hi,
I'd like to report a possible bug with lprun. It seems that it is possible to indefinitely hang a thread by dumping an unfinished Task. I managed to reproduce it using the code below. It works in LinqPad(v5.40.00), but not via lprun.
This is probably due to the limitation of the console, and wanting the result of the finished Task to appear where it was initially dumped? But I think that in this case this leads to the undesireable behaviour of breaking the script.
Is this a real bug, or has this been already documented somewhere, and I just haven't read the manual?
I'd like to report a possible bug with lprun. It seems that it is possible to indefinitely hang a thread by dumping an unfinished Task. I managed to reproduce it using the code below. It works in LinqPad(v5.40.00), but not via lprun.
async Task Main() { var tcs = new TaskCompletionSource<bool>(); // Not awaited, so execution should continue // Comment to make the script finish even in lprun tcs.Task.Dump(); tcs.SetResult(true); "done".Dump(); }In my case I initially discovered this when I was making async network calls. I had one Task reading from the network, and another writing. When the writing side was blocked by dumping a task, even the reading side got blocked. Took a while to figure that out.
This is probably due to the limitation of the console, and wanting the result of the finished Task to appear where it was initially dumped? But I think that in this case this leads to the undesireable behaviour of breaking the script.
Is this a real bug, or has this been already documented somewhere, and I just haven't read the manual?
Comments
If you were to dump a task (synchronously), it would try to output the result of that task. And since nothing will be able to update that, there's no way it will be able to complete.
The problem would be in your script. Sounds like you have a producer/consumer setup and your producers are getting blocked somewhere.
If you are interested, I made a larger test case that simulates the problem I was having. In this test case the result of the Task is set asynchronously via another Task, instead of doing it in the main flow. When this is executed under LPRun, the network task never prints "Result set". Instead the whole program, including the network Task on the background, gets blocked Dumping the result of the second network call.
I understand that ultimately this is the result of mixing blocking IO with async stuff, but it came as a surprise, as the same code doesn't block in LINQPad. This makes it harder to write code that can be trusted to run succesfully in LPRun after testing it with just LINQPad.
In my opinion this is a problem, but I don't really have a good solution to propose. Maybe there could be DumpAsync, but it would be a pain to use it. Maybe there could be a command line option to not block on dumping unfinished Tasks and risk making the output more messy?
I'm sorry this a bit long:
I have a similar issue, with real code that have
async
stuff.First this thread mentioned that I need to use
Util.CreateSynchronizationContext
so the UI works. But now I have a problem that in LINQPad a script is working correctly but when run with lprun it hangs. RemovingUtil.CreateSynchronizationContext
helps in this case, as this time I don't need the UI elements.With
Util.CreateSynchronizationContext(true, true)
I get information that it ends onSynchronization Context Post (thread 19 to thread 16)
and thread 16 is the main/starting thread. So main thread awaits on task but that task posts something to the main thread => deadlock.But I print a message at the end of the task and it prints so the task should complete, but somehow the implementation of synchronization context in lprun is different and it posts something to main thread and it blocks.
This is enough to reproduce the issue. It works in LINQPad but deadlocks in lprun. I know that I have a workaround by not using the synchronization context but if I would need it (as linked topic suggests) I would be getting deadlocks.
It happens in LINQPad 5, LINQPad 6 and 7 crashes when posting to the main thread.
Util.CreateSynchronizationContext should be a no-op when called from lprun. I'll fix this in the next build.
Thanks.
Will it be included also in LINQPad 5?