Simon Jakobi pushed to branch wip/sjakobi/T27547-testsuite-robustness-reword at Glasgow Haskell Compiler / GHC

Commits:

4 changed files:

Changes:

  • compiler/GHC/Runtime/Interpreter.hs
    ... ... @@ -113,6 +113,7 @@ import qualified GHC.Exts.Heap as Heap
    113 113
     import GHC.Stack.CCS (CostCentre,CostCentreStack)
    
    114 114
     import System.Directory
    
    115 115
     import System.Process
    
    116
    +import System.Timeout (timeout)
    
    116 117
     import qualified GHC.InfoProv as InfoProv
    
    117 118
     
    
    118 119
     import GHC.Builtin.Modules( gHC_PRIM, gHC_PRIMOPWRAPPERS )
    
    ... ... @@ -652,10 +653,16 @@ stopInterp interp = case interpInstance interp of
    652 653
             case state of
    
    653 654
               InterpPending    -> pure state -- already stopped
    
    654 655
               InterpRunning i  -> do
    
    655
    -            ex <- getProcessExitCode (interpHandle (instProcess i))
    
    656
    -            if isJust ex
    
    657
    -               then pure ()
    
    658
    -               else sendMessage i Shutdown
    
    656
    +            let hdl = interpHandle (instProcess i)
    
    657
    +            ex <- getProcessExitCode hdl
    
    658
    +            unless (isJust ex) $ do
    
    659
    +              sendMessage i Shutdown
    
    660
    +              -- The interpreter process shares our stdout/stderr; wait for
    
    661
    +              -- it to exit (flushing its output), lest output it produced
    
    662
    +              -- on our behalf be lost if it is killed once we exit.
    
    663
    +              -- Bounded, in case the process is wedged.
    
    664
    +              _ <- timeout 5000000 {- 5s -} (waitForProcess hdl)
    
    665
    +              pure ()
    
    659 666
                 pure InterpPending
    
    660 667
     
    
    661 668
     -- -----------------------------------------------------------------------------
    

  • testsuite/config/ghc
    ... ... @@ -197,6 +197,11 @@ debug_ways = [x[0] for x in config.way_flags.items()
    197 197
     threaded_ways = [x[0] for x in config.way_flags.items()
    
    198 198
                           if '-threaded' in x[1] or 'ghci' == x[0] or 'ghci-opt' == x[0]]
    
    199 199
     
    
    200
    +# #27547
    
    201
    +nonmoving_threaded_ways = [name for name, flags in config.way_flags.items()
    
    202
    +                                if '-threaded' in flags
    
    203
    +                                if '-xn' in config.way_rts_flags.get(name, [])]
    
    204
    +
    
    200 205
     # Ways which run with multiple capabilities
    
    201 206
     concurrent_ways = [name for name, flags in config.way_flags.items()
    
    202 207
                             if '-threaded' in flags or 'ghci' == name or 'ghci-opt' == name
    

  • testsuite/tests/rts/all.T
    ... ... @@ -668,7 +668,11 @@ test('IOManager', [js_skip, when(arch('wasm32'), skip), when(opsys('mingw32'), s
    668 668
     test('T24142', [req_target_smp], compile_and_run, ['-threaded -with-rtsopts "-N2"'])
    
    669 669
     
    
    670 670
     test('T25232', [unless(have_profiling(), skip), only_ways(['normal','nonmoving','nonmoving_prof','nonmoving_thr_prof']), extra_ways(['nonmoving', 'nonmoving_prof'] + (['nonmoving_thr_prof'] if have_threaded() else []))], compile_and_run, [''])
    
    671
    -test('T25280', [unless(opsys('linux'),skip),req_process,js_skip], compile_and_run, [''])
    
    671
    +# T25280 waits for its forked children, so it detects the forkProcess
    
    672
    +# child-shutdown deadlock of #27547 (by timing out).
    
    673
    +test('T25280', [unless(opsys('linux'),skip),req_process,js_skip,
    
    674
    +                expect_broken_for(27547, nonmoving_threaded_ways)],
    
    675
    +               compile_and_run, [''])
    
    672 676
     
    
    673 677
     # N.B. This will likely issue a warning on stderr but we merely care that the
    
    674 678
     # program doesn't crash.
    

  • testsuite/timeout/timeout.py
    ... ... @@ -27,6 +27,19 @@ try:
    27 27
                     else:
    
    28 28
                         raise e
    
    29 29
     
    
    30
    +    def killStragglers(pid):
    
    31
    +        # A test can leave descendants that outlive its main process (e.g. a
    
    32
    +        # deadlocked forkProcess child, #27547). They hold the inherited
    
    33
    +        # stdout/stderr pipes open and would hang the testsuite driver, so
    
    34
    +        # kill the whole process group.
    
    35
    +        try:
    
    36
    +            os.killpg(pid, signal.SIGKILL)
    
    37
    +        except OSError as e:
    
    38
    +            # ESRCH: group already gone. EPERM: macOS quirk — signalling a
    
    39
    +            # group whose leader is a reaped zombie.
    
    40
    +            if e.errno not in (errno.ESRCH, errno.EPERM):
    
    41
    +                raise e
    
    42
    +
    
    30 43
         pid = os.fork()
    
    31 44
         if pid == 0:
    
    32 45
             # child
    
    ... ... @@ -40,6 +53,9 @@ try:
    40 53
             old = signal.signal(signal.SIGALRM, handler)
    
    41 54
             signal.alarm(secs)
    
    42 55
             (pid2, res) = os.waitpid(pid, 0)
    
    56
    +        # The handler signals pid, so it must not run once pid is reaped.
    
    57
    +        signal.alarm(0)
    
    58
    +        killStragglers(pid)
    
    43 59
             if (os.WIFEXITED(res)):
    
    44 60
                 sys.exit(os.WEXITSTATUS(res))
    
    45 61
             elif os.WIFSIGNALED(res):