Rodrigo Mesquita pushed to branch wip/romes/multi-threaded-debugger at Glasgow Haskell Compiler / GHC Commits: 422ec8e5 by Rodrigo Mesquita at 2026-10-23T14:41:27+02:00 test: make T3171 not fragile and add version for ext interp too - actually test the sleep of the user-given expression gets interrupted - the ext interp version tests two things: - Requests can be cancelled without terminating the ext interp (#18852) - The stmt being evaluated gets interrupted in the ext interp (that is, ^C gets sent to the thread running the user-given expression and doesn't affect the ext interp main thread) - testing ext interp requires sending ^C to whole process group Fixes #15383 - - - - - ea1dd1c7 by Andrea Vezzosi at 2026-10-26T09:50:00+01:00 interp: cancel requests if async exceptions while waiting for reply. - - - - - 05f68d0e by Andrea Vezzosi at 2026-10-26T18:21:34+01:00 interp: more uninterruptibleMask_ removed - - - - - 7e21b3f8 by Andrea Vezzosi at 2026-10-26T23:42:27+01:00 interp: do not terminate ext. interp. process on exception `sendMessage` throwing an exception does not warrant terminating the external interpreter process anymore, because communication can keep going no matter what. `stopInterp` now resets the whole `Interp` state, so that a new ExtInterpInstance can be later created and work correctly. - - - - - 1c9fe4ea by Andrea Vezzosi at 2026-10-27T08:54:09+01:00 fixup! interp: spawn fillPipe thread, writePipe now safely interruptible - - - - - 8dbf2fd4 by Andrea Vezzosi at 2026-10-27T08:58:54+01:00 interp: withExtInterp calls stopInterp on PipeClosedException - - - - - 5be020df by Andrea Vezzosi at 2026-10-27T09:05:16+01:00 fixup! interp: cancel requests if async exceptions while waiting for reply. - - - - - 081d2af7 by Rodrigo Mesquita at 2026-10-27T10:33:45+00:00 Global break action which uses shared thread map the status Var must actually be global across all threads, otherwise all of them will block trying to write some Status Var no one is looking at at all have to fix these toggle untoggle things very satisfying solution with mutable state Matthew-certified big brain WIP HMmm almost there I think pretty great progressssssssss ExecOptions isolateThreadBreaks ThreadBreaksIsolationMode move to GHCi.Run.Breakpoints - - - - - fd092d1b by Rodrigo Mesquita at 2026-10-27T10:33:46+00:00 Refactor: we have the interactive Ids directly... clean up significantly Removes things like names <- map idName ids map (\i -> let id = find (\x -> i==idName x) names) idso and is more straightforward because there's less state going around (no more updating HscEnv as an output) It also kills `printTypeOfNames` completely, since now we just have the Ids. No longer `lookupName` names to get back the ids... ``` printTypeOfName :: GHC.GhcMonad m => Name -> m () printTypeOfName n = do maybe_tything <- GHC.lookupName n case maybe_tything of Nothing -> return () Just thing -> printTyThing thing ``` now is just `printTyThing . AnId` Also: ```diff - tythings <- catMaybes `liftM` mapM GHC.lookupName namesSorted - docs <- mapM pprTypeAndContents [i | AnId i <- tythings] + docs <- mapM pprTypeAndContents idsSorted ``` (and yes, in ExecBreaks there are only Ids, no TyThings, bc they are only constructed by the interpreter execution which can only bind ids. for TyThings use runDecls which doesn't return execBreaks.) I have a plan for the `resumes`, I'm getting there. fixes grrr UNDO the removing-hsc-env-globality part of the clean up and keep just the good parts fixes - - - - - 30e63bc7 by Rodrigo Mesquita at 2026-10-27T10:33:46+00:00 Make the ExecBreak contain a Resume Put the resume in ExecBreak - - - - - 6e080b8b by Rodrigo Mesquita at 2026-10-27T10:33:46+00:00 Make resumeExec receive a Resume as an argument - - - - - f3ef734d by Rodrigo Mesquita at 2026-10-27T10:33:46+00:00 Build EvalOpts once per exec rather than per breakpoint stop in interpreter_steplocal the allocations increased because now we're updating `eval_opts` after constructing them in `handleRunStatus` (which is run many times since in step-local most often we don't stop, just check). So drop the update to the eval_opts, and, in fact, pull their construction out. the loop always uses the same eval_opts, so no point in constructing them inside from scratch every time. -3.6% from this branch which had the regressions in interpreter_steplocal - - - - - aac8c29c by Rodrigo Mesquita at 2026-10-27T10:33:46+00:00 Look up a thread's ResumeContext without taking a lock modifyMVar is unnecessarily expensive and blocking when most often we just care about reading without modifying. optimize for default case (thread context already exists). It is called every single time a breakpoint is hit (regardless of whether we stop there, e.g. in interpreter_steplocal we do not stop there often) -3.3% improvement over original regression - - - - - 42e3e95b by Rodrigo Mesquita at 2026-10-27T10:33:46+00:00 Every thread resume-context keeps a var for its EvalStatus Kill the global mapping from thread id to EvalStatus Instead, keep the EvalStatus in the ResumeContext associated to a thread in the threadContextVar (where entries are created on breakpoints being hit and when writing an EvalComplete result) Before, every breakpoint hit meant reading the global map TVar and deleting a map entry which did a lot of allocations. Now, we have a Nothing variable which we overwrite to "take" rather than deleting from a Map and allocating the GlobalDbgMap too. Note that for every breakpoint hit we'd have to insert the ThreadId entry in the map again (because we deleted it last time we resumed), again allocating unnecessarily. Now it's only a look up on the map and an update on the variable directly. And a LOT of breakpoints were hit on interpreter_steplocal, stress testing the cost of hitting a breakpoint The isolated threads move to their own top-level TVar Set. -4.4% improvement over the regression - - - - - 0e9d8b01 by Rodrigo Mesquita at 2026-10-27T10:33:46+00:00 Move whether a thread is isolated to the same global map Stops us from reconstructing the set every time this changed instead to being a per thread variable just toggled on/off, by default non-isolated like before (before it was Set.empty, Set.insert for True, Set.delete for FALSE). This is more straightforward -- there's always an isolation mode per thread and we can toggle it directly reduces slightly allocations, but the main advantage is killing a second global map and making it more uniform with just one thread map and no membership check for toggledness. - - - - - c97217d0 by Rodrigo Mesquita at 2026-10-27T10:33:46+00:00 Prefer foldl' in readAnyThreadEvalBreak txn retry 'orElse' next is just next, so we might as well use a strict fold - - - - - adaf801f by Rodrigo Mesquita at 2026-10-27T10:33:46+00:00 Add inline TODO about improving efficiency of readyAnyThreadBreak It is currently O(N) in number of threads that have hit breakpoints It should be number of *live* threads that have hit breaks, bc we still need to find a way to evacuate threads from the map - - - - - 11a6c0cb by Rodrigo Mesquita at 2026-10-27T10:33:46+00:00 Set global breakpoint action in sandboxIO rather than withBreakAction - - - - - ae43af89 by Rodrigo Mesquita at 2026-10-27T10:33:47+00:00 TODO comment on breakOnException being global rather than per-thread - - - - - 377809cb by Rodrigo Mesquita at 2026-10-27T10:33:47+00:00 fixmeup - - - - - 1c0801f5 by Rodrigo Mesquita at 2026-10-27T10:34:08+00:00 WIP: Asynchronous eval/resume - - - - - 9173debe by Rodrigo Mesquita at 2026-10-27T10:34:10+00:00 Fix commentary - - - - - 1add71e6 by Rodrigo Mesquita at 2026-10-27T10:34:10+00:00 fix - - - - - 10b933a8 by Rodrigo Mesquita at 2026-10-27T10:34:10+00:00 more fixes - - - - - d6957a49 by Rodrigo Mesquita at 2026-10-27T10:34:25+00:00 fixes api use, kill uninterruptible mask - - - - - ce28d80c by Rodrigo Mesquita at 2026-10-27T10:34:27+00:00 Kill redundant custom handler in GHCi.Server - - - - - fa310706 by Rodrigo Mesquita at 2026-10-27T10:34:27+00:00 Revert "Kill redundant custom handler in GHCi.Server" This reverts commit a64e1dbdc9f7a5d46a33c3bbafa69ffa49aee1ec. - - - - - 1b4812d9 by Rodrigo Mesquita at 2026-10-27T10:34:27+00:00 clean up some comments in GHCi.Signals - - - - - 091cc0da by Rodrigo Mesquita at 2026-10-27T10:34:27+00:00 TSO_STOP_ON_EXCEPTION -- per-thread break-on-exception - - - - - 22ae37c5 by Rodrigo Mesquita at 2026-10-27T10:34:27+00:00 Add redirectInterrupts again - - - - - 4b02fa0e by Rodrigo Mesquita at 2026-10-27T10:34:27+00:00 unmask runIt only - - - - - f99eaaf8 by Rodrigo Mesquita at 2026-10-27T10:34:27+00:00 Revert "Add redirectInterrupts again" This reverts commit 564c64781849dd3e681b68ae6b167779403118a6. - - - - - 6c6486ea by Rodrigo Mesquita at 2026-10-27T10:34:27+00:00 Move cmmtn - - - - - 518c3554 by Rodrigo Mesquita at 2026-10-27T10:34:27+00:00 internal interp: redirect interrupts on waits to the thread we're waiting for - - - - - 0eec43cc by Rodrigo Mesquita at 2026-10-27T10:34:27+00:00 Do redirectInterrupts to ext interp too - - - - - 8e4cea00 by Rodrigo Mesquita at 2026-10-27T10:34:41+00:00 get rid of installSignalHandlers and discardCtrlC and uninterruptibleMask - - - - - c81f4571 by Rodrigo Mesquita at 2026-10-27T10:34:41+00:00 fixes - - - - - aec7d6c1 by Rodrigo Mesquita at 2026-10-27T10:34:42+00:00 Manage thread status stopped vs running and allow many threads to read the same result off of a stoped thread - - - - - e5100d77 by Rodrigo Mesquita at 2026-10-27T10:34:42+00:00 abstracrt resumeStoppedThread and use it in abandonStmt too. - - - - - 6f99c52e by Rodrigo Mesquita at 2026-10-27T10:40:31+00:00 fix redirect todo on seq/resumeseq - - - - - 35 changed files: - compiler/GHC.hs - compiler/GHC/Driver/Config.hs - compiler/GHC/Runtime/Debugger.hs - compiler/GHC/Runtime/Eval.hs - compiler/GHC/Runtime/Eval/Types.hs - compiler/GHC/Runtime/Heap/Inspect.hs - compiler/GHC/Runtime/Interpreter.hs - compiler/GHC/Runtime/Interpreter/Process.hs - compiler/GHC/Tc/Gen/Splice.hs - ghc/GHCi/UI.hs - ghc/GHCi/UI/Monad.hs - libraries/ghc-heap/GHC/Exts/Heap/FFIClosures_ProfilingDisabled.hsc - libraries/ghc-heap/GHC/Exts/Heap/FFIClosures_ProfilingEnabled.hsc - libraries/ghc-heap/tests/parse_tso_flags.hs - libraries/ghc-internal/src/GHC/Internal/Heap/Closures.hs - libraries/ghci/GHCi/Debugger.hs - libraries/ghci/GHCi/Message.hs - libraries/ghci/GHCi/Message/Async.hs - libraries/ghci/GHCi/Run.hs - + libraries/ghci/GHCi/Run/Breakpoints.hs - libraries/ghci/GHCi/Server.hs - libraries/ghci/GHCi/Server/Threads.hs - libraries/ghci/GHCi/Signals.hs - libraries/ghci/ghci.cabal.in - rts/Exception.cmm - rts/Interpreter.c - rts/RtsSymbols.c - rts/include/rts/Constants.h - testsuite/tests/ghc-api/T8628.hs - testsuite/tests/ghc-api/T8639_api.hs - testsuite/tests/ghc-api/apirecomp001/myghc.hs - testsuite/tests/ghci/should_run/Makefile - testsuite/tests/ghci/should_run/T3171.stdout - + testsuite/tests/ghci/should_run/T3171ext.stdout - testsuite/tests/ghci/should_run/all.T The diff was not included because it is too large. View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/4be7bc824442286f1c25394e23d73c4... -- View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/4be7bc824442286f1c25394e23d73c4... You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help