
reid 2001/09/03 16:40:17 PDT Modified files: lib Prelude.hs tests testScript.in tests/libs except1.output prodcon.output tests/rts ioerror1.output mvar.output Log: This commit is all based on the following change to the scheduler loop in the Prelude: 1766c1766 < loop (Hugs_ForkThread a b:r) = loop (b:a:r) --- > loop (Hugs_ForkThread a b:r) = loop (a:b:r) [all other changes are to make the testsuite happy with the change] This has the effect of changing what happens when a thread becomes runnable. The old behaviour was to schedule the new thread immediately. That is, the new thread would preempt the current thread. The new behaviour is to maintain the current thread and just add the new thread to the list of runnable threads. The effect of this is that a thread executing a sequence of takes and puts (e.g., the sequence involved in reading from or writing to a Chan) will not be preempted unless one of the takes is on an empty mvar or one of the puts is on a full mvar. This is important because of one of the subtleties of the Hugs' thread implementation: each invocation of hugsIORun (e.g., the initial call to main, callbacks from C code, calls to unsafePerformIO, etc.) creates its own list of runnable threads and runs until all threads in that list are done (and, reports deadlock if the main thread from that invocation hasn't returned a value). Since threads cannot migrate from one runnable list to another, there's a distinct possibility of deadlock when the main thread in one invocation is blocked waiting for a thread in another list to wake it up. [I saw exactly this happening in the Win32 implementation of the HGL where callbacks from C into Haskell insert events into the event channel of each window. One thread would start writing to a Chan object but would get preempted before it finished. The preempting thread would then make a call to C which calls back into Haskell and tries to read the value written to the Chan. But it isn't allowed to read the value until the writer completes so it blocks. Now the writer should start running again but it can't because, although it is runnable, it is on a different runnable list so it gets ignored. So now the runnable list is empty and the callback terminates with a deadlock error message.] The most obvious fix would be to create a single global mutable list of threads but I'll save this (huge!) change for another day. [If anyone wants to take a stab at this before I get round to it (next 3-4 day weekend :-)), feel free. That'd probably be a good time to try implementing GHC's killThread and other asynchronous exception features.] A simpler fix is to prevent the first thread from being preempted unless it actually blocks. This change required changes to the sample output in the testsuite. All changes are either: 1) A permutation of the lines in the old file; or 2) Correcting for the change in the output format of IOErrors. (Should have been a separate fix but I only just noticed the problem.) Revision Changes Path 1.13 +1 -1 hugs98/lib/Prelude.hs 1.7 +3 -3 hugs98/tests/testScript.in 1.3 +21 -21 hugs98/tests/libs/except1.output 1.2 +12 -12 hugs98/tests/libs/prodcon.output 1.2 +4 -2 hugs98/tests/rts/ioerror1.output 1.3 +3 -3 hugs98/tests/rts/mvar.output