Drop the non-threaded scheduler's ad-hoc deadlock detection logic
Usually the RTS detects deadlocks via the garbage collector finding unreachable TSOs. For instance, a thread which is reachable only from, e.g., the blocking queue of an
MVar# which itself is not reachable must be deadlocked.
However, the non-threaded RTS also has a second path (in
scheduleDetectDeadlock) by which deadlocks can be identified: if we reach a state where the run queue is empty, even after a major GC (which might shake some threads lose due to, e.g., finalization) then the RTS considers the program deadlocked. Moreover, in the case of (a) the WinIO event manager, and (b) GHC RTS API users, this logic can cause false positives.
The WinIO case occurs when the program blocks on IO (which is achieved by blocking on an
IOPort), at which point the RTS should block on an IO completion port; when the IO finishes, the process will be awoken and the RTS will fill the
IOPort. However, the scheduler-based deadlock detection logic prevents this from happening, instead concluding that the process is deadlocked. WinIO currently attempts to work around this by introducing a new thread status,
BlockedOnIoCompletion, which disables the scheduler-based check. However, as found in #18382, this is not a robust solution and can cause some true deadlocks to be overlooked.
As far as I can tell there are no deadlocks which the scheduler-based deadlock detection logic can catch that the non-threaded scheduler cannot. In light of this, I suggest that it be dropped.