cvs commit: hugs98/lib Prelude.hs hugs98/tests testScript.in
hugs98/tests/libs except1.output prodcon.output hugs98/tests/rts
ioerror1.output mvar.output
Alastair Reid
reid@glass.cse.ogi.edu
Mon, 3 Sep 2001 16:40:18 -0700
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