Rodrigo Mesquita pushed to branch wip/romes/multi-threaded-debugger at Glasgow Haskell Compiler / GHC

Commits:

9 changed files:

Changes:

  • compiler/GHC/Runtime/Eval.hs
    ... ... @@ -188,12 +188,13 @@ execOptions = ExecOptions
    188 188
         -- by default we just care about breakpoints hit in this eval thread
    
    189 189
       }
    
    190 190
     
    
    191
    --- | Run a statement in the current interactive context.
    
    191
    +-- | Run a statement in the current interactive context, as a new thread in the interpreter.
    
    192
    +-- Returns an action which blocks waiting for the execution to finish and yield a result.
    
    192 193
     execStmt
    
    193 194
       :: GhcMonad m
    
    194 195
       => String             -- ^ a statement (bind or expression)
    
    195 196
       -> ExecOptions
    
    196
    -  -> m ExecResult
    
    197
    +  -> m (m ExecResult)
    
    197 198
     execStmt input exec_opts@ExecOptions{..} = do
    
    198 199
         hsc_env <- getSession
    
    199 200
     
    
    ... ... @@ -204,13 +205,15 @@ execStmt input exec_opts@ExecOptions{..} = do
    204 205
     
    
    205 206
         case mb_stmt of
    
    206 207
           -- empty statement / comment
    
    207
    -      Nothing -> return (ExecComplete (Right []) 0)
    
    208
    +      Nothing -> pure (pure (ExecComplete (Right []) 0))
    
    208 209
           Just stmt -> execStmt' stmt input exec_opts
    
    209 210
     
    
    210 211
     -- | Like `execStmt`, but takes a parsed statement as argument. Useful when
    
    211 212
     -- doing preprocessing on the AST before execution, e.g. in GHCi (see
    
    212 213
     -- GHCi.UI.runStmt).
    
    213
    -execStmt' :: GhcMonad m => GhciLStmt GhcPs -> String -> ExecOptions -> m ExecResult
    
    214
    +--
    
    215
    +-- Returns an action which blocks waiting for the execution to finish and yield a result.
    
    216
    +execStmt' :: GhcMonad m => GhciLStmt GhcPs -> String -> ExecOptions -> m (m ExecResult)
    
    214 217
     execStmt' stmt stmt_text ExecOptions{..} = do
    
    215 218
         hsc_env <- getSession
    
    216 219
         let interp = hscInterp hsc_env
    
    ... ... @@ -226,21 +229,22 @@ execStmt' stmt stmt_text ExecOptions{..} = do
    226 229
         case r of
    
    227 230
           Nothing ->
    
    228 231
             -- empty statement / comment
    
    229
    -        return (ExecComplete (Right []) 0)
    
    232
    +        pure (pure (ExecComplete (Right []) 0))
    
    230 233
           Just (ids, hval, fix_env) -> do
    
    231 234
             updateFixityEnv fix_env
    
    232 235
     
    
    233 236
             let eval_opts = initEvalOpts idflags' (enableGhcStepMode execSingleStep)
    
    234 237
                               (enableIsolateThreadBreaks execIsolateMode)
    
    235
    -        status <- liftIO $ evalStmt interp eval_opts (execWrap hval)
    
    238
    +        rtid <- liftIO $ evalStmt interp eval_opts (execWrap hval)
    
    236 239
     
    
    237 240
             let ic = hsc_IC hsc_env
    
    238 241
                 bindings = (ic_tythings ic, ic_gre_cache ic)
    
    239 242
     
    
    240 243
                 size = ghciHistSize idflags'
    
    241
    -
    
    242
    -        handleRunStatus execSingleStep eval_opts stmt_text bindings ids
    
    243
    -                        status (emptyHistory size)
    
    244
    +        pure $ do
    
    245
    +          status <- liftIO (waitThreadResult interp rtid)
    
    246
    +          handleRunStatus execSingleStep eval_opts stmt_text bindings ids
    
    247
    +                          status (emptyHistory size)
    
    244 248
     
    
    245 249
     runDecls :: GhcMonad m => String -> m [TyThing]
    
    246 250
     runDecls = runDeclsWithLocation "<interactive>" 1
    
    ... ... @@ -326,16 +330,16 @@ handleRunStatus step eval_opts expr bindings final_ids status history0 = do
    326 330
           return (ExecComplete (Left (fromSerializableException e)) alloc)
    
    327 331
     
    
    328 332
         -- Nothing case: we stopped when an exception was raised, not at a breakpoint.
    
    329
    -    EvalBreak apStack_ref Nothing resume_ctxt ccs -> do
    
    330
    -      resume_ctxt_fhv <- liftIO $ mkFinalizedHValue interp resume_ctxt
    
    331
    -      apStack_fhv     <- liftIO $ mkFinalizedHValue interp apStack_ref
    
    333
    +    EvalBreak apStack_ref Nothing resume_tid ccs -> do
    
    334
    +      resume_tid_fhv <- liftIO $ mkFinalizedHValue interp resume_tid
    
    335
    +      apStack_fhv    <- liftIO $ mkFinalizedHValue interp apStack_ref
    
    332 336
           let span = mkGeneralSrcSpan (fsLit "<unknown>")
    
    333 337
           (hsc_env1, break_ids) <- liftIO $
    
    334 338
             bindLocalsAtBreakpoint hsc_env apStack_fhv span Nothing
    
    335 339
           let
    
    336 340
             resume = Resume
    
    337 341
               { resumeStmt = expr
    
    338
    -          , resumeContext = resume_ctxt_fhv
    
    342
    +          , resumeContext = resume_tid_fhv
    
    339 343
               , resumeBindings = bindings
    
    340 344
               , resumeFinalIds = final_ids
    
    341 345
               , resumeApStack = apStack_fhv
    
    ... ... @@ -356,7 +360,7 @@ handleRunStatus step eval_opts expr bindings final_ids status history0 = do
    356 360
         -- The interpreter yields on a breakpoint if:
    
    357 361
         --  - the breakpoint was explicitly enabled (in @BreakArray@)
    
    358 362
         --  - or one of the stepping options in @EvalOpts@ caused us to stop at one
    
    359
    -    EvalBreak apStack_ref (Just eval_break) resume_ctxt ccs -> do
    
    363
    +    EvalBreak apStack_ref (Just eval_break) resume_tid ccs -> do
    
    360 364
           let ibi = evalBreakpointToId eval_break
    
    361 365
           let hug = hsc_HUG hsc_env
    
    362 366
           info_brks  <- liftIO $ readIModBreaks hug ibi
    
    ... ... @@ -368,8 +372,8 @@ handleRunStatus step eval_opts expr bindings final_ids status history0 = do
    368 372
             breakArray <- getBreakArray interp ibi info_brks
    
    369 373
             breakpointStatus interp breakArray (ibi_info_index ibi)
    
    370 374
     
    
    371
    -      apStack_fhv <- liftIO $ mkFinalizedHValue interp apStack_ref
    
    372
    -      resume_ctxt_fhv   <- liftIO $ mkFinalizedHValue interp resume_ctxt
    
    375
    +      apStack_fhv    <- liftIO $ mkFinalizedHValue interp apStack_ref
    
    376
    +      resume_tid_fhv <- liftIO $ mkFinalizedHValue interp resume_tid
    
    373 377
     
    
    374 378
           -- This breakpoint is enabled or we mean to break here;
    
    375 379
           -- we want to stop instead of just logging it.
    
    ... ... @@ -382,7 +386,7 @@ handleRunStatus step eval_opts expr bindings final_ids status history0 = do
    382 386
             let
    
    383 387
               resume = Resume
    
    384 388
                 { resumeStmt = expr
    
    385
    -            , resumeContext = resume_ctxt_fhv
    
    389
    +            , resumeContext = resume_tid_fhv
    
    386 390
                 , resumeBindings = bindings
    
    387 391
                 , resumeFinalIds = final_ids
    
    388 392
                 , resumeApStack = apStack_fhv
    
    ... ... @@ -398,7 +402,9 @@ handleRunStatus step eval_opts expr bindings final_ids status history0 = do
    398 402
             return (ExecBreak break_ids (Just ibi) resume)
    
    399 403
           else do
    
    400 404
             -- resume with the same step type
    
    401
    -        status <- liftIO $ GHCi.resumeStmt interp eval_opts resume_ctxt_fhv
    
    405
    +        status <- liftIO $ do
    
    406
    +          GHCi.resumeStmt interp eval_opts resume_tid_fhv
    
    407
    +          GHCi.waitThreadResult interp resume_tid_fhv
    
    402 408
             history <- if not tracing then pure history0 else do
    
    403 409
               history1 <- liftIO $ mkHistory hug apStack_fhv ibi
    
    404 410
               let !history' = history1 `consBL` history0
    
    ... ... @@ -414,7 +420,9 @@ resumeExec :: GhcMonad m
    414 420
                -> ThreadBreaksIsolationMode
    
    415 421
                -> Maybe Int
    
    416 422
                -> Resume
    
    417
    -           -> m ExecResult
    
    423
    +           -> m (m ExecResult)
    
    424
    +           -- ^ Returns an action which blocks waiting for the resumed thread
    
    425
    +           -- to break again or yield a result
    
    418 426
     resumeExec step isolateMode mbCnt r = do
    
    419 427
       hsc_env <- getSession
    
    420 428
       let ic = hsc_IC hsc_env
    
    ... ... @@ -438,7 +446,7 @@ resumeExec step isolateMode mbCnt r = do
    438 446
     
    
    439 447
       case r of
    
    440 448
         Resume { resumeStmt = expr
    
    441
    -           , resumeContext = fhv
    
    449
    +           , resumeContext = rtid
    
    442 450
                , resumeBindings = bindings
    
    443 451
                , resumeFinalIds = final_ids
    
    444 452
                , resumeApStack = apStack
    
    ... ... @@ -454,17 +462,19 @@ resumeExec step isolateMode mbCnt r = do
    454 462
     
    
    455 463
               let eval_opts = initEvalOpts dflags (enableGhcStepMode step)
    
    456 464
                                 (enableIsolateThreadBreaks isolateMode)
    
    457
    -          status <- liftIO $ GHCi.resumeStmt interp eval_opts fhv
    
    458
    -          let prevHistoryLst = fromListBL 50 hist
    
    459
    -              hug = hsc_HUG hsc_env
    
    460
    -              hist' = case mb_brkpt of
    
    461
    -                 Nothing -> pure prevHistoryLst
    
    462
    -                 Just bi
    
    463
    -                   | breakHere False step span -> do
    
    464
    -                      hist1 <- liftIO (mkHistory hug apStack bi)
    
    465
    -                      return $ hist1 `consBL` fromListBL 50 hist
    
    466
    -                   | otherwise -> pure prevHistoryLst
    
    467
    -          handleRunStatus step eval_opts expr bindings final_ids status =<< hist'
    
    465
    +          liftIO $ GHCi.resumeStmt interp eval_opts rtid
    
    466
    +          pure $ do
    
    467
    +            status <- liftIO $ GHCi.waitThreadResult interp rtid
    
    468
    +            let prevHistoryLst = fromListBL 50 hist
    
    469
    +                hug = hsc_HUG hsc_env
    
    470
    +                hist' = case mb_brkpt of
    
    471
    +                   Nothing -> pure prevHistoryLst
    
    472
    +                   Just bi
    
    473
    +                     | breakHere False step span -> do
    
    474
    +                        hist1 <- liftIO (mkHistory hug apStack bi)
    
    475
    +                        return $ hist1 `consBL` fromListBL 50 hist
    
    476
    +                     | otherwise -> pure prevHistoryLst
    
    477
    +            handleRunStatus step eval_opts expr bindings final_ids status =<< hist'
    
    468 478
     
    
    469 479
     setupBreakpoint :: GhcMonad m => Interp -> InternalBreakpointId -> Int -> m ()   -- #19157
    
    470 480
     setupBreakpoint interp ibi cnt = do
    
    ... ... @@ -1275,7 +1285,9 @@ compileParsedExprRemote expr@(L loc _) = withSession $ \hsc_env -> do
    1275 1285
     
    
    1276 1286
       updateFixityEnv fix_env
    
    1277 1287
       let eval_opts = initEvalOpts dflags EvalStepNone True
    
    1278
    -  status <- liftIO $ evalStmt interp eval_opts (EvalThis hvals_io)
    
    1288
    +  status <- liftIO $ do
    
    1289
    +    rtid <- evalStmt interp eval_opts (EvalThis hvals_io)
    
    1290
    +    waitThreadResult interp rtid
    
    1279 1291
       case status of
    
    1280 1292
         EvalComplete _ (EvalSuccess [hval]) -> return hval
    
    1281 1293
         EvalComplete _ (EvalException e) ->
    

  • compiler/GHC/Runtime/Interpreter.hs
    ... ... @@ -11,6 +11,7 @@ module GHC.Runtime.Interpreter
    11 11
       , evalStmt, EvalStatus_(..), EvalStatus, EvalResult(..), EvalExpr(..)
    
    12 12
       , resumeStmt
    
    13 13
       , abandonStmt
    
    14
    +  , waitThreadResult
    
    14 15
       , evalIO
    
    15 16
       , evalString
    
    16 17
       , evalStringToIOString
    
    ... ... @@ -286,11 +287,11 @@ evalStmt
    286 287
       :: Interp
    
    287 288
       -> EvalOpts
    
    288 289
       -> EvalExpr ForeignHValue
    
    289
    -  -> IO (EvalStatus_ [ForeignHValue] [HValueRef])
    
    290
    +  -> IO (ForeignRef ThreadId)
    
    290 291
     evalStmt interp opts foreign_expr = do
    
    291
    -  status <- withExpr foreign_expr $ \expr ->
    
    292
    +  r <- withExpr foreign_expr $ \expr ->
    
    292 293
         interpCmd interp (EvalStmt opts expr)
    
    293
    -  handleEvalStatus interp status
    
    294
    +  mkFinalizedHValue interp r
    
    294 295
      where
    
    295 296
       withExpr :: EvalExpr ForeignHValue -> (EvalExpr HValueRef -> IO a) -> IO a
    
    296 297
       withExpr (EvalThis fhv) cont =
    
    ... ... @@ -304,17 +305,25 @@ resumeStmt
    304 305
       :: Interp
    
    305 306
       -> EvalOpts
    
    306 307
       -> ForeignRef ThreadId
    
    307
    -  -> IO (EvalStatus_ [ForeignHValue] [HValueRef])
    
    308
    +  -> IO ()
    
    308 309
     resumeStmt interp opts resume_tid = do
    
    309
    -  status <- withForeignRef resume_tid $ \rhv ->
    
    310
    +  withForeignRef resume_tid $ \rhv ->
    
    310 311
         interpCmd interp (ResumeStmt opts rhv)
    
    311
    -  handleEvalStatus interp status
    
    312 312
     
    
    313 313
     abandonStmt :: Interp -> ForeignRef ThreadId -> IO ()
    
    314 314
     abandonStmt interp resume_tid =
    
    315 315
       withForeignRef resume_tid $ \rhv ->
    
    316 316
         interpCmd interp (AbandonStmt rhv)
    
    317 317
     
    
    318
    +waitThreadResult
    
    319
    +  :: Interp
    
    320
    +  -> ForeignRef ThreadId
    
    321
    +  -> IO (EvalStatus_ [ForeignHValue] [HValueRef])
    
    322
    +waitThreadResult interp tid = do
    
    323
    +  status <- withForeignRef tid $ \rhv -> do
    
    324
    +    interpCmd interp (WaitThreadResult rhv)
    
    325
    +  handleEvalStatus interp status
    
    326
    +
    
    318 327
     handleEvalStatus
    
    319 328
       :: Interp
    
    320 329
       -> EvalStatus [HValueRef]
    
    ... ... @@ -417,8 +426,9 @@ whereFrom interp ref =
    417 426
     seqHValue :: Interp -> UnitEnv -> Logger -> ForeignHValue -> IO (EvalResult ())
    
    418 427
     seqHValue interp unit_env logger ref =
    
    419 428
       withForeignRef ref $ \hval -> do
    
    420
    -    status <- interpCmd interp (Seq hval)
    
    421
    -    handleSeqHValueStatus interp unit_env logger status
    
    429
    +    rtid   <- interpCmd interp (Seq hval)
    
    430
    +    status <- interpCmd interp (WaitThreadResult rtid)
    
    431
    +    handleSeqHValueStatus interp unit_env logger (clearEvalStatus status)
    
    422 432
     
    
    423 433
     evalBreakpointToId :: EvalBreakpoint -> InternalBreakpointId
    
    424 434
     evalBreakpointToId eval_break =
    
    ... ... @@ -460,9 +470,10 @@ handleSeqHValueStatus interp unit_env logger eval_status =
    460 470
                 Just modbreaks -> put =<< getBreakLoc (readIModModBreaks hug) ibi modbreaks
    
    461 471
     
    
    462 472
           -- resume the seq (:force) processing in the iserv process
    
    463
    -      withForeignRef resume_ctxt_fhv $ \hval -> do
    
    464
    -        status <- interpCmd interp (ResumeSeq hval)
    
    465
    -        handleSeqHValueStatus interp unit_env logger status
    
    473
    +      withForeignRef resume_ctxt_fhv $ \rtid -> do
    
    474
    +        interpCmd interp (ResumeSeq rtid)
    
    475
    +        status <- interpCmd interp (WaitThreadResult rtid)
    
    476
    +        handleSeqHValueStatus interp unit_env logger (clearEvalStatus status)
    
    466 477
         (EvalComplete _ r) -> return r
    
    467 478
     
    
    468 479
     
    
    ... ... @@ -626,11 +637,9 @@ spawnIServ conf = do
    626 637
                                               []
    
    627 638
                                               (iservConfOpts    conf)
    
    628 639
       interpPipe <- mkPipeFromHandles rh wh
    
    629
    -  lock <- newMVar ()
    
    630 640
       let process = InterpProcess
    
    631 641
                       { interpHandle = ph
    
    632 642
                       , interpPipe
    
    633
    -                  , interpLock   = lock
    
    634 643
                       }
    
    635 644
     
    
    636 645
       pending_frees <- newMVar []
    
    ... ... @@ -771,3 +780,9 @@ readIModModBreaks hug mod = imodBreaks_modBreaks . expectJust <$> readIModBreaks
    771 780
     fromEvalResult :: EvalResult a -> IO a
    
    772 781
     fromEvalResult (EvalException e) = throwIO (fromSerializableException e)
    
    773 782
     fromEvalResult (EvalSuccess a) = return a
    
    783
    +
    
    784
    +clearEvalStatus :: EvalStatus a -> EvalStatus ()
    
    785
    +clearEvalStatus = \case
    
    786
    +  EvalComplete w (EvalException se) -> EvalComplete w (EvalException se)
    
    787
    +  EvalComplete w (EvalSuccess _)    -> EvalComplete w (EvalSuccess ())
    
    788
    +  EvalBreak ap mb rt rccs           -> EvalBreak ap mb rt rccs

  • compiler/GHC/Runtime/Interpreter/JS.hs
    ... ... @@ -127,11 +127,9 @@ startTHRunnerProcess interp_js settings = do
    127 127
       std_in <- readIORef interp_in
    
    128 128
     
    
    129 129
       interpPipe <- mkPipeFromHandles rh wh
    
    130
    -  lock <- newMVar ()
    
    131 130
       let proc = InterpProcess
    
    132 131
                   { interpHandle = hdl
    
    133 132
                   , interpPipe
    
    134
    -              , interpLock   = lock
    
    135 133
                   }
    
    136 134
       pure (std_in, proc)
    
    137 135
     
    
    ... ... @@ -382,7 +380,7 @@ jsLinkPlan logger tmpfs tmp_dir ar_cache link_cfg cfg inst link_plan = do
    382 380
       --   Haskell server.
    
    383 381
       server_started <- jsServerStarted <$> readMVar (instJSState (instExtra inst))
    
    384 382
       if server_started
    
    385
    -    then sendMessageNoResponse inst $ LoadObj all_js
    
    383
    +    then sendMessage inst $ LoadObj all_js
    
    386 384
         else jsLoadFile            inst all_js
    
    387 385
     
    
    388 386
       ----------------------------------------------------------------
    

  • compiler/GHC/Runtime/Interpreter/Process.hs
    ... ... @@ -2,16 +2,7 @@ module GHC.Runtime.Interpreter.Process
    2 2
       (
    
    3 3
       -- * Message API
    
    4 4
         Message(..)
    
    5
    -  , DelayedResponse (..)
    
    6
    -  -- * Top-level message API (these acquire/release a lock)
    
    7 5
       , sendMessage
    
    8
    -  , sendMessageNoResponse
    
    9
    -  , sendMessageDelayedResponse
    
    10
    -  , receiveDelayedResponse
    
    11
    -  -- * Nested message API (these require the interpreter to already be locked)
    
    12
    -  , sendAnyValue
    
    13
    -  , receiveAnyValue
    
    14
    -  , receiveTHMessage
    
    15 6
       )
    
    16 7
     where
    
    17 8
     
    
    ... ... @@ -27,79 +18,13 @@ import GHC.Utils.Exception as Ex
    27 18
     import Data.Binary
    
    28 19
     import System.Exit
    
    29 20
     import System.Process
    
    30
    -import Control.Concurrent.MVar (MVar, withMVar, takeMVar, putMVar, isEmptyMVar)
    
    31
    -
    
    32
    -data DelayedResponse a = DelayedResponse
    
    33 21
     
    
    34 22
     -- -----------------------------------------------------------------------------
    
    35 23
     -- Top-level Message API
    
    36 24
     
    
    37
    --- | Send a message to the interpreter process that doesn't expect a response
    
    38
    ---   (locks the interpreter while sending)
    
    39
    -sendMessageNoResponse :: ExtInterpInstance d -> Message () -> IO ()
    
    40
    -sendMessageNoResponse i m =
    
    41
    -  withLock i $ writeInterpProcess (instProcess i) (putMessage m)
    
    42
    -
    
    43
    --- | Send a message to the interpreter that expects a response
    
    44
    ---   (locks the interpreter while until the response is received)
    
    25
    +-- | Send a message to the interpreter and expect a response, blocking until it arrives.
    
    45 26
     sendMessage :: Binary a => ExtInterpInstance d -> Message a -> IO a
    
    46
    -sendMessage i m = withLock i $ callInterpProcess (instProcess i) m
    
    47
    -
    
    48
    --- | Send a message to the interpreter process whose response is expected later
    
    49
    ---
    
    50
    --- This is useful to avoid forgetting to receive the value and to ensure that
    
    51
    --- the type of the response isn't lost. Use receiveDelayedResponse to read it.
    
    52
    --- (locks the interpreter until the response is received using
    
    53
    --- `receiveDelayedResponse`)
    
    54
    -sendMessageDelayedResponse :: ExtInterpInstance d -> Message a -> IO (DelayedResponse a)
    
    55
    -sendMessageDelayedResponse i m = do
    
    56
    -  lock i
    
    57
    -  writeInterpProcess (instProcess i) (putMessage m)
    
    58
    -  pure DelayedResponse
    
    59
    -
    
    60
    --- | Expect a delayed result to be received now
    
    61
    -receiveDelayedResponse :: Binary a => ExtInterpInstance d -> DelayedResponse a -> IO a
    
    62
    -receiveDelayedResponse i DelayedResponse = do
    
    63
    -  ensureLocked i
    
    64
    -  r <- readInterpProcess (instProcess i) get
    
    65
    -  unlock i
    
    66
    -  pure r
    
    67
    -
    
    68
    --- -----------------------------------------------------------------------------
    
    69
    --- Nested Message API
    
    70
    -
    
    71
    --- | Send any value (requires locked interpreter)
    
    72
    -sendAnyValue :: Binary a => ExtInterpInstance d -> a -> IO ()
    
    73
    -sendAnyValue i m = ensureLocked i >> writeInterpProcess (instProcess i) (put m)
    
    74
    -
    
    75
    --- | Expect a value to be received (requires locked interpreter)
    
    76
    -receiveAnyValue :: ExtInterpInstance d -> Get a -> IO a
    
    77
    -receiveAnyValue i get = ensureLocked i >> readInterpProcess (instProcess i) get
    
    78
    -
    
    79
    --- | Wait for a Template Haskell message (requires locked interpreter)
    
    80
    -receiveTHMessage :: ExtInterpInstance d -> IO THMsg
    
    81
    -receiveTHMessage i = ensureLocked i >> receiveAnyValue i getTHMessage
    
    82
    -
    
    83
    --- -----------------------------------------------------------------------------
    
    84
    -
    
    85
    -getLock :: ExtInterpInstance d -> MVar ()
    
    86
    -getLock = interpLock . instProcess
    
    87
    -
    
    88
    -withLock :: ExtInterpInstance d -> IO a -> IO a
    
    89
    -withLock i f = withMVar (getLock i) (const f)
    
    90
    -
    
    91
    -lock :: ExtInterpInstance d -> IO ()
    
    92
    -lock i = takeMVar (getLock i)
    
    93
    -
    
    94
    -unlock :: ExtInterpInstance d -> IO ()
    
    95
    -unlock i = putMVar (getLock i) ()
    
    96
    -
    
    97
    -ensureLocked :: ExtInterpInstance d -> IO ()
    
    98
    -ensureLocked i =
    
    99
    -  isEmptyMVar (getLock i) >>= \case
    
    100
    -    False -> panic "ensureLocked: external interpreter not locked"
    
    101
    -    _     -> pure ()
    
    102
    -
    
    27
    +sendMessage i m = callInterpProcess (instProcess i) m
    
    103 28
     
    
    104 29
     -- | Send a 'Message' and receive the response from the interpreter process
    
    105 30
     callInterpProcess :: Binary a => InterpProcess -> Message a -> IO a
    
    ... ... @@ -107,18 +32,6 @@ callInterpProcess i msg =
    107 32
       remoteCall (interpPipe i) msg
    
    108 33
         `catchException` \(e :: SomeException) -> handleInterpProcessFailure i e
    
    109 34
     
    
    110
    --- | Read a value from the interpreter process
    
    111
    -readInterpProcess :: InterpProcess -> Get a -> IO a
    
    112
    -readInterpProcess i get =
    
    113
    -  readPipe (interpPipe i) get
    
    114
    -    `catchException` \(e :: SomeException) -> handleInterpProcessFailure i e
    
    115
    -
    
    116
    --- | Send a value to the interpreter process
    
    117
    -writeInterpProcess :: InterpProcess -> Put -> IO ()
    
    118
    -writeInterpProcess i put =
    
    119
    -  writePipe (interpPipe i) put
    
    120
    -    `catchException` \(e :: SomeException) -> handleInterpProcessFailure i e
    
    121
    -
    
    122 35
     handleInterpProcessFailure :: InterpProcess -> SomeException -> IO a
    
    123 36
     handleInterpProcessFailure i e = do
    
    124 37
       let hdl = interpHandle i
    

  • compiler/GHC/Runtime/Interpreter/Types.hs
    ... ... @@ -111,7 +111,6 @@ type WasmInterp = ExtInterpState WasmInterpConfig ()
    111 111
     data InterpProcess = InterpProcess
    
    112 112
       { interpPipe   :: !Pipe           -- ^ Pipe to communicate with the server
    
    113 113
       , interpHandle :: !ProcessHandle  -- ^ Process handle of the server
    
    114
    -  , interpLock   :: !(MVar ())      -- ^ Lock to prevent concurrent access to the stream
    
    115 114
       }
    
    116 115
     
    
    117 116
     -- | Status of an external interpreter
    

  • compiler/GHC/Runtime/Interpreter/Wasm.hs
    ... ... @@ -73,14 +73,12 @@ spawnWasmInterp WasmInterpConfig {..} = do
    73 73
       hSetBuffering rh NoBuffering
    
    74 74
       interpPipe <- mkPipeFromHandles rh wh
    
    75 75
       pending_frees <- newMVar []
    
    76
    -  lock <- newMVar ()
    
    77 76
       pure
    
    78 77
         $ ExtInterpInstance
    
    79 78
           { instProcess =
    
    80 79
               InterpProcess
    
    81 80
                 { interpHandle = ph,
    
    82
    -              interpPipe,
    
    83
    -              interpLock = lock
    
    81
    +              interpPipe
    
    84 82
                 },
    
    85 83
             instPendingFrees = pending_frees,
    
    86 84
             instExtra = ()
    

  • libraries/ghci/GHCi/Message.hs
    ... ... @@ -12,7 +12,7 @@
    12 12
     module GHCi.Message
    
    13 13
       ( Message(..), Msg(..)
    
    14 14
       , ConInfoTable(..)
    
    15
    -  , THMessage(..), THMsg(..)
    
    15
    +  , THMessage(..) -- , THMsg(..)
    
    16 16
       , QResult(..)
    
    17 17
       , EvalStatus_(..,EvalBreak), EvalStatus, EvalResult(..), EvalOpts(..), EvalExpr(..)
    
    18 18
       , EvalBreak(..), EvalBreakpoint (..)
    
    ... ... @@ -20,7 +20,7 @@ module GHCi.Message
    20 20
       , toSerializableException, fromSerializableException
    
    21 21
       , THResult(..), THResultType(..)
    
    22 22
       , QState(..)
    
    23
    -  , getMessage, putMessage, getTHMessage, putTHMessage
    
    23
    +  , getMessage, putMessage, {- getTHMessage, -} putTHMessage
    
    24 24
       , Pipe, mkPipeFromHandles, mkPipeFromContinuations, remoteCall, remoteTHCall, readPipe, writePipe
    
    25 25
       , BreakModule
    
    26 26
       , BreakUnitId
    
    ... ... @@ -62,6 +62,8 @@ import Data.Dynamic
    62 62
     import Data.Typeable (TypeRep)
    
    63 63
     import Data.IORef
    
    64 64
     import Data.Map (Map)
    
    65
    +import Data.IntMap (IntMap)
    
    66
    +import qualified Data.IntMap as IntMap
    
    65 67
     import Foreign
    
    66 68
     import GHC.Generics
    
    67 69
     import GHC.Stack.CCS
    
    ... ... @@ -70,6 +72,7 @@ import qualified GHC.Boot.TH.Monad as TH
    70 72
     import System.Exit
    
    71 73
     import System.IO
    
    72 74
     import System.IO.Error
    
    75
    +import Control.Monad
    
    73 76
     
    
    74 77
     -- -----------------------------------------------------------------------------
    
    75 78
     -- The RPC protocol between GHC and the interactive server
    
    ... ... @@ -132,13 +135,13 @@ data Message a where
    132 135
       EvalStmt
    
    133 136
         :: EvalOpts
    
    134 137
         -> EvalExpr HValueRef {- IO [a] -}
    
    135
    -    -> Message (EvalStatus [HValueRef]) {- [a] -}
    
    138
    +    -> Message (RemoteRef ThreadId) {- [a] -}
    
    136 139
     
    
    137 140
       -- | Resume evaluation of a statement after a breakpoint
    
    138 141
       ResumeStmt
    
    139 142
        :: EvalOpts
    
    140 143
        -> RemoteRef ThreadId
    
    141
    -   -> Message (EvalStatus [HValueRef])
    
    144
    +   -> Message ()
    
    142 145
     
    
    143 146
       -- | Abandon evaluation of a statement after a breakpoint
    
    144 147
       AbandonStmt
    
    ... ... @@ -240,12 +243,23 @@ data Message a where
    240 243
       -- | Evaluate something. This is used to support :force in GHCi.
    
    241 244
       Seq
    
    242 245
         :: HValueRef
    
    243
    -    -> Message (EvalStatus ())
    
    246
    +    -> Message (RemoteRef ThreadId)
    
    244 247
     
    
    245 248
       -- | Resume forcing a free variable in a breakpoint (#2950)
    
    246 249
       ResumeSeq
    
    247 250
         :: RemoteRef ThreadId
    
    248
    -    -> Message (EvalStatus ())
    
    251
    +    -> Message ()
    
    252
    +
    
    253
    +  WaitThreadResult
    
    254
    +    :: RemoteRef ThreadId
    
    255
    +    -> Message (EvalStatus [HValueRef])
    
    256
    +
    
    257
    +  WaitAnyThreadBreak
    
    258
    +    :: Message EvalBreak
    
    259
    +
    
    260
    +  WaitThreadResultOrAnyThreadBreak
    
    261
    +    :: RemoteRef ThreadId
    
    262
    +    -> Message (EvalStatus [HValueRef])
    
    249 263
     
    
    250 264
       -- | User-defined request encoded as a tag/payload pair.  This is left
    
    251 265
       -- uninterpreted by GHC and is meant for GHC API applications to be able to supply
    
    ... ... @@ -323,40 +337,40 @@ data THMessage a where
    323 337
     
    
    324 338
     deriving instance Show (THMessage a)
    
    325 339
     
    
    326
    -data THMsg = forall a . (Binary a, Show a) => THMsg (THMessage a)
    
    327
    -
    
    328
    -getTHMessage :: Get THMsg
    
    329
    -getTHMessage = do
    
    330
    -  b <- getWord8
    
    331
    -  case b of
    
    332
    -    0  -> THMsg <$> NewName <$> get
    
    333
    -    1  -> THMsg <$> (Report <$> get <*> get)
    
    334
    -    2  -> THMsg <$> (LookupName <$> get <*> get)
    
    335
    -    3  -> THMsg <$> Reify <$> get
    
    336
    -    4  -> THMsg <$> ReifyFixity <$> get
    
    337
    -    5  -> THMsg <$> (ReifyInstances <$> get <*> get)
    
    338
    -    6  -> THMsg <$> ReifyRoles <$> get
    
    339
    -    7  -> THMsg <$> (ReifyAnnotations <$> get <*> get)
    
    340
    -    8  -> THMsg <$> ReifyModule <$> get
    
    341
    -    9  -> THMsg <$> ReifyConStrictness <$> get
    
    342
    -    10 -> THMsg <$> AddDependentFile <$> get
    
    343
    -    11 -> THMsg <$> AddTempFile <$> get
    
    344
    -    12 -> THMsg <$> AddTopDecls <$> get
    
    345
    -    13 -> THMsg <$> (IsExtEnabled <$> get)
    
    346
    -    14 -> THMsg <$> return ExtsEnabled
    
    347
    -    15 -> THMsg <$> return StartRecover
    
    348
    -    16 -> THMsg <$> EndRecover <$> get
    
    349
    -    17 -> THMsg <$> return FailIfErrs
    
    350
    -    18 -> return (THMsg RunTHDone)
    
    351
    -    19 -> THMsg <$> AddModFinalizer <$> get
    
    352
    -    20 -> THMsg <$> (AddForeignFilePath <$> get <*> get)
    
    353
    -    21 -> THMsg <$> AddCorePlugin <$> get
    
    354
    -    22 -> THMsg <$> ReifyType <$> get
    
    355
    -    23 -> THMsg <$> (PutDoc <$> get <*> get)
    
    356
    -    24 -> THMsg <$> GetDoc <$> get
    
    357
    -    25 -> THMsg <$> return GetPackageRoot
    
    358
    -    26 -> THMsg <$> AddDependentDirectory <$> get
    
    359
    -    n -> error ("getTHMessage: unknown message " ++ show n)
    
    340
    +-- data THMsg = forall a . (Binary a, Show a) => THMsg (THMessage a)
    
    341
    +--
    
    342
    +-- getTHMessage :: Get THMsg
    
    343
    +-- getTHMessage = do
    
    344
    +--   b <- getWord8
    
    345
    +--   case b of
    
    346
    +--     0  -> THMsg <$> NewName <$> get
    
    347
    +--     1  -> THMsg <$> (Report <$> get <*> get)
    
    348
    +--     2  -> THMsg <$> (LookupName <$> get <*> get)
    
    349
    +--     3  -> THMsg <$> Reify <$> get
    
    350
    +--     4  -> THMsg <$> ReifyFixity <$> get
    
    351
    +--     5  -> THMsg <$> (ReifyInstances <$> get <*> get)
    
    352
    +--     6  -> THMsg <$> ReifyRoles <$> get
    
    353
    +--     7  -> THMsg <$> (ReifyAnnotations <$> get <*> get)
    
    354
    +--     8  -> THMsg <$> ReifyModule <$> get
    
    355
    +--     9  -> THMsg <$> ReifyConStrictness <$> get
    
    356
    +--     10 -> THMsg <$> AddDependentFile <$> get
    
    357
    +--     11 -> THMsg <$> AddTempFile <$> get
    
    358
    +--     12 -> THMsg <$> AddTopDecls <$> get
    
    359
    +--     13 -> THMsg <$> (IsExtEnabled <$> get)
    
    360
    +--     14 -> THMsg <$> return ExtsEnabled
    
    361
    +--     15 -> THMsg <$> return StartRecover
    
    362
    +--     16 -> THMsg <$> EndRecover <$> get
    
    363
    +--     17 -> THMsg <$> return FailIfErrs
    
    364
    +--     18 -> return (THMsg RunTHDone)
    
    365
    +--     19 -> THMsg <$> AddModFinalizer <$> get
    
    366
    +--     20 -> THMsg <$> (AddForeignFilePath <$> get <*> get)
    
    367
    +--     21 -> THMsg <$> AddCorePlugin <$> get
    
    368
    +--     22 -> THMsg <$> ReifyType <$> get
    
    369
    +--     23 -> THMsg <$> (PutDoc <$> get <*> get)
    
    370
    +--     24 -> THMsg <$> GetDoc <$> get
    
    371
    +--     25 -> THMsg <$> return GetPackageRoot
    
    372
    +--     26 -> THMsg <$> AddDependentDirectory <$> get
    
    373
    +--     n -> error ("getTHMessage: unknown message " ++ show n)
    
    360 374
     
    
    361 375
     putTHMessage :: THMessage a -> Put
    
    362 376
     putTHMessage m = case m of
    
    ... ... @@ -719,11 +733,27 @@ serializeBCOs rbcos = parMap doChunk (chunkList 100 rbcos)
    719 733
     -- -----------------------------------------------------------------------------
    
    720 734
     -- Reading/writing messages
    
    721 735
     
    
    736
    +type CorrelationId = Int
    
    737
    +data Request = Request !CorrelationId Msg
    
    738
    +data Reply   = Reply   !CorrelationId LB.ByteString
    
    739
    +
    
    740
    +instance Binary Request where
    
    741
    +  put (Request i (Msg m)) = put i >> putMessage m
    
    742
    +  get = Request <$> get <*> getMessage
    
    743
    +
    
    744
    +instance Binary Reply where
    
    745
    +  put (Reply i bs) = put i >> put bs
    
    746
    +  get = Reply <$> get <*> get
    
    747
    +
    
    722 748
     -- | An opaque pipe for bidirectional binary data transmission.
    
    723 749
     data Pipe = Pipe
    
    724
    -  { getSome :: !(IO ByteString)
    
    725
    -  , putAll :: !(B.Builder -> IO ())
    
    726
    -  , pipeLeftovers :: !(IORef (Maybe ByteString))
    
    750
    +  { getSome         :: !(IO ByteString)
    
    751
    +  , putAll          :: !(B.Builder -> IO ())
    
    752
    +  , pipeLeftovers   :: !(IORef (Maybe ByteString))
    
    753
    +  , pipeLock        :: !(MVar ()) -- ^ Lock to prevent concurrent access to the stream
    
    754
    +  , pipePending     :: !(IORef (IntMap (MVar LB.ByteString)))
    
    755
    +  , pipeDrainThread :: !(MVar ThreadId)
    
    756
    +  , pipeNextReq     :: !(IORef Int)
    
    727 757
       }
    
    728 758
     
    
    729 759
     -- | Make a 'Pipe' from a 'Handle' to read and a 'Handle' to write.
    
    ... ... @@ -733,30 +763,59 @@ mkPipeFromHandles pipeRead pipeWrite = do
    733 763
           putAll b = do
    
    734 764
             B.hPutBuilder pipeWrite b
    
    735 765
             hFlush pipeWrite
    
    736
    -  pipeLeftovers <- newIORef Nothing
    
    737
    -  pure $ Pipe { getSome, putAll, pipeLeftovers }
    
    766
    +  mkPipeFromContinuations getSome putAll
    
    738 767
     
    
    739 768
     -- | Make a 'Pipe' from a reader function and a writer function.
    
    740 769
     mkPipeFromContinuations :: IO ByteString -> (B.Builder -> IO ()) -> IO Pipe
    
    741 770
     mkPipeFromContinuations getSome putAll = do
    
    742
    -  pipeLeftovers <- newIORef Nothing
    
    743
    -  pure $ Pipe { getSome, putAll, pipeLeftovers }
    
    771
    +  pipeLeftovers   <- newIORef Nothing
    
    772
    +  pipeLock        <- newMVar ()
    
    773
    +  pipePending     <- newIORef IntMap.empty
    
    774
    +  pipeDrainThread <- newEmptyMVar
    
    775
    +  pipeNextReq     <- newIORef 0
    
    776
    +  let p = Pipe { .. }
    
    777
    +
    
    778
    +  -- Fork thread to drain read end.
    
    779
    +  drainTid <- forkIO (drainPipe p)
    
    780
    +  putMVar pipeDrainThread drainTid
    
    781
    +  -- todo: proper thread clean up and no leaks
    
    744 782
     
    
    745
    -remoteCall :: Binary a => Pipe -> Message a -> IO a
    
    783
    +  pure p
    
    784
    +
    
    785
    +remoteCall :: (Show a, Binary a) => Pipe -> Message a -> IO a
    
    746 786
     remoteCall pipe msg = do
    
    747
    -  writePipe pipe (putMessage msg)
    
    748
    -  readPipe pipe get
    
    749 787
     
    
    750
    -writePipe :: Pipe -> Put -> IO ()
    
    751
    -writePipe Pipe{..} put = putAll $ execPut put
    
    788
    +  -- Mark pending
    
    789
    +  uq <- freshReqId pipe
    
    790
    +  wv <- newEmptyMVar
    
    791
    +  atomicModifyIORef' (pipePending pipe) $
    
    792
    +    \m -> (IntMap.insert uq wv m, ())
    
    793
    +
    
    794
    +  -- Write request
    
    795
    +  writePipe pipe (put (Request uq (Msg msg)))
    
    796
    +
    
    797
    +  -- Block waiting for reply
    
    798
    +  runGet get <$> takeMVar wv
    
    752 799
     
    
    753 800
     remoteTHCall :: Binary a => Pipe -> THMessage a -> IO a
    
    754 801
     remoteTHCall pipe msg = do
    
    755 802
       writePipe pipe (putTHMessage msg)
    
    756 803
       readPipe pipe get
    
    757 804
     
    
    805
    +drainPipe :: Pipe -> IO ()
    
    806
    +drainPipe p = forever $ do
    
    807
    +  Reply uq bs <- readPipe p get
    
    808
    +  wv <- atomicModifyIORef' (pipePending p) $ \m -> do
    
    809
    +    case IntMap.lookup uq m of
    
    810
    +      Nothing -> error "drainPipe: Received reply to no pending request"
    
    811
    +      Just wv -> (IntMap.delete uq m, wv)
    
    812
    +  putMVar wv bs -- signal matching request
    
    813
    +
    
    814
    +writePipe :: Pipe -> Put -> IO ()
    
    815
    +writePipe p@Pipe{..} put = withLock p $ putAll $ execPut put
    
    816
    +
    
    758 817
     readPipe :: Pipe -> Get a -> IO a
    
    759
    -readPipe Pipe{..} get = do
    
    818
    +readPipe p@Pipe{..} get = withLock p $ do
    
    760 819
       leftovers <- readIORef pipeLeftovers
    
    761 820
       m <- getBin getSome get leftovers
    
    762 821
       case m of
    
    ... ... @@ -786,3 +845,9 @@ getBin getsome get leftover = go leftover (runGetIncremental get)
    786 845
             else go Nothing (fun (Just b))
    
    787 846
        go _lft (Fail _rest _off str) =
    
    788 847
          throwIO (ErrorCall ("getBin: " ++ str))
    
    848
    +
    
    849
    +withLock :: Pipe -> IO c -> IO c
    
    850
    +withLock Pipe{..} = bracket (takeMVar pipeLock) (putMVar pipeLock) . const
    
    851
    +
    
    852
    +freshReqId :: Pipe -> IO Int
    
    853
    +freshReqId Pipe{pipeNextReq} = atomicModifyIORef' pipeNextReq $ \i -> (i+1, i)

  • libraries/ghci/GHCi/Run.hs
    ... ... @@ -128,6 +128,15 @@ run m = case m of
    128 128
       Seq ref -> doSeq ref
    
    129 129
       ResumeSeq ref -> resumeSeq ref
    
    130 130
     
    
    131
    +  WaitThreadResult tid -> localRef tid >>= atomically . readThreadEvalStatus
    
    132
    +  WaitAnyThreadBreak   -> atomically $ readAnyThreadEvalBreak
    
    133
    +  WaitThreadResultOrAnyThreadBreak t -> do
    
    134
    +    tid <- localRef t
    
    135
    +    atomically $
    
    136
    +      readThreadEvalStatus tid
    
    137
    +        `orElse`
    
    138
    +      (EvalPaused <$> readAnyThreadEvalBreak)
    
    139
    +
    
    131 140
       Shutdown            -> unexpectedMessage m
    
    132 141
       RunTH {}            -> unexpectedMessage m
    
    133 142
       RunModFinalizers {} -> unexpectedMessage m
    
    ... ... @@ -206,7 +215,7 @@ tryEval io = do
    206 215
     
    
    207 216
     --------------------------------------------------------------------------------
    
    208 217
     
    
    209
    -evalStmt :: EvalOpts -> EvalExpr HValueRef -> IO (EvalStatus [HValueRef])
    
    218
    +evalStmt :: EvalOpts -> EvalExpr HValueRef -> IO (RemoteRef ThreadId)
    
    210 219
     evalStmt opts expr = do
    
    211 220
       io <- mkIO expr
    
    212 221
       sandboxIO opts $ do
    
    ... ... @@ -248,16 +257,16 @@ evalStringToString r str = do
    248 257
     -- The UI process has more and therefore also can show more
    
    249 258
     -- information about the breakpoint than the current iserv
    
    250 259
     -- process.
    
    251
    -doSeq :: RemoteRef a -> IO (EvalStatus ())
    
    252
    -doSeq ref = clearEvalStatus <$> do
    
    260
    +doSeq :: RemoteRef a -> IO (RemoteRef ThreadId)
    
    261
    +doSeq ref = do
    
    253 262
         sandboxIO evalOptsSeq $ do
    
    254 263
           _ <- (void $ evaluate =<< localRef ref)
    
    255 264
           return []
    
    256 265
     
    
    257 266
     -- | Process a ResumeSeq message. Continue the :force processing     #2950
    
    258 267
     -- after a breakpoint.
    
    259
    -resumeSeq :: RemoteRef ThreadId -> IO (EvalStatus ())
    
    260
    -resumeSeq hvref = clearEvalStatus <$> resumeStmt evalOptsSeq hvref
    
    268
    +resumeSeq :: RemoteRef ThreadId -> IO ()
    
    269
    +resumeSeq hvref = resumeStmt evalOptsSeq hvref
    
    261 270
     
    
    262 271
     evalOptsSeq :: EvalOpts
    
    263 272
     evalOptsSeq = EvalOpts
    
    ... ... @@ -296,15 +305,16 @@ abandonStmt hvref = do
    296 305
     
    
    297 306
     --------------------------------------------------------------------------------
    
    298 307
     
    
    299
    -resumeStmt :: EvalOpts -> RemoteRef ThreadId -> IO (EvalStatus [HValueRef])
    
    308
    +resumeStmt :: EvalOpts -> RemoteRef ThreadId -> IO ()
    
    300 309
     resumeStmt opts rtid = do
    
    301 310
       resumeThreadId <- localRef rtid
    
    302 311
       ResumeContext{..} <- getThreadResumeContext resumeThreadId
    
    303
    -  withBreakAction opts resumeThreadId $ \waitForResult ->
    
    304
    -    mask_ $ do
    
    305
    -      putMVar resumeBreakMVar () -- this awakens the stopped thread...
    
    306
    -      redirectInterrupts resumeThreadId $
    
    307
    -        waitForResult
    
    312
    +  setBreakAction opts resumeThreadId -- See Note [TODO: all break options must be set every time]
    
    313
    +  -- withBreakAction opts resumeThreadId $ \waitForResult ->
    
    314
    +    -- mask_ $ do
    
    315
    +  putMVar resumeBreakMVar () -- this awakens the stopped thread...
    
    316
    +      -- redirectInterrupts resumeThreadId $
    
    317
    +      --   waitForResult
    
    308 318
     
    
    309 319
     -- When running a computation, we redirect ^C exceptions to the running
    
    310 320
     -- thread.  ToDo: we might want a way to continue even if the target
    
    ... ... @@ -316,7 +326,7 @@ resumeStmt opts rtid = do
    316 326
     -- only while we execute the user's code.  We can't afford to lose the final
    
    317 327
     -- putMVar, otherwise deadlock ensues. (#1583, #1922, #1946)
    
    318 328
     
    
    319
    -sandboxIO :: EvalOpts -> IO [HValueRef] -> IO (EvalStatus [HValueRef])
    
    329
    +sandboxIO :: EvalOpts -> IO [HValueRef] -> IO (RemoteRef ThreadId)
    
    320 330
     sandboxIO opts io
    
    321 331
         | useSandboxThread opts
    
    322 332
         = do
    
    ... ... @@ -336,17 +346,31 @@ sandboxIO opts io
    336 346
             tid <- forkIO $ do
    
    337 347
     
    
    338 348
               takeMVar wait -- don't run anything in this thread before
    
    339
    -                        -- `withBreakAction` has a chance to set up.
    
    349
    +                        -- `setBreakAction` has a chance to set up.
    
    340 350
     
    
    341 351
               tid <- myThreadId
    
    342 352
               labelThread tid "GHCi sandbox"
    
    353
    +
    
    354
    +            -- WHEN THE THREAD IS FINISHED, write the value itself.
    
    355
    +            --
    
    356
    +            -- we only write the TID status map in two places:
    
    357
    +            --
    
    358
    +            -- When the "main" thread finishes (here)
    
    359
    +            -- When any thread hits a breakpoint (global break action)
    
    343 360
               unsafeUnmask runIt >>= writeThreadEvalStatus tid
    
    361
    +            -- TODO: This unsafe-unmask is very dubious. Consult with Andrea. I
    
    362
    +            -- don't think we should be uninterruptable masking interp commands
    
    363
    +            -- somewhere else. This should just be runIt?
    
    344 364
     
    
    345
    -        -- We are running in uninterruptibleMask
    
    346
    -        withBreakAction opts tid $ \waitForResult -> do
    
    347
    -          putMVar wait ()
    
    348
    -          redirectInterrupts tid $ unsafeUnmask $
    
    349
    -            waitForResult
    
    365
    +        -- We are running in uninterruptibleMask -- TODO WHY??
    
    366
    +        setBreakAction opts tid
    
    367
    +        putMVar wait ()
    
    368
    +
    
    369
    +        mkRemoteRef tid
    
    370
    +        -- withBreakAction opts tid $ \waitForResult -> do
    
    371
    +        --   putMVar wait ()
    
    372
    +        --   redirectInterrupts tid $ unsafeUnmask $
    
    373
    +        --     waitForResult
    
    350 374
     
    
    351 375
         | otherwise
    
    352 376
         = do
    
    ... ... @@ -360,7 +384,17 @@ sandboxIO opts io
    360 384
           -- tracing, etc.) need the expression to be running in a
    
    361 385
           -- separate thread, so debugging is only enabled when
    
    362 386
           -- using the sandbox.
    
    363
    -      runIt
    
    387
    +      tid <- forkIO (pure ())
    
    388
    +      runIt >>= writeThreadEvalStatus tid
    
    389
    +      mkRemoteRef tid
    
    390
    +        -- We `runIt` on the main thread, but fork a new thread to store to the
    
    391
    +        -- result with `writeThreadEvalStatus` to fit the asynchronous API. We
    
    392
    +        -- can't use myThreadId because if the user happens to runIt twice
    
    393
    +        -- before awaiting, the second result will clobber the first.
    
    394
    +
    
    395
    +        -- TODO We have to be careful evacuating this map bc there may be
    
    396
    +        -- uncollected results from finished threads. So the thread map can't
    
    397
    +        -- be weak refs ? Or maybe because of the RemoteRef it's fine. Ah, prob.
    
    364 398
       where
    
    365 399
         runIt = measureAlloc $ tryEval $ rethrow opts $ clearCCS io
    
    366 400
     
    
    ... ... @@ -415,12 +449,16 @@ sandboxIO opts io
    415 449
     -- whenever "main" or one of the threads spawned by "main" hit a breakpoint,
    
    416 450
     -- rather than just "main" being observed. This should be used for the main
    
    417 451
     -- debuggee execution of a debugger.
    
    418
    -withBreakAction :: EvalOpts -> ThreadId -> (IO (EvalStatus [HValueRef]) -> IO a) -> IO a
    
    419
    -withBreakAction opts tid@(ThreadId tid#) act = do
    
    452
    +-- withBreakAction :: EvalOpts -> ThreadId -> (IO (EvalStatus [HValueRef]) -> IO a) -> IO a
    
    453
    +-- withBreakAction opts tid@(ThreadId tid#) act = do
    
    454
    +
    
    455
    +setBreakAction :: EvalOpts -> ThreadId -> IO ()
    
    456
    +setBreakAction opts tid@(ThreadId tid#) = do
    
    420 457
       ctx <- getThreadResumeContext tid
    
    421
    -  bracket (setBreakAction ctx) (resetBreakAction ctx) (\_ -> act (waitForResult ctx))
    
    458
    +  setBreakAction' ctx
    
    459
    +    -- bracket _ (resetBreakAction ctx) (\_ -> act (waitForResult ctx))
    
    422 460
       where
    
    423
    -    setBreakAction ctx = do
    
    461
    +    setBreakAction' ctx = do
    
    424 462
           -- TODO: TERRIBLY NOT MULTI-THREAD FRIENDLY. Each thread needs its own RTS flag exceptionFlag, like we have for step-in or step-out.
    
    425 463
           runIf breakOnException $ \_ -> poke exceptionFlag 1
    
    426 464
             -- Breaking on exceptions is not enabled by default, since it
    
    ... ... @@ -433,7 +471,7 @@ withBreakAction opts tid@(ThreadId tid#) act = do
    433 471
           runIfCtx ctx isolateThreadBreaks setIsolatedCtx
    
    434 472
     
    
    435 473
         resetBreakAction ctx () = do
    
    436
    -      poke exceptionFlag 0
    
    474
    +      poke exceptionFlag 0 -- just wrong, expection flag should be per-thread
    
    437 475
           rts_disableStopAfterReturn    tid#
    
    438 476
           rts_disableStopNextBreakpoint tid#
    
    439 477
           runIfCtx ctx isolateThreadBreaks unsetIsolatedCtx
    
    ... ... @@ -485,9 +523,3 @@ redirectInterrupts target wait = do
    485 523
            Nothing -> wait
    
    486 524
            Just target -> do throwTo target (e :: SomeException); wait
    
    487 525
     
    488
    -clearEvalStatus :: EvalStatus a -> EvalStatus ()
    
    489
    -clearEvalStatus = \case
    
    490
    -  EvalComplete w (EvalException se) -> EvalComplete w (EvalException se)
    
    491
    -  EvalComplete w (EvalSuccess _)    -> EvalComplete w (EvalSuccess ())
    
    492
    -  EvalBreak ap mb rt rccs           -> EvalBreak ap mb rt rccs
    
    493
    -

  • libraries/ghci/GHCi/Server.hs
    ... ... @@ -70,6 +70,14 @@ servWithCustom verbose hook pipe restore customHandler = loop
    70 70
     
    
    71 71
         when verbose $ trace ("msg: " ++ (show msg))
    
    72 72
         case msg of
    
    73
    +      -- The expressions which block waiting for a result musn't block the
    
    74
    +      -- server thread, so send them off and notify over the wire when done.
    
    75
    +      --
    
    76
    +      -- The other end (see 'remoteCall') will be processing notifications, and
    
    77
    +      -- it will use the returned pointer as the index for the matching notification.
    
    78
    +      WaitThreadResult                 {} -> forkIO (run msg >>= (notification =<< myThreadId)) >>= reply . mkRemoteRef
    
    79
    +      WaitAnyThreadBreak               {} -> forkIO (run msg >>= (notification =<< myThreadId)) >>= reply . mkRemoteRef
    
    80
    +      WaitThreadResultOrAnyThreadBreak {} -> forkIO (run msg >>= (notification =<< myThreadId)) >>= reply . mkRemoteRef
    
    73 81
           CustomMessage tag payload -> handleCustom tag payload
    
    74 82
           Shutdown -> return ()
    
    75 83
           RunTH st q ty loc -> wrapRunTH $ runTH pipe st q ty loc