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