Haskell.org
Sign In Sign Up
Manage this list Sign In Sign Up

Keyboard Shortcuts

Thread View

  • j: Next unread message
  • k: Previous unread message
  • j a: Jump to all threads
  • j l: Jump to MailingList overview

ghc-commits

Thread Start a new thread
Download
Threads by month
  • ----- 2026 -----
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2025 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
ghc-commits@haskell.org

  • 1 participants
  • 10690 discussions
[Git][ghc/ghc][wip/T26875] ghc-internal: avoid depending on GHC.Internal.Control.Monad.Fix
by Teo Camarasu (@teo) 19 Feb '26

19 Feb '26
Teo Camarasu pushed to branch wip/T26875 at Glasgow Haskell Compiler / GHC Commits: af32ff38 by Teo Camarasu at 2026-02-19T10:32:42+00:00 ghc-internal: avoid depending on GHC.Internal.Control.Monad.Fix This module contains the definition of MonadFix, since we want an instance for IO, that instance requires a lot of machinery and we want to avoid an orphan instance, this will naturally be quite high up in the dependency graph. So we want to avoid other modules depending on it as far as possible. Resolves #26875 We get some metric increases on Windows, but get mild improvements everywhere else. Metric Increase: T10858 T13056 - - - - - 14 changed files: - libraries/base/src/System/IO.hs - libraries/ghc-internal/src/GHC/Internal/Control/Monad/Fix.hs - libraries/ghc-internal/src/GHC/Internal/Control/Monad/ST/Lazy/Imp.hs - libraries/ghc-internal/src/GHC/Internal/Data/Functor/Identity.hs - libraries/ghc-internal/src/GHC/Internal/System/IO.hs - libraries/ghc-internal/src/GHC/Internal/TH/Monad.hs - testsuite/tests/ghci/scripts/ListTuplePunsPpr.stdout - testsuite/tests/ghci/scripts/T4175.stdout - testsuite/tests/interface-stability/base-exports.stdout - testsuite/tests/interface-stability/base-exports.stdout-javascript-unknown-ghcjs - testsuite/tests/interface-stability/base-exports.stdout-mingw32 - testsuite/tests/interface-stability/base-exports.stdout-ws-32 - testsuite/tests/interface-stability/template-haskell-exports.stdout - testsuite/tests/mdo/should_fail/mdofail006.stderr Changes: ===================================== libraries/base/src/System/IO.hs ===================================== @@ -185,6 +185,7 @@ module System.IO ) where import GHC.Internal.System.IO +import GHC.Internal.Control.Monad.Fix (fixIO) -- $locking -- Implementations should enforce as far as possible, at least locally to the ===================================== libraries/ghc-internal/src/GHC/Internal/Control/Monad/Fix.hs ===================================== @@ -24,7 +24,8 @@ module GHC.Internal.Control.Monad.Fix ( MonadFix(mfix), - fix + fix, + fixIO, ) where import GHC.Internal.Data.Either @@ -35,11 +36,17 @@ import GHC.Internal.Data.Monoid ( Monoid, Dual(..), Sum(..), Product(..) import GHC.Internal.Data.NonEmpty ( NonEmpty(..) ) import GHC.Internal.Data.Ord ( Down(..) ) import GHC.Internal.Data.Tuple ( Solo(..), snd ) -import GHC.Internal.Base ( Monad, errorWithoutStackTrace, (.) ) +import GHC.Internal.Base ( IO, Monad, errorWithoutStackTrace, (.), return, liftM ) import GHC.Internal.Generics import GHC.Internal.List ( head, drop ) import GHC.Internal.Control.Monad.ST.Imp -import GHC.Internal.System.IO +import qualified GHC.Internal.Control.Monad.ST.Lazy.Imp as Lazy +import GHC.Internal.Data.Functor.Identity (Identity(..)) +import GHC.Internal.MVar +import GHC.Internal.IO.Unsafe +import GHC.Internal.IO.Exception +import GHC.Internal.TH.Monad +import GHC.Internal.Control.Exception -- | Monads having fixed points with a \'knot-tying\' semantics. -- Instances of 'MonadFix' should satisfy the following laws: @@ -102,6 +109,86 @@ instance MonadFix NonEmpty where instance MonadFix IO where mfix = fixIO +-- --------------------------------------------------------------------------- +-- fixIO + +-- | The implementation of 'Control.Monad.Fix.mfix' for 'IO'. +-- +-- This operation may fail with: +-- +-- * 'FixIOException' if the function passed to 'fixIO' inspects its argument. +-- +-- ==== __Examples__ +-- +-- the IO-action is only executed once. The recursion is only on the values. +-- +-- >>> take 3 <$> fixIO (\x -> putStr ":D" >> (:x) <$> readLn @Int) +-- :D +-- 2 +-- [2,2,2] +-- +-- If we are strict in the value, just as with 'Data.Function.fix', we do not get termination: +-- +-- >>> fixIO (\x -> putStr x >> pure ('x' : x)) +-- * hangs forever * +-- +-- We can tie the knot of a structure within 'IO' using 'fixIO': +-- +-- @ +-- data Node = MkNode Int (IORef Node) +-- +-- foo :: IO () +-- foo = do +-- p \<- fixIO (\p -> newIORef (MkNode 0 p)) +-- q <- output p +-- r <- output q +-- _ <- output r +-- pure () +-- +-- output :: IORef Node -> IO (IORef Node) +-- output ref = do +-- MkNode x p <- readIORef ref +-- print x +-- pure p +-- @ +-- +-- >>> foo +-- 0 +-- 0 +-- 0 +fixIO :: (a -> IO a) -> IO a +fixIO k = do + m <- newEmptyMVar + ans <- unsafeDupableInterleaveIO + (readMVar m `catch` \BlockedIndefinitelyOnMVar -> + throwIO FixIOException) + result <- k ans + putMVar m result + return result + +-- Note [Blackholing in fixIO] +-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +-- We do our own explicit black holing here, because GHC's lazy +-- blackholing isn't enough. In an infinite loop, GHC may run the IO +-- computation a few times before it notices the loop, which is wrong. +-- +-- NOTE2: the explicit black-holing with an IORef ran into trouble +-- with multiple threads (see #5421), so now we use an MVar. We used +-- to use takeMVar with unsafeInterleaveIO. This, however, uses noDuplicate#, +-- which is not particularly cheap. Better to use readMVar, which can be +-- performed in multiple threads safely, and to use unsafeDupableInterleaveIO +-- to avoid the noDuplicate cost. +-- +-- What we'd ideally want is probably an IVar, but we don't quite have those. +-- STM TVars look like an option at first, but I don't think they are: +-- we'd need to be able to write to the variable in an IO context, which can +-- only be done using 'atomically', and 'atomically' is not allowed within +-- unsafePerformIO. We can't know if someone will try to use the result +-- of fixIO with unsafePerformIO! +-- +-- See also System.IO.Unsafe.unsafeFixIO. +-- + -- | @since base-2.01 instance MonadFix ((->) r) where mfix f = \ r -> let a = f a r in a @@ -116,6 +203,10 @@ instance MonadFix (Either e) where instance MonadFix (ST s) where mfix = fixST +-- | @since base-2.01 +instance MonadFix (Lazy.ST s) where + mfix = Lazy.fixST + -- Instances of Data.Monoid wrappers -- | @since base-4.8.0.0 @@ -171,3 +262,24 @@ instance (MonadFix f, MonadFix g) => MonadFix (f :*: g) where -- | @since base-4.12.0.0 instance MonadFix Down where mfix f = Down (fix (getDown . f)) + + +-- | @since base-4.8.0.0 +instance MonadFix Identity where + mfix f = Identity (fix (runIdentity . f)) + +-- | If the function passed to 'mfix' inspects its argument, +-- the resulting action will throw a 'FixIOException'. +-- +-- @since 2.17.0.0 +instance MonadFix Q where + -- We use the same blackholing approach as in fixIO. + -- See Note [Blackholing in fixIO]. + mfix k = do + m <- runIO newEmptyMVar + ans <- runIO (unsafeDupableInterleaveIO + (readMVar m `catch` \BlockedIndefinitelyOnMVar -> + throwIO FixIOException)) + result <- k ans + runIO (putMVar m result) + return result ===================================== libraries/ghc-internal/src/GHC/Internal/Control/Monad/ST/Lazy/Imp.hs ===================================== @@ -37,7 +37,6 @@ module GHC.Internal.Control.Monad.ST.Lazy.Imp ( unsafeIOToST ) where -import GHC.Internal.Control.Monad.Fix import GHC.Internal.Data.Tuple import qualified GHC.Internal.Control.Monad.ST.Imp as ST @@ -210,10 +209,6 @@ fixST m = ST (\ s -> -- itself is demanded directly in the `let` body. See also -- Note [Lazy ST: not producing lazy pairs]. --- | @since base-2.01 -instance MonadFix (ST s) where - mfix = fixST - -- | @since base-4.23.0.0 instance Semigroup a => Semigroup (ST s a) where (<>) = liftA2 (<>) ===================================== libraries/ghc-internal/src/GHC/Internal/Data/Functor/Identity.hs ===================================== @@ -33,7 +33,6 @@ module GHC.Internal.Data.Functor.Identity ( Identity(..), ) where -import GHC.Internal.Control.Monad.Fix import GHC.Internal.Data.Bits (Bits, FiniteBits) import GHC.Internal.Data.Coerce import GHC.Internal.Data.Foldable @@ -143,7 +142,3 @@ instance Applicative Identity where -- | @since base-4.8.0.0 instance Monad Identity where m >>= k = k (runIdentity m) - --- | @since base-4.8.0.0 -instance MonadFix Identity where - mfix f = Identity (fix (runIdentity . f)) ===================================== libraries/ghc-internal/src/GHC/Internal/System/IO.hs ===================================== @@ -20,7 +20,6 @@ module GHC.Internal.System.IO ( -- * The IO monad IO, - fixIO, -- * Files and handles @@ -258,7 +257,6 @@ import GHC.Internal.IO.Encoding import GHC.Internal.Text.Read import GHC.Internal.IO.StdHandles import GHC.Internal.Show -import GHC.Internal.MVar ----------------------------------------------------------------------------- -- Standard IO @@ -602,87 +600,6 @@ hReady h = hWaitForInput h 0 hPrint :: Show a => Handle -> a -> IO () hPrint hdl = hPutStrLn hdl . show - --- --------------------------------------------------------------------------- --- fixIO - --- | The implementation of 'Control.Monad.Fix.mfix' for 'IO'. --- --- This operation may fail with: --- --- * 'FixIOException' if the function passed to 'fixIO' inspects its argument. --- --- ==== __Examples__ --- --- the IO-action is only executed once. The recursion is only on the values. --- --- >>> take 3 <$> fixIO (\x -> putStr ":D" >> (:x) <$> readLn @Int) --- :D --- 2 --- [2,2,2] --- --- If we are strict in the value, just as with 'Data.Function.fix', we do not get termination: --- --- >>> fixIO (\x -> putStr x >> pure ('x' : x)) --- * hangs forever * --- --- We can tie the knot of a structure within 'IO' using 'fixIO': --- --- @ --- data Node = MkNode Int (IORef Node) --- --- foo :: IO () --- foo = do --- p \<- fixIO (\p -> newIORef (MkNode 0 p)) --- q <- output p --- r <- output q --- _ <- output r --- pure () --- --- output :: IORef Node -> IO (IORef Node) --- output ref = do --- MkNode x p <- readIORef ref --- print x --- pure p --- @ --- --- >>> foo --- 0 --- 0 --- 0 -fixIO :: (a -> IO a) -> IO a -fixIO k = do - m <- newEmptyMVar - ans <- unsafeDupableInterleaveIO - (readMVar m `catch` \BlockedIndefinitelyOnMVar -> - throwIO FixIOException) - result <- k ans - putMVar m result - return result - --- Note [Blackholing in fixIO] --- ~~~~~~~~~~~~~~~~~~~~~~~~~~~ --- We do our own explicit black holing here, because GHC's lazy --- blackholing isn't enough. In an infinite loop, GHC may run the IO --- computation a few times before it notices the loop, which is wrong. --- --- NOTE2: the explicit black-holing with an IORef ran into trouble --- with multiple threads (see #5421), so now we use an MVar. We used --- to use takeMVar with unsafeInterleaveIO. This, however, uses noDuplicate#, --- which is not particularly cheap. Better to use readMVar, which can be --- performed in multiple threads safely, and to use unsafeDupableInterleaveIO --- to avoid the noDuplicate cost. --- --- What we'd ideally want is probably an IVar, but we don't quite have those. --- STM TVars look like an option at first, but I don't think they are: --- we'd need to be able to write to the variable in an IO context, which can --- only be done using 'atomically', and 'atomically' is not allowed within --- unsafePerformIO. We can't know if someone will try to use the result --- of fixIO with unsafePerformIO! --- --- See also System.IO.Unsafe.unsafeFixIO. --- - -- | The function creates a temporary file in ReadWrite mode. -- The created file isn\'t deleted automatically, so you need to delete it manually. -- ===================================== libraries/ghc-internal/src/GHC/Internal/TH/Monad.hs ===================================== @@ -28,13 +28,8 @@ import Data.Data hiding (Fixity(..)) import Data.IORef import System.IO.Unsafe ( unsafePerformIO ) import Control.Monad.IO.Class (MonadIO (..)) -import Control.Monad.Fix (MonadFix (..)) -import Control.Exception (BlockedIndefinitelyOnMVar (..), catch, throwIO) -import Control.Exception.Base (FixIOException (..)) -import Control.Concurrent.MVar (newEmptyMVar, readMVar, putMVar) import System.IO ( hPutStrLn, stderr ) import qualified Data.Kind as Kind (Type) -import GHC.IO.Unsafe ( unsafeDupableInterleaveIO ) import GHC.Types (TYPE, RuntimeRep(..)) #else import GHC.Internal.Base hiding (NonEmpty(..),Type, Module, sequence) @@ -46,12 +41,8 @@ import GHC.Internal.Data.Foldable import GHC.Internal.Data.Typeable import GHC.Internal.Control.Monad.IO.Class import GHC.Internal.Control.Monad.Fail -import GHC.Internal.Control.Monad.Fix -import GHC.Internal.Control.Exception import GHC.Internal.Num import GHC.Internal.IO.Unsafe -import GHC.Internal.MVar -import GHC.Internal.IO.Exception import qualified GHC.Internal.Types as Kind (Type) #endif import GHC.Internal.ForeignSrcLang @@ -258,22 +249,6 @@ instance Semigroup a => Semigroup (Q a) where instance Monoid a => Monoid (Q a) where mempty = pure mempty --- | If the function passed to 'mfix' inspects its argument, --- the resulting action will throw a 'FixIOException'. --- --- @since 2.17.0.0 -instance MonadFix Q where - -- We use the same blackholing approach as in fixIO. - -- See Note [Blackholing in fixIO] in System.IO in base. - mfix k = do - m <- runIO newEmptyMVar - ans <- runIO (unsafeDupableInterleaveIO - (readMVar m `catch` \BlockedIndefinitelyOnMVar -> - throwIO FixIOException)) - result <- k ans - runIO (putMVar m result) - return result - ----------------------------------------------------- -- ===================================== testsuite/tests/ghci/scripts/ListTuplePunsPpr.stdout ===================================== @@ -41,10 +41,10 @@ data Tuple2 a b = (,) a b -- Defined in ‘GHC.Internal.Tuple’ instance Traversable (Tuple2 a) -- Defined in ‘GHC.Internal.Data.Traversable’ -instance Foldable (Tuple2 a) - -- Defined in ‘GHC.Internal.Data.Foldable’ instance Monoid a => Applicative (Tuple2 a) -- Defined in ‘GHC.Internal.Base’ +instance Foldable (Tuple2 a) + -- Defined in ‘GHC.Internal.Data.Foldable’ instance Functor (Tuple2 a) -- Defined in ‘GHC.Internal.Base’ instance Monoid a => Monad (Tuple2 a) -- Defined in ‘GHC.Internal.Base’ ===================================== testsuite/tests/ghci/scripts/T4175.stdout ===================================== @@ -44,8 +44,8 @@ instance Traversable Maybe -- Defined in ‘GHC.Internal.Data.Traversable’ instance MonadFail Maybe -- Defined in ‘GHC.Internal.Control.Monad.Fail’ -instance Foldable Maybe -- Defined in ‘GHC.Internal.Data.Foldable’ instance Applicative Maybe -- Defined in ‘GHC.Internal.Base’ +instance Foldable Maybe -- Defined in ‘GHC.Internal.Data.Foldable’ instance Functor Maybe -- Defined in ‘GHC.Internal.Base’ instance Monad Maybe -- Defined in ‘GHC.Internal.Base’ instance Semigroup a => Monoid (Maybe a) ===================================== testsuite/tests/interface-stability/base-exports.stdout ===================================== @@ -11553,6 +11553,7 @@ instance forall e. GHC.Internal.Control.Monad.Fix.MonadFix (GHC.Internal.Data.Ei instance forall r. GHC.Internal.Control.Monad.Fix.MonadFix ((->) r) -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Data.Monoid.First -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Types.IO -- Defined in ‘GHC.Internal.Control.Monad.Fix’ +instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Data.Functor.Identity.Identity -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Data.Monoid.Last -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix [] -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance forall (f :: * -> *) i (c :: GHC.Internal.Generics.Meta). GHC.Internal.Control.Monad.Fix.MonadFix f => GHC.Internal.Control.Monad.Fix.MonadFix (GHC.Internal.Generics.M1 i c f) -- Defined in ‘GHC.Internal.Control.Monad.Fix’ @@ -11560,14 +11561,14 @@ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Maybe.Maybe -- Def instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Base.NonEmpty -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Generics.Par1 -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Data.Semigroup.Internal.Product -- Defined in ‘GHC.Internal.Control.Monad.Fix’ +instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.TH.Monad.Q -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance forall (f :: * -> *). GHC.Internal.Control.Monad.Fix.MonadFix f => GHC.Internal.Control.Monad.Fix.MonadFix (GHC.Internal.Generics.Rec1 f) -- Defined in ‘GHC.Internal.Control.Monad.Fix’ +instance forall s. GHC.Internal.Control.Monad.Fix.MonadFix (GHC.Internal.Control.Monad.ST.Lazy.Imp.ST s) -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance forall s. GHC.Internal.Control.Monad.Fix.MonadFix (GHC.Internal.ST.ST s) -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix Solo -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Data.Semigroup.Internal.Sum -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance forall a. GHC.Internal.Base.Monoid a => GHC.Internal.Control.Monad.Fix.MonadFix ((,) a) -- Defined in ‘GHC.Internal.Control.Monad.Fix’ -instance forall s. GHC.Internal.Control.Monad.Fix.MonadFix (GHC.Internal.Control.Monad.ST.Lazy.Imp.ST s) -- Defined in ‘GHC.Internal.Control.Monad.ST.Lazy.Imp’ instance GHC.Internal.Control.Monad.Fix.MonadFix Data.Complex.Complex -- Defined in ‘Data.Complex’ -instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Data.Functor.Identity.Identity -- Defined in ‘GHC.Internal.Data.Functor.Identity’ instance [safe] forall (f :: * -> *) (g :: * -> *). (GHC.Internal.Control.Monad.Fix.MonadFix f, GHC.Internal.Control.Monad.Fix.MonadFix g) => GHC.Internal.Control.Monad.Fix.MonadFix (Data.Functor.Product.Product f g) -- Defined in ‘Data.Functor.Product’ instance GHC.Internal.Control.Monad.Fix.MonadFix Data.Semigroup.First -- Defined in ‘Data.Semigroup’ instance GHC.Internal.Control.Monad.Fix.MonadFix Data.Semigroup.Last -- Defined in ‘Data.Semigroup’ ===================================== testsuite/tests/interface-stability/base-exports.stdout-javascript-unknown-ghcjs ===================================== @@ -11580,6 +11580,7 @@ instance forall e. GHC.Internal.Control.Monad.Fix.MonadFix (GHC.Internal.Data.Ei instance forall r. GHC.Internal.Control.Monad.Fix.MonadFix ((->) r) -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Data.Monoid.First -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Types.IO -- Defined in ‘GHC.Internal.Control.Monad.Fix’ +instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Data.Functor.Identity.Identity -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Data.Monoid.Last -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix [] -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance forall (f :: * -> *) i (c :: GHC.Internal.Generics.Meta). GHC.Internal.Control.Monad.Fix.MonadFix f => GHC.Internal.Control.Monad.Fix.MonadFix (GHC.Internal.Generics.M1 i c f) -- Defined in ‘GHC.Internal.Control.Monad.Fix’ @@ -11587,14 +11588,14 @@ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Maybe.Maybe -- Def instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Base.NonEmpty -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Generics.Par1 -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Data.Semigroup.Internal.Product -- Defined in ‘GHC.Internal.Control.Monad.Fix’ +instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.TH.Monad.Q -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance forall (f :: * -> *). GHC.Internal.Control.Monad.Fix.MonadFix f => GHC.Internal.Control.Monad.Fix.MonadFix (GHC.Internal.Generics.Rec1 f) -- Defined in ‘GHC.Internal.Control.Monad.Fix’ +instance forall s. GHC.Internal.Control.Monad.Fix.MonadFix (GHC.Internal.Control.Monad.ST.Lazy.Imp.ST s) -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance forall s. GHC.Internal.Control.Monad.Fix.MonadFix (GHC.Internal.ST.ST s) -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix Solo -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Data.Semigroup.Internal.Sum -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance forall a. GHC.Internal.Base.Monoid a => GHC.Internal.Control.Monad.Fix.MonadFix ((,) a) -- Defined in ‘GHC.Internal.Control.Monad.Fix’ -instance forall s. GHC.Internal.Control.Monad.Fix.MonadFix (GHC.Internal.Control.Monad.ST.Lazy.Imp.ST s) -- Defined in ‘GHC.Internal.Control.Monad.ST.Lazy.Imp’ instance GHC.Internal.Control.Monad.Fix.MonadFix Data.Complex.Complex -- Defined in ‘Data.Complex’ -instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Data.Functor.Identity.Identity -- Defined in ‘GHC.Internal.Data.Functor.Identity’ instance [safe] forall (f :: * -> *) (g :: * -> *). (GHC.Internal.Control.Monad.Fix.MonadFix f, GHC.Internal.Control.Monad.Fix.MonadFix g) => GHC.Internal.Control.Monad.Fix.MonadFix (Data.Functor.Product.Product f g) -- Defined in ‘Data.Functor.Product’ instance GHC.Internal.Control.Monad.Fix.MonadFix Data.Semigroup.First -- Defined in ‘Data.Semigroup’ instance GHC.Internal.Control.Monad.Fix.MonadFix Data.Semigroup.Last -- Defined in ‘Data.Semigroup’ ===================================== testsuite/tests/interface-stability/base-exports.stdout-mingw32 ===================================== @@ -11811,6 +11811,7 @@ instance forall e. GHC.Internal.Control.Monad.Fix.MonadFix (GHC.Internal.Data.Ei instance forall r. GHC.Internal.Control.Monad.Fix.MonadFix ((->) r) -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Data.Monoid.First -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Types.IO -- Defined in ‘GHC.Internal.Control.Monad.Fix’ +instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Data.Functor.Identity.Identity -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Data.Monoid.Last -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix [] -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance forall (f :: * -> *) i (c :: GHC.Internal.Generics.Meta). GHC.Internal.Control.Monad.Fix.MonadFix f => GHC.Internal.Control.Monad.Fix.MonadFix (GHC.Internal.Generics.M1 i c f) -- Defined in ‘GHC.Internal.Control.Monad.Fix’ @@ -11818,14 +11819,14 @@ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Maybe.Maybe -- Def instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Base.NonEmpty -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Generics.Par1 -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Data.Semigroup.Internal.Product -- Defined in ‘GHC.Internal.Control.Monad.Fix’ +instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.TH.Monad.Q -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance forall (f :: * -> *). GHC.Internal.Control.Monad.Fix.MonadFix f => GHC.Internal.Control.Monad.Fix.MonadFix (GHC.Internal.Generics.Rec1 f) -- Defined in ‘GHC.Internal.Control.Monad.Fix’ +instance forall s. GHC.Internal.Control.Monad.Fix.MonadFix (GHC.Internal.Control.Monad.ST.Lazy.Imp.ST s) -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance forall s. GHC.Internal.Control.Monad.Fix.MonadFix (GHC.Internal.ST.ST s) -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix Solo -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Data.Semigroup.Internal.Sum -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance forall a. GHC.Internal.Base.Monoid a => GHC.Internal.Control.Monad.Fix.MonadFix ((,) a) -- Defined in ‘GHC.Internal.Control.Monad.Fix’ -instance forall s. GHC.Internal.Control.Monad.Fix.MonadFix (GHC.Internal.Control.Monad.ST.Lazy.Imp.ST s) -- Defined in ‘GHC.Internal.Control.Monad.ST.Lazy.Imp’ instance GHC.Internal.Control.Monad.Fix.MonadFix Data.Complex.Complex -- Defined in ‘Data.Complex’ -instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Data.Functor.Identity.Identity -- Defined in ‘GHC.Internal.Data.Functor.Identity’ instance [safe] forall (f :: * -> *) (g :: * -> *). (GHC.Internal.Control.Monad.Fix.MonadFix f, GHC.Internal.Control.Monad.Fix.MonadFix g) => GHC.Internal.Control.Monad.Fix.MonadFix (Data.Functor.Product.Product f g) -- Defined in ‘Data.Functor.Product’ instance GHC.Internal.Control.Monad.Fix.MonadFix Data.Semigroup.First -- Defined in ‘Data.Semigroup’ instance GHC.Internal.Control.Monad.Fix.MonadFix Data.Semigroup.Last -- Defined in ‘Data.Semigroup’ ===================================== testsuite/tests/interface-stability/base-exports.stdout-ws-32 ===================================== @@ -11553,6 +11553,7 @@ instance forall e. GHC.Internal.Control.Monad.Fix.MonadFix (GHC.Internal.Data.Ei instance forall r. GHC.Internal.Control.Monad.Fix.MonadFix ((->) r) -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Data.Monoid.First -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Types.IO -- Defined in ‘GHC.Internal.Control.Monad.Fix’ +instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Data.Functor.Identity.Identity -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Data.Monoid.Last -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix [] -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance forall (f :: * -> *) i (c :: GHC.Internal.Generics.Meta). GHC.Internal.Control.Monad.Fix.MonadFix f => GHC.Internal.Control.Monad.Fix.MonadFix (GHC.Internal.Generics.M1 i c f) -- Defined in ‘GHC.Internal.Control.Monad.Fix’ @@ -11560,14 +11561,14 @@ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Maybe.Maybe -- Def instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Base.NonEmpty -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Generics.Par1 -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Data.Semigroup.Internal.Product -- Defined in ‘GHC.Internal.Control.Monad.Fix’ +instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.TH.Monad.Q -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance forall (f :: * -> *). GHC.Internal.Control.Monad.Fix.MonadFix f => GHC.Internal.Control.Monad.Fix.MonadFix (GHC.Internal.Generics.Rec1 f) -- Defined in ‘GHC.Internal.Control.Monad.Fix’ +instance forall s. GHC.Internal.Control.Monad.Fix.MonadFix (GHC.Internal.Control.Monad.ST.Lazy.Imp.ST s) -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance forall s. GHC.Internal.Control.Monad.Fix.MonadFix (GHC.Internal.ST.ST s) -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix Solo -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Data.Semigroup.Internal.Sum -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance forall a. GHC.Internal.Base.Monoid a => GHC.Internal.Control.Monad.Fix.MonadFix ((,) a) -- Defined in ‘GHC.Internal.Control.Monad.Fix’ -instance forall s. GHC.Internal.Control.Monad.Fix.MonadFix (GHC.Internal.Control.Monad.ST.Lazy.Imp.ST s) -- Defined in ‘GHC.Internal.Control.Monad.ST.Lazy.Imp’ instance GHC.Internal.Control.Monad.Fix.MonadFix Data.Complex.Complex -- Defined in ‘Data.Complex’ -instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Data.Functor.Identity.Identity -- Defined in ‘GHC.Internal.Data.Functor.Identity’ instance [safe] forall (f :: * -> *) (g :: * -> *). (GHC.Internal.Control.Monad.Fix.MonadFix f, GHC.Internal.Control.Monad.Fix.MonadFix g) => GHC.Internal.Control.Monad.Fix.MonadFix (Data.Functor.Product.Product f g) -- Defined in ‘Data.Functor.Product’ instance GHC.Internal.Control.Monad.Fix.MonadFix Data.Semigroup.First -- Defined in ‘Data.Semigroup’ instance GHC.Internal.Control.Monad.Fix.MonadFix Data.Semigroup.Last -- Defined in ‘Data.Semigroup’ ===================================== testsuite/tests/interface-stability/template-haskell-exports.stdout ===================================== @@ -2052,7 +2052,6 @@ instance GHC.Internal.Classes.Ord GHC.Internal.TH.Syntax.Type -- Defined in ‘G instance GHC.Internal.Classes.Ord GHC.Internal.TH.Syntax.TypeFamilyHead -- Defined in ‘GHC.Internal.TH.Syntax’ instance GHC.Internal.Classes.Ord GHC.Internal.LanguageExtensions.Extension -- Defined in ‘GHC.Internal.LanguageExtensions’ instance GHC.Internal.Control.Monad.Fail.MonadFail GHC.Internal.TH.Monad.Q -- Defined in ‘GHC.Internal.TH.Monad’ -instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.TH.Monad.Q -- Defined in ‘GHC.Internal.TH.Monad’ instance GHC.Internal.Control.Monad.IO.Class.MonadIO GHC.Internal.TH.Monad.Q -- Defined in ‘GHC.Internal.TH.Monad’ instance GHC.Internal.Data.Foldable.Foldable GHC.Internal.TH.Syntax.TyVarBndr -- Defined in ‘GHC.Internal.TH.Syntax’ instance GHC.Internal.Data.Traversable.Traversable GHC.Internal.TH.Syntax.TyVarBndr -- Defined in ‘GHC.Internal.TH.Syntax’ ===================================== testsuite/tests/mdo/should_fail/mdofail006.stderr ===================================== @@ -5,5 +5,5 @@ cyclic evaluation in fixIO While handling thread blocked indefinitely in an MVar operation HasCallStack backtrace: - throwIO, called at libraries/ghc-internal/src/GHC/Internal/System/IO.hs:641:37 in ghc-internal:GHC.Internal.System.IO + throwIO, called at libraries/ghc-internal/src/GHC/Internal/Control/Monad/Fix.hs:167:37 in ghc-internal:GHC.Internal.Control.Monad.Fix View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/af32ff389ca3280bfa62cebf399b98c… -- View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/af32ff389ca3280bfa62cebf399b98c… You're receiving this email because of your account on gitlab.haskell.org.
1 0
0 0
[Git][ghc/ghc][wip/io-manager-deadlock-detection] Deleted 6 commits: Free per-cap I/O managers during shutdown and forkProcess
by Duncan Coutts (@dcoutts) 19 Feb '26

19 Feb '26
Duncan Coutts pushed to branch wip/io-manager-deadlock-detection at Glasgow Haskell Compiler / GHC WARNING: The push did not contain any new commits, but force pushed to delete the commits and changes below. Deleted commits: e9c4d16c by Duncan Coutts at 2026-02-16T00:30:50+00:00 Free per-cap I/O managers during shutdown and forkProcess Historically this was not strictly necessary. The select and win32 legacy I/O managers did not maintain any dynamically allocated resources. The new poll one does (an auxillary table), and so this should be freed. After forkProcess, all threads get deleted. This includes threads waiting on I/O or timers. So as of this patch, resetting the I/O manager is just about tidying things up. For example, for the poll I/O manager this will reset the size of the AIOP table (which otherwise grows but never shrinks). In future however the re-initialising will become neeecessary for functionality, since some I/O managers will need to re-initialise wakeup fds that are set CLOEXEC. - - - - - fcc1cdd3 by Duncan Coutts at 2026-02-16T00:30:50+00:00 Add an FdWakup module for posix I/O managers This will be used to implement wakeupIOManager for in-RTS I/O managers. It provides a notification/wakeup mechanism using FDs, suitable for situations when I/O managers are blocked on a set of fds anyway. - - - - - 4e4aa84f by Duncan Coutts at 2026-02-16T00:30:50+00:00 Add wakeupIOManager support for select I/O manager Uses the FdWakup mechanism. - - - - - 6fe60774 by Duncan Coutts at 2026-02-16T00:30:50+00:00 Add wakeupIOManager support for poll I/O manager Uses the FdWakup mechanism. A quirk we have to cope with is that we now need to poll one more fd -- the wakeup_fd_r -- but this fd has no corresponding entry in the aiop_table. This is awkward since we have set up our aiop_poll_table to be an auxilliary table with matching indicies. The solution this patch uses (and described in the comments) is to have two tables: struct pollfd *aiop_poll_table, *full_poll_table; and to have the aiop_poll_table alias the tail of the full_poll_table. The head entry in the full_poll_table is the extra fd. So we poll the full_poll_table, while the aiop_poll_table still has matching indicies with the aiop_table. Hurrah for C aliasing rules. - - - - - 24b6f621 by Duncan Coutts at 2026-02-16T00:30:50+00:00 Add wakeupIOManager support for win32 legacy I/O manager - - - - - bc97a19f by Duncan Coutts at 2026-02-16T00:30:50+00:00 wakeupIOManager is now required for all I/O managers We are going to rely on it. Previously it could be a no-op. Update the docs in the header file. Also, temporarily disable awaitCompletedTimeoutsOrIO post-condition assertion. It will become more complicated due to wakeupIOManager, and it's not yet clear how to express it. We will re-introduce a post condition after a few more changes. - - - - - 12 changed files: - rts/Capability.c - rts/IOManager.c - rts/IOManager.h - rts/IOManagerInternals.h - rts/Schedule.c - + rts/posix/FdWakeup.c - + rts/posix/FdWakeup.h - rts/posix/Poll.c - rts/posix/Poll.h - rts/posix/Select.c - rts/posix/Select.h - rts/rts.cabal Changes: ===================================== rts/Capability.c ===================================== @@ -1280,6 +1280,7 @@ shutdownCapabilities(Task *task, bool safe) static void freeCapability (Capability *cap) { + freeCapabilityIOManager(cap->iomgr); stgFree(cap->mut_lists); stgFree(cap->saved_mut_lists); if (cap->current_segments) { ===================================== rts/IOManager.c ===================================== @@ -343,9 +343,7 @@ void initCapabilityIOManager(CapIOManager *iomgr) switch (iomgr_type) { #if defined(IOMGR_ENABLED_SELECT) case IO_MANAGER_SELECT: - iomgr->blocked_queue_hd = END_TSO_QUEUE; - iomgr->blocked_queue_tl = END_TSO_QUEUE; - iomgr->sleeping_queue = END_TSO_QUEUE; + initCapabilityIOManagerSelect(iomgr); break; #endif @@ -373,6 +371,26 @@ void initCapabilityIOManager(CapIOManager *iomgr) } +void freeCapabilityIOManager(CapIOManager *iomgr) +{ + switch (iomgr_type) { +#if defined(IOMGR_ENABLED_SELECT) + case IO_MANAGER_SELECT: + freeCapabilityIOManagerSelect(iomgr); + break; +#endif + +#if defined(IOMGR_ENABLED_POLL) + case IO_MANAGER_POLL: + freeCapabilityIOManagerPoll(iomgr); + break; +#endif + default: + break; + } +} + + /* Called late in the RTS initialisation */ void startIOManager(void) @@ -546,8 +564,21 @@ exitIOManager(bool wait_threads) */ void wakeupIOManager(void) { + debugTrace(DEBUG_iomanager, "Sending wakeup to I/O manager..."); switch (iomgr_type) { +#if defined(IOMGR_ENABLED_SELECT) + case IO_MANAGER_SELECT: + wakeupIOManagerSelect(MainCapability.iomgr); + break; +#endif + +#if defined(IOMGR_ENABLED_POLL) + case IO_MANAGER_POLL: + wakeupIOManagerPoll(MainCapability.iomgr); + break; +#endif + #if defined(IOMGR_ENABLED_MIO_POSIX) case IO_MANAGER_MIO_POSIX: /* MIO Posix implementation in posix/Signals.c */ @@ -572,8 +603,13 @@ void wakeupIOManager(void) #endif break; #endif - default: +#if defined(IOMGR_ENABLED_WIN32_LEGACY) + case IO_MANAGER_WIN32_LEGACY: + abandonRequestWait(); break; +#endif + default: + barf("wakeupIOManager not implemented"); } } @@ -782,7 +818,9 @@ void awaitCompletedTimeoutsOrIO(CapIOManager *iomgr) default: barf("pollCompletedTimeoutsOrIO not implemented"); } - ASSERT(!emptyRunQueue(iomgr->cap) || getSchedState() != SCHED_RUNNING); + // FIXME: the post condition is now more complicated. Await can now simply + // be interrupted by wakeupIOManager. + // ASSERT(!emptyRunQueue(iomgr->cap) || getSchedState() != SCHED_RUNNING); } ===================================== rts/IOManager.h ===================================== @@ -242,6 +242,28 @@ CapIOManager *allocCapabilityIOManager(Capability *cap); */ void initCapabilityIOManager(CapIOManager *iomgr); +/* When shutting down a capability, or after forkProcess, free the resources + * held by a CapIOManager to put it back into a state in which either it can be + * re-initialised using initCapabilityIOManager, or the whole structure freed. + * + * Note that this does not free the CapIOManager structure itself, just the + * contents. + * + * This is used during capability shutdown, during RTS shutdown. It is not used + * when reducing the number of capabilities. Capabilities are disabled rather + * than freed entirely: the I/O manager keeps running but threads that become + * runnable are migrated away. + * + * It is also used after forkProcess. + */ +void freeCapabilityIOManager(CapIOManager *iomgr); + +/* CapIOManager life cycle: + * + * alloc -> init -> free -> free struct + * ^ | + * +--------+ + */ /* Init hook: called from hs_init_ghc, very late in the startup after almost * everything else is done. @@ -283,19 +305,13 @@ void stopIOManager(void); void exitIOManager(bool wait_threads); -/* Wakeup hook: called from the scheduler's wakeUpRts (currently only in - * threaded mode). +/* Wakeup hook: called from the scheduler's wakeUpRts(). * * The I/O manager can be blocked waiting on I/O or timers. Sometimes there are * other external events where we need to wake up the I/O manager and return - * to the schedulr. - * - * At the moment, all the non-threaded I/O managers will do this automagically - * since a signal will interrupt any waiting system calls, so at the moment - * the implementation for the non-threaded I/O managers does nothing. + * to the scheduler. * - * For the I/O managers in threaded mode, this arranges to unblock the I/O - * manager if it waa blocked waiting. + * This arranges to unblock the I/O manager if it was blocked waiting. */ void wakeupIOManager(void); ===================================== rts/IOManagerInternals.h ===================================== @@ -46,6 +46,11 @@ struct _CapIOManager { StgTSO *sleeping_queue; #endif +#if defined(IOMGR_ENABLED_SELECT) || defined(IOMGR_ENABLED_POLL) + /* FDs for waking up the I/O manager when it is blocked waiting */ + int wakeup_fd_r, wakeup_fd_w; +#endif + #if defined(IOMGR_ENABLED_POLL) /* AIOP and timeout collections shared by several I/O manager impls */ ClosureTable aiop_table; @@ -53,8 +58,11 @@ struct _CapIOManager { #endif #if defined(IOMGR_ENABLED_POLL) - /* Auxiliary table with size and indexes matching the aiop_table */ - struct pollfd *aiop_poll_table; + /* Auxiliary table with size and indexes matching the aiop_table. This is + * aliased to the tail of the full poll table, which has a head entry for + * the wakeup_fd_r above, so we can also poll that fd. + */ + struct pollfd *aiop_poll_table, *full_poll_table; #endif #if defined(IOMGR_ENABLED_WIN32_LEGACY) ===================================== rts/Schedule.c ===================================== @@ -2165,6 +2165,15 @@ forkProcess(HsStablePtr *entry // exist. truncateRunQueue(cap); + // Reset and re-initialise the capability's I/O manager, + // to get the I/O manager ready again. + // + // Any threads waiting on I/O or timers should have been + // removed from I/O manager queues by deleteThread_ above. + // TODO: but we could assert that here. + freeCapabilityIOManager(cap->iomgr); + initCapabilityIOManager(cap->iomgr); + // Any suspended C-calling Tasks are no more, their OS threads // don't exist now: cap->suspended_ccalls = NULL; @@ -2309,6 +2318,10 @@ setNumCapabilities (uint32_t new_n_capabilities USED_IF_THREADS) // the capability; we don't have to worry about GC data // structures, the nursery, etc. // + // This approach also handles threads blocked on I/O. Such threads + // remain blocked, and when I/O completes and threads become runnable + // then they are migrated away. + // for (n = new_n_capabilities; n < enabled_capabilities; n++) { getCapability(n)->disabled = true; traceCapDisable(getCapability(n)); ===================================== rts/posix/FdWakeup.c ===================================== @@ -0,0 +1,132 @@ +/* ----------------------------------------------------------------------------- + * + * (c) The GHC Team 2025 + * + * Utilities for a simple fd-based cross-thread wakeup mechanism. + * + * This is used in I/O managers, to provide a mechanism to wake them when they + * are blocked waiting on fds and timeouts. The mechanism works by including + * the read end fd into the set of fds the I/O manager waits on, and when a + * wake up is needed, the write end fd is used. + * + * This is implemented using either eventfd() or pipe(). + * + * Linux 2.6.22+ and FreeBSD 13+ support eventfd. It is a single fd with a + * 64bit counter. It uses less resources than a pipe, and is probably a tad + * faster. Using write() adds to the counter, while read() reads and resets + * it. This gives us event combining. + * + * Otherwise we use a classic unix pipe. + * + * -------------------------------------------------------------------------*/ + +#include "rts/PosixSource.h" +#include "Rts.h" + +#include "FdWakeup.h" + +#include <fcntl.h> +#include <unistd.h> + +#ifdef HAVE_SYS_EVENTFD_H +#include <sys/eventfd.h> +#endif + +#if !defined(HAVE_EVENTFD) \ + || (defined(HAVE_EVENTFD) && !(defined(EFD_CLOEXEC) && defined(EFD_NONBLOCK))) +static void fcntl_CLOEXEC_NONBLOCK(int fd) +{ + int res1 = fcntl(fd, F_SETFD, FD_CLOEXEC); + int res2 = fcntl(fd, F_SETFL, O_NONBLOCK); + if (RTS_UNLIKELY(res1 < 0 || res2 < 0)) { + sysErrorBelch("newFdWakeup fcntl()"); + stg_exit(EXIT_FAILURE); + } +} +#endif + +void newFdWakeup(int *wakeup_fd_r, int *wakeup_fd_w) +{ +#if defined(HAVE_EVENTFD) + int wakeup_fd; +#if defined(EFD_CLOEXEC) && defined(EFD_NONBLOCK) + wakeup_fd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); +#else + wakeup_fd = eventfd(0, 0); + if (wakeup_fd >= 0) fcntl_CLOEXEC_NONBLOCK(wakeup_fd); +#endif + if (RTS_UNLIKELY(wakeup_fd < 0)) { + sysErrorBelch("newFdWakeup eventfd()"); + stg_exit(EXIT_FAILURE); + } + /* eventfd uses the same fd for each end */ + *wakeup_fd_r = wakeup_fd; + *wakeup_fd_w = wakeup_fd; +#else + int pipefd[2]; + int res; + res = pipe(pipefd); + if (RTS_UNLIKELY(res < 0)) { + sysErrorBelch("newFdWakeup pipe"); + stg_exit(EXIT_FAILURE); + } + fcntl_CLOEXEC_NONBLOCK(pipefd[0]); + fcntl_CLOEXEC_NONBLOCK(pipefd[1]); + *wakeup_fd_r = pipefd[0]; /* read end */ + *wakeup_fd_w = pipefd[1]; /* write end */ +#endif +} + +void closeFdWakeup(int wakeup_fd_r, int wakeup_fd_w) +{ +#if defined(HAVE_EVENTFD) + ASSERT(wakeup_fd_r == wakeup_fd_w); + close(wakeup_fd_r); +#else + ASSERT(wakeup_fd_r != wakeup_fd_w); + close(wakeup_fd_r); + close(wakeup_fd_w); +#endif +} + +void sendFdWakeup(int wakeup_fd_w) +{ + int res; +#if defined(HAVE_EVENTFD) + uint64_t val = 1; + res = write(wakeup_fd_w, &val, 8); +#else + unsigned char buf = 1; + res = write(wakeup_fd_w, &buf, 1); +#endif + if (RTS_UNLIKELY(res < 0)) { + /* Unlikely the pipe buffer will fill, but it would not be an error. */ + if (errno == EAGAIN) return; + sysErrorBelch("sendFdWakeup write"); + stg_exit(EXIT_FAILURE); + } +} + +void collectFdWakeup(int wakeup_fd_r) +{ + int res; +#if defined(HAVE_EVENTFD) + uint64_t buf; + /* eventfd combines events into one counter, so a single read is enough */ + res = read(wakeup_fd_r, &buf, 8); +#else + /* Drain the pipe buffer. Multiple wakeup notifications could + * have been sent before we have a chance to collect them. + */ + uint64_t buf; + do { + res = read(wakeup_fd_r, &buf, 8); + } while (res == 8); +#endif + if (RTS_UNLIKELY(res < 0)) { + /* After the first pipe read, it could block */ + if (errno == EAGAIN) return; + sysErrorBelch("collectFdWakeup read"); + stg_exit(EXIT_FAILURE); + } +} ===================================== rts/posix/FdWakeup.h ===================================== @@ -0,0 +1,27 @@ +/* ----------------------------------------------------------------------------- + * + * (c) The GHC Team 2025 + * + * Utilities for a simple fd-based cross-thread wakeup mechanism. + * + * This is used in I/O managers, to provide a mechanism to wake them when they + * are blocked waiting on fds and timeouts. The mechanism works by including + * the read end fd into the set of fds the I/O manager waits on, and when a + * wake up is needed, the write end fd is used. + * + * Prototypes for functions in FdWakeup.c + * + * -------------------------------------------------------------------------*/ + +#pragma once + +#include "BeginPrivate.h" + +void newFdWakeup(int *fd_r, int *fd_w); +void closeFdWakeup(int fd_r, int fd_w); + +void sendFdWakeup(int fd_w); +void collectFdWakeup(int fd_r); + +#include "EndPrivate.h" + ===================================== rts/posix/Poll.c ===================================== @@ -41,6 +41,7 @@ #include "IOManagerInternals.h" #include "Timeout.h" +#include "FdWakeup.h" /****************************************************************************** @@ -107,8 +108,9 @@ timeout (if any) as the poll() timeout parameter. The CapIOManager structure for this I/O manager contains: ClosureTable aiop_table; - struct pollfd *aiop_poll_table; + struct pollfd *aiop_poll_table, *full_poll_table; StgTimeoutQueue *timeout_queue; + int wakeup_fd_r, wakeup_fd_w; We also support the Linux-specific ppoll API which supports higher resolution time delays -- nanoseconds rather than milliseconds as in classic poll(). It @@ -117,6 +119,15 @@ also allows the signal mask to be adjusted, but we do not make use of this. int ppoll(struct pollfd *fds, nfds_t nfds, const struct timespec *tmo_p, const sigset_t *sigmask); +We have both aiop_poll_table and full_poll_table. This is to cope with needing +to wait on the special extra file descriptor wakeup_fd_r. This fd is used to +support waking the I/O manager when we are blocked in a poll call. This +requires waiting on an extra fd that has no corresponding entry in the +aiop_table. To manage this quirk, we alias the aiop_poll_table to be the tail +of the full_poll_table and have the first entry of the full_poll_table be the +wakeup_fd_r. This means the aiop_poll_table indicies match up exactly with the +aiop_table, but still allows the full_poll_table to have an extra entry. + ******************************************************************************/ /* Forward declarations */ @@ -129,8 +140,31 @@ static void reportPollError(int res, nfds_t nfds) STG_NORETURN; void initCapabilityIOManagerPoll(CapIOManager *iomgr) { initClosureTable(&iomgr->aiop_table, ClosureTableCompact); - iomgr->aiop_poll_table = NULL; iomgr->timeout_queue = emptyTimeoutQueue(); + + newFdWakeup(&iomgr->wakeup_fd_r, &iomgr->wakeup_fd_w); + + iomgr->full_poll_table = stgMallocBytes(sizeof(struct pollfd) /* size 1 */, + "initCapabilityIOManagerPoll"); + iomgr->full_poll_table[0] = (struct pollfd) { + .fd = iomgr->wakeup_fd_r, + .events = POLLIN, + .revents = 0 + }; + iomgr->aiop_poll_table = iomgr->full_poll_table+1; /* hence empty */ +} + + +void freeCapabilityIOManagerPoll(CapIOManager *iomgr) +{ + stgFree(iomgr->full_poll_table); + closeFdWakeup(iomgr->wakeup_fd_r, iomgr->wakeup_fd_w); +} + + +void wakeupIOManagerPoll(CapIOManager *iomgr) +{ + sendFdWakeup(iomgr->wakeup_fd_w); } @@ -275,7 +309,7 @@ static void notifyIOCompletion(CapIOManager *iomgr, StgAsyncIOOp *aiop) } -static void processIOCompletions(CapIOManager *iomgr, int ncompletions) +static bool processIOCompletions(CapIOManager *iomgr, int ncompletions) { /* The scheme we use with poll is that we have a dense poll table, and a * corresponding table that maps to the closure table index. The poll @@ -285,6 +319,19 @@ static void processIOCompletions(CapIOManager *iomgr, int ncompletions) */ debugTrace(DEBUG_iomanager, "processIOCompletions(ncompletions = %d)", ncompletions); + + bool wakeup; + /* If the wakeup_fd_r is ready, collect it */ + if (iomgr->full_poll_table[0].revents) { + ASSERT(iomgr->full_poll_table[0].fd == iomgr->wakeup_fd_r); + collectFdWakeup(iomgr->wakeup_fd_r); + ncompletions--; + wakeup = true; + debugTrace(DEBUG_iomanager, "Received wakeup in poll I/O manager."); + } else { + wakeup = false; + } + struct pollfd *aiop_poll_table = iomgr->aiop_poll_table; int n = ncompletions; int i = 0; @@ -337,11 +384,14 @@ static void processIOCompletions(CapIOManager *iomgr, int ncompletions) i++; } } + return wakeup; } void pollCompletedTimeoutsOrIOPoll(CapIOManager *iomgr) { + ASSERT(iomgr->aiop_poll_table == iomgr->full_poll_table+1); + if (!isEmptyTimeoutQueue(iomgr->timeout_queue)) { Time now = getProcessElapsedTime(); processTimeoutCompletions(iomgr, now); @@ -349,20 +399,20 @@ void pollCompletedTimeoutsOrIOPoll(CapIOManager *iomgr) if (!isEmptyClosureTable(&iomgr->aiop_table)) { - nfds_t nfds = sizeClosureTable(&iomgr->aiop_table); + nfds_t nfds = sizeClosureTable(&iomgr->aiop_table) + 1; /* Poll for I/O readiness, without waiting. */ #if defined(HAVE_DECL_PPOLL) && HAVE_DECL_PPOLL == 1 /* We could use poll here, since we use no timeout, but for consistency we use the same syscall as at the other call site. */ struct timespec tv = (struct timespec) { .tv_sec = 0, .tv_nsec = 0 }; - int res = ppoll(iomgr->aiop_poll_table, nfds, &tv, NULL); + int res = ppoll(iomgr->full_poll_table, nfds, &tv, NULL); debugTrace(DEBUG_iomanager, "ppoll(nfds = %d, timeout.sec = 0, timeout.nsec = 0) = %d", nfds, res); #else - int res = poll(iomgr->aiop_poll_table, nfds, 0); + int res = poll(iomgr->full_poll_table, nfds, 0); debugTrace(DEBUG_iomanager, "poll(nfds = %d, timeout_ms = 0) = %d", @@ -390,6 +440,10 @@ void pollCompletedTimeoutsOrIOPoll(CapIOManager *iomgr) void awaitCompletedTimeoutsOrIOPoll(CapIOManager *iomgr) { + bool wakeup = false; /* got woken up via wakeupIOManager */ + + ASSERT(iomgr->aiop_poll_table == iomgr->full_poll_table+1); + /* Loop until we've woken up some threads. This loop is needed because the * poll() timing isn't accurate, we sometimes sleep for a while but not * long enough to wake up a thread in a threadDelay. Or we may need to @@ -422,9 +476,9 @@ void awaitCompletedTimeoutsOrIOPoll(CapIOManager *iomgr) #endif /* Check for I/O readiness, possibly waiting. */ - nfds_t nfds = sizeClosureTable(&iomgr->aiop_table); + nfds_t nfds = sizeClosureTable(&iomgr->aiop_table) + 1; #if defined(HAVE_DECL_PPOLL) && HAVE_DECL_PPOLL == 1 - int res = ppoll(iomgr->aiop_poll_table, nfds, timeout_ns, NULL); + int res = ppoll(iomgr->full_poll_table, nfds, timeout_ns, NULL); debugTrace(DEBUG_iomanager, "ppoll(nfds = %d, timeout.sec = %d, timeout.nsec = %d) = %d", @@ -432,7 +486,7 @@ void awaitCompletedTimeoutsOrIOPoll(CapIOManager *iomgr) timeout_ns == NULL ? 0 : timeout_ns->tv_nsec, res); #else - int res = poll(iomgr->aiop_poll_table, nfds, timeout_ms); + int res = poll(iomgr->full_poll_table, nfds, timeout_ms); debugTrace(DEBUG_iomanager, "poll(nfds = %d, timeout_ms = %d) = %d", @@ -454,7 +508,7 @@ void awaitCompletedTimeoutsOrIOPoll(CapIOManager *iomgr) } else if (res > 0) { int ncompletions = res; ASSERT(ncompletions <= (int)nfds); - processIOCompletions(iomgr, ncompletions); + wakeup = processIOCompletions(iomgr, ncompletions); } else if (errno == EINTR) { /* We got interrupted by a signal. In the non-threaded RTS, if the @@ -479,6 +533,7 @@ void awaitCompletedTimeoutsOrIOPoll(CapIOManager *iomgr) } } while (emptyRunQueue(iomgr->cap) + && !wakeup && (getSchedState() == SCHED_RUNNING)); } @@ -508,13 +563,17 @@ static bool enlargeTables(CapIOManager *iomgr) bool ok = enlargeClosureTable(iomgr->cap, &iomgr->aiop_table, newcapacity); if (RTS_UNLIKELY(!ok)) return false; - /* Update the auxiliary aiop_poll_table to match */ - struct pollfd *aiop_poll_table; - aiop_poll_table = stgReallocBytes(iomgr->aiop_poll_table, - sizeof(struct pollfd) * newcapacity, - "Poll.c: enlargeTables"); - iomgr->aiop_poll_table = aiop_poll_table; + /* Update the auxiliary aiop_poll_table to match. The full_poll_table is + * one bigger than the aiop_poll_table, since it has an extra entry at the + * front for wakeup_fd_r, with no corresponding aiop. */ + iomgr->full_poll_table = + stgReallocBytes(iomgr->full_poll_table, + sizeof(struct pollfd) * (newcapacity+1), + "Poll.c: enlargeTables"); + iomgr->aiop_poll_table = iomgr->full_poll_table+1; + /* Initialise the new part of the aiop_poll_table */ + struct pollfd *aiop_poll_table = iomgr->aiop_poll_table; for (int i = oldcapacity; i < newcapacity; i++) { aiop_poll_table[i] = (struct pollfd) { .fd = -1, ===================================== rts/posix/Poll.h ===================================== @@ -17,6 +17,8 @@ #if defined(IOMGR_ENABLED_POLL) void initCapabilityIOManagerPoll(CapIOManager *iomgr); +void freeCapabilityIOManagerPoll(CapIOManager *iomgr); +void wakeupIOManagerPoll(CapIOManager *iomgr); /* Synchronous I/O and timer operations */ bool syncIOWaitReadyPoll(CapIOManager *iomgr, StgTSO *tso, ===================================== rts/posix/Select.c ===================================== @@ -22,6 +22,7 @@ #include "IOManagerInternals.h" #include "Stats.h" #include "GetTime.h" +#include "FdWakeup.h" # if defined(HAVE_SYS_SELECT_H) # include <sys/select.h> @@ -54,6 +55,25 @@ #define TimeToLowResTimeRoundUp(t) (t) #endif +void initCapabilityIOManagerSelect(CapIOManager *iomgr) +{ + iomgr->blocked_queue_hd = END_TSO_QUEUE; + iomgr->blocked_queue_tl = END_TSO_QUEUE; + iomgr->sleeping_queue = END_TSO_QUEUE; + + newFdWakeup(&iomgr->wakeup_fd_r, &iomgr->wakeup_fd_w); +} + +void freeCapabilityIOManagerSelect(CapIOManager *iomgr) +{ + closeFdWakeup(iomgr->wakeup_fd_r, iomgr->wakeup_fd_w); +} + +void wakeupIOManagerSelect(CapIOManager *iomgr) +{ + sendFdWakeup(iomgr->wakeup_fd_w); +} + /* * Return the time since the program started, in LowResTime, * rounded down. @@ -225,6 +245,7 @@ awaitCompletedTimeoutsOrIOSelect(CapIOManager *iomgr, bool wait) bool seen_bad_fd = false; struct timeval tv, *ptv; LowResTime now; + bool wakeup = false; /* got woken up via wakeupIOManager */ IF_DEBUG(scheduler, debugBelch("scheduler: checking for threads blocked on I/O"); @@ -252,6 +273,13 @@ awaitCompletedTimeoutsOrIOSelect(CapIOManager *iomgr, bool wait) FD_ZERO(&rfd); FD_ZERO(&wfd); + /* We're always interested in our wakeup fd */ + { + int fd = iomgr->wakeup_fd_r; + maxfd = (fd > maxfd) ? fd : maxfd; + FD_SET(fd, &rfd); + } + for(tso = iomgr->blocked_queue_hd; tso != END_TSO_QUEUE; tso = next) { @@ -376,6 +404,13 @@ awaitCompletedTimeoutsOrIOSelect(CapIOManager *iomgr, bool wait) } } + /* If the wakeup_fd_r is ready, collect it */ + if (FD_ISSET(iomgr->wakeup_fd_r, &rfd)) { + collectFdWakeup(iomgr->wakeup_fd_r); + wakeup = true; + debugTrace(DEBUG_iomanager, "Received wakeup in select I/O manager."); + } + /* Step through the waiting queue, unblocking every thread that now has * a file descriptor in a ready state. */ @@ -458,7 +493,8 @@ awaitCompletedTimeoutsOrIOSelect(CapIOManager *iomgr, bool wait) } } while (wait && getSchedState() == SCHED_RUNNING - && emptyRunQueue(iomgr->cap)); + && emptyRunQueue(iomgr->cap) + && !wakeup); } #endif /* IOMGR_ENABLED_SELECT */ ===================================== rts/posix/Select.h ===================================== @@ -15,6 +15,10 @@ typedef StgWord LowResTime; LowResTime getDelayTarget (HsInt us); +void initCapabilityIOManagerSelect(CapIOManager *iomgr); +void freeCapabilityIOManagerSelect(CapIOManager *iomgr); +void wakeupIOManagerSelect(CapIOManager *iomgr); + void awaitCompletedTimeoutsOrIOSelect(CapIOManager *iomgr, bool wait); #include "EndPrivate.h" ===================================== rts/rts.cabal ===================================== @@ -569,6 +569,7 @@ library wasm/OSThreads.c wasm/JSFFI.c wasm/JSFFIGlobals.c + posix/FdWakeup.c posix/Select.c posix/Poll.c posix/Timeout.c @@ -581,6 +582,7 @@ library posix/Ticker.c posix/OSMem.c posix/OSThreads.c + posix/FdWakeup.c posix/MIO.c posix/Poll.c posix/Select.c View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/24a403ed5992b638b5f08b99a8fa58… -- View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/24a403ed5992b638b5f08b99a8fa58… You're receiving this email because of your account on gitlab.haskell.org.
1 0
0 0
[Git][ghc/ghc][wip/andreask/hadrian_build_par] hadrian/build-cabal: Better respect and utilize -j
by Andreas Klebinger (@AndreasK) 19 Feb '26

19 Feb '26
Andreas Klebinger pushed to branch wip/andreask/hadrian_build_par at Glasgow Haskell Compiler / GHC Commits: b65ce27c by Andreas Klebinger at 2026-02-19T09:27:54+01:00 hadrian/build-cabal: Better respect and utilize -j * We now respect -j<n> for the cabal invocation to build hadrian rather than hardcoding -j * We use the --semaphore flag to ensure cabal/ghc build the hadrian executable in parallel using the -jsem mechanism. Saves 10-15s on fresh builds for me. Fixes #26876 - - - - - 1 changed file: - hadrian/build-cabal Changes: ===================================== hadrian/build-cabal ===================================== @@ -23,9 +23,52 @@ fi CABVERSTR=$("$CABAL" --numeric-version) CABVER=( ${CABVERSTR//./ } ) +THREADS="-j1" +GC_THREADS="" +SEMAPHORE="" + +echo "$@" + +# Try building hadrian in parallel. We check for -j<n>. +# If threads > 1 we pass --semaphore to allow ghc to build more than one module in parallel +# If threads > 4 we pass -qn as higher parallel gc thread counts can lead to slow downs +# We only do any of thise for cabal >= 3.14, because I don't trust older versions to handle --semaphore right +if [ "${CABVER[0]}" -gt 3 ] || [ "${CABVER[0]}" -eq 3 -a "${CABVER[1]}" -ge 14 ]; +then + + for arg in "$@"; do + case "$arg" in + -j) + GC_THREADS="-qn4" + SEMAPHORE="--semaphore" + THREADS="-j" + ;; + -j[0-9]*) + threads="${arg#-j}" + if [[ "$threads" =~ ^[0-9]+$ ]] && [ "$threads" -ne 0 ]; then + THREADS="-j${threads}" + if [ $threads -ge 4 ]; then + GC_THREADS="-qn4" + fi + if [ $threads -gt 1 ]; then + SEMAPHORE="--semaphore" + fi + fi + ;; + esac + + done + +fi + +if [ "$(uname -s)" = "FreeBSD" ]; then + # Can't rely on posix semaphore support in free bsd. + SEMAPHORE="" +fi + if [ "${CABVER[0]}" -gt 2 -o "${CABVER[0]}" -eq 2 -a "${CABVER[1]}" -ge 2 ]; then - "$CABAL" --project-file="$PROJ" new-build "${CABFLAGS[@]}" -j exe:hadrian + "$CABAL" --project-file="$PROJ" new-build "${CABFLAGS[@]}" ${THREADS} ${SEMAPHORE} --ghc-options="+RTS ${GC_THREADS} -RTS" exe:hadrian # use new-exec instead of new-run to make sure that the build-tools (alex & happy) are in PATH "$CABAL" --project-file="$PROJ" new-exec "${CABFLAGS[@]}" hadrian -- \ --directory "$PWD" \ View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/b65ce27c0b28e5853535b6c3944a62b… -- View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/b65ce27c0b28e5853535b6c3944a62b… You're receiving this email because of your account on gitlab.haskell.org.
1 0
0 0
[Git][ghc/ghc][wip/romes/hadrian-cross-stage2-rebase_SVEN_FINAL] Clarify the interpreter story
by Sven Tennie (@supersven) 18 Feb '26

18 Feb '26
Sven Tennie pushed to branch wip/romes/hadrian-cross-stage2-rebase_SVEN_FINAL at Glasgow Haskell Compiler / GHC Commits: 30145457 by Sven Tennie at 2026-02-19T08:56:34+01:00 Clarify the interpreter story - - - - - 3 changed files: - hadrian/src/Rules/Generate.hs - hadrian/src/Settings/Packages.hs - hadrian/src/Settings/Program.hs Changes: ===================================== hadrian/src/Rules/Generate.hs ===================================== @@ -491,7 +491,7 @@ generateSettings settingsFile = do settings <- traverse sequence $ [ ("unlit command", ("$topdir/../bin/" <>) <$> expr (programName (ctx { Context.package = unlit, Context.stage = predStage stage }))) - , ("Use interpreter", expr $ yesNo <$> ghcWithInterpreter (predStage stage)) + , ("Use interpreter", expr $ yesNo <$> ghcWithInterpreter stage) -- Hard-coded as Cabal queries these to determine way support and we -- need to always advertise all ways when bootstrapping. -- The settings file is generated at install time when installing a bindist. ===================================== hadrian/src/Settings/Packages.hs ===================================== @@ -8,7 +8,7 @@ import Oracles.Flag import Packages import Settings import Settings.Builders.Common (wayCcArgs) -import Settings.Program (ghcWithInterpreter) +import Settings.Program (ghcWithInterpreter, ghcWithInternalInterpreter) import qualified GHC.Toolchain.Library as Lib import GHC.Toolchain.Target @@ -27,9 +27,12 @@ packageArgs = do cross = flag CrossCompiling haveCurses = any (/= "") <$> traverse (`buildSetting` stage) [ CursesIncludeDir, CursesLibDir ] + -- Check if the bootstrap compiler has the same version as the one we + -- are building. This is used to build cross-compilers + bootCross = (==) <$> ghcVersionStage (stage0InTree) <*> ghcVersionStage Stage1 + compilerStageOption f = buildingCompilerStage' . f =<< expr flavour - isCrossStage <- staged crossStage cursesIncludeDir <- staged (buildSetting CursesIncludeDir) cursesLibraryDir <- staged (buildSetting CursesLibDir) ffiIncludeDir <- staged (buildSetting FfiIncludeDir) @@ -88,7 +91,7 @@ packageArgs = do -- on the host and must rely on external interpreter to load -- target code, otherwise enable for stage2 since that runs on -- the target and can use target's own ghci object linker - [ expr (ghcWithInterpreter stage) `cabalFlag` "internal-interpreter" + [ ghcWithInterpreter stage `cabalFlag` "internal-interpreter" , orM [ notM cross, haveCurses ] `cabalFlag` "terminfo" , arg "-build-tool-depends" , staged (buildFlag UseLibzstd) `cabalFlag` "with-libzstd" @@ -110,8 +113,8 @@ packageArgs = do , compilerStageOption ghcDebugAssertions ? arg "-DDEBUG" ] , builder (Cabal Flags) ? mconcat - [ expr (ghcWithInterpreter stage) `cabalFlag` "interpreter" - , andM [expr (ghcWithInterpreter stage), notM (pure isCrossStage)] `cabalFlag` "internal-interpreter" + [ ghcWithInterpreter stage `cabalFlag` "interpreter" + , ghcWithInternalInterpreter stage `cabalFlag` "internal-interpreter" , ifM stage0 -- We build a threaded stage 1 if the bootstrapping compiler -- supports it. @@ -162,10 +165,11 @@ packageArgs = do -- builder (Cabal Setup) ? cabalExtraDirs ffiIncludeDir ffiLibraryDir , builder (Cabal Flags) ? mconcat - [ stage0 `cabalFlag` "bootstrap", - andM [expr (ghcWithInterpreter stage), notM (pure isCrossStage)] `cabalFlag` "internal-interpreter" + [ ifM stage0 + (andM [cross, bootCross] `cabalFlag` "internal-interpreter") + (arg "internal-interpreter") + , stage0 `cabalFlag` "bootstrap" ] - ] , package unix ? builder (Cabal Flags) ? arg "+os-string" ===================================== hadrian/src/Settings/Program.hs ===================================== @@ -1,7 +1,9 @@ module Settings.Program ( programContext , ghcWithInterpreter - ) where + , archSupportsInternalInterpreter + , ghcWithInternalInterpreter +) where import Base import Context @@ -9,7 +11,7 @@ import Oracles.Flavour import Packages import GHC.Platform.ArchOS -import Settings.Builders.Common (anyTargetOs, anyTargetArch, isArmTarget) +import Settings.Builders.Common (anyTargetOs, anyTargetArch, isArmTarget, crossStage) -- TODO: there is duplication and inconsistency between this and -- Rules.Program.getProgramContexts. There should only be one way to @@ -39,6 +41,18 @@ programContext stage pkg = do -- Also checks whether the target supports GHCi. ghcWithInterpreter :: Stage -> Action Bool ghcWithInterpreter stage = do + hasInternalInterpreter <- ghcWithInternalInterpreter stage + isCross <- crossStage stage + return $ (hasInternalInterpreter || isCross) && (stage >= Stage1) + +ghcWithInternalInterpreter :: Stage -> Action Bool +ghcWithInternalInterpreter stage = do + hasInternalInterpreter <- archSupportsInternalInterpreter stage + isCross <- crossStage stage + return $ hasInternalInterpreter && not isCross && (stage >= Stage1) + +archSupportsInternalInterpreter :: Stage -> Action Bool +archSupportsInternalInterpreter stage = do goodOs <- anyTargetOs stage [ OSMinGW32, OSLinux, OSSolaris2 , OSFreeBSD, OSDragonFly, OSNetBSD, OSOpenBSD , OSDarwin, OSKFreeBSD @@ -56,4 +70,4 @@ ghcWithInterpreter stage = do -- fall back on dynamic linking: dynamicGhcProgs <- askDynGhcPrograms stage - return $ ((goodOs && goodArch) || dynamicGhcProgs) && (stage >= Stage2) + return $ (goodOs && goodArch) || dynamicGhcProgs View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/30145457b935d819b56aded50c4c05a… -- View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/30145457b935d819b56aded50c4c05a… You're receiving this email because of your account on gitlab.haskell.org.
1 0
0 0
[Git][ghc/ghc][wip/andreask/bump_shake] 32 commits: NCG/LA64: adjust register usage to avoid src-register being clobbered
by Andreas Klebinger (@AndreasK) 18 Feb '26

18 Feb '26
Andreas Klebinger pushed to branch wip/andreask/bump_shake at Glasgow Haskell Compiler / GHC Commits: 2e39a340 by Peng Fan at 2026-02-07T03:42:01-05:00 NCG/LA64: adjust register usage to avoid src-register being clobbered - - - - - 9faf1b35 by Teo Camarasu at 2026-02-07T03:42:43-05:00 ghc-internal: Delete unnecessary GHC.Internal.Data.Ix This module merely re-exports GHC.Internal.Ix. It was copied from `base` when `ghc-internal` was split, but there is no reason to have this now. So, let's delete it. Resolves #26848 - - - - - d112b440 by Sven Tennie at 2026-02-07T10:47:56-05:00 Add cabal.project file to generate-ci This fixes the HLS setup for our CI code generation script (generate-ci). The project file simply makes `generate-ci` of the cabal file discoverable. - - - - - 5339f6f0 by Andreas Klebinger at 2026-02-07T10:48:40-05:00 CI: Don't collapse test results. This puts test output back into the primary test log instead of a subsection removing the need to expand a section to see test results. While the intention was good in practice the old behaviour mostly wastes time by requiring expansion of the section. Fixes #26882 - - - - - 0e1cd2e0 by Evan Piro at 2026-02-08T10:35:16-08:00 Linker.MacOS reduce dynflags import - - - - - 1c79a4cd by Michael Alan Dorman at 2026-02-09T08:11:51-05:00 Remove `extra_src_files` variable from `testsuite/driver/testlib.py` While reading through the test harness code, I noticed this variable with a TODO attached that referenced #12223. Although that bug is closed, it strongly implied that this special-case variable that only affected a single test was expected to be removed at some point. I also looked at 3415bcaa0b1903b5e12dfaadb5b774718e406eab---where it was added---whose commit message suggested that it would have been desirable to remove it, but that there were special circumstances that meant it had to remain (though it doesn't elucidate what those special circumstances are). However, the special circumstances were mentioned as if the test was in a different location than is currently is, so I decided to try changing the test to use the standard `extra_files` mechanism, which works in local testing. This also seems like a reasonable time to remove the script that was originally used in the transition, since it doesn't really serve a purpose anymore. - - - - - 0020e38a by Matthew Pickering at 2026-02-09T17:29:14-05:00 determinism: Use a stable sort in WithHsDocIdentifiers binary instance `WithHsDocIdentifiers` is defined as ``` 71 data WithHsDocIdentifiers a pass = WithHsDocIdentifiers 72 { hsDocString :: !a 73 , hsDocIdentifiers :: ![Located (IdP pass)] 74 } ``` This list of names is populated from `rnHsDocIdentifiers`, which calls `lookupGRE`, which calls `lookupOccEnv_AllNameSpaces`, which calls `nonDetEltsUFM` and returns the results in an order depending on uniques. Sorting the list with a stable sort before returning the interface makes the output deterministic and follows the approach taken by other fields in `Docs`. Fixes #26858 - - - - - 89898ce6 by echoumcp1 at 2026-02-09T17:30:01-05:00 Replace putstrln with logMsg in handleSeqHValueStatus Fixes #26549 - - - - - 7c52c4f9 by John Paul Adrian Glaubitz at 2026-02-10T13:52:43-05:00 rts: Switch prim to use modern atomic compiler builtins The __sync_*() atomic compiler builtins have been deprecated in GCC for a while now and also don't provide variants for 64-bit values such as __sync_fetch_and_add_8(). Thus, replace them with the modern __atomic_*() compiler builtins and while we're at it, also drop the helper macro CAS_NAND() which is now no longer needed since we stopped using the __sync_*() compiler builtins altogether. Co-authored-by: Ilias Tsitsimpis <iliastsi(a)debian.org> Fixes #26729 - - - - - cf60850a by Recursion Ninja at 2026-02-10T13:53:27-05:00 Decoupling L.H.S.Decls from GHC.Types.ForeignCall - Adding TTG extension point for 'CCallTarget' - Adding TTG extension point for 'CType' - Adding TTG extension point for 'Header' - Moving ForeignCall types that do not need extension to new L.H.S.Decls.Foreign module - Replacing 'Bool' parameters with descriptive data-types to increase clairty and prevent "Boolean Blindness" - - - - - 11a04cbb by Eric Lee at 2026-02-11T09:20:46-05:00 Derive Semigroup/Monoid for instances believed could be derived in #25871 - - - - - 15d9ce44 by Eric Lee at 2026-02-11T09:20:46-05:00 add Ghc.Data.Pair deriving - - - - - c85dc170 by Evan Piro at 2026-02-11T09:21:45-05:00 Linker.MacOS reduce options import - - - - - a541dd83 by Chris Wendt at 2026-02-11T16:06:41-05:00 Initialize plugins for `:set +c` in GHCi Fixes #23110. - - - - - 0f5a73bc by Cheng Shao at 2026-02-11T16:07:27-05:00 compiler: add Binary Text instance This patch adds `Binary` instance for strict `Text`, in preparation of making `Text` usable in certain GHC API use cases (e.g. haddock). This also introduces `text` as a direct dependency of the `ghc` package. - - - - - 9e58b8a1 by Cheng Shao at 2026-02-11T16:08:10-05:00 ghc-toolchain: add C11 check This patch partially reverts commit b8307eab80c5809df5405d76c822bf86877f5960 that removed C99 check in autoconf/ghc-toolchain. Now we: - No longer re-implement `FP_SET_CFLAGS_C11` similar to `FP_SET_CFLAGS_C99` in the past, since autoconf doesn't provide a convenient `AC_PROG_CC_C11` function. ghc-toolchain will handle it anyway. - The Cmm CPP C99 check is relanded and repurposed for C11. - The C99 logic in ghc-toolchain is relanded and repurposed for C11. - The C99 check in Stg.h is corrected to check for C11. The obsolete _ISOC99_SOURCE trick is dropped. - Usages of `-std=gnu99` in the testsuite are corrected to use `-std=gnu11`. Closes #26908. - - - - - 4df0adf6 by Simon Peyton Jones at 2026-02-11T21:50:13-05:00 Simplify the treatment of static forms This MR implements GHC proposal 732: simplify static forms, https://github.com/ghc-proposals/ghc-proposals/pull/732 thereby addressing #26556. See `Note [Grand plan for static forms]` in GHC.Iface.Tidy.StaticPtrTable The main changes are: * There is a new, simple rule for (static e), namely that the free term variables of `e` must be bound at top level. The check is done in the `HsStatic` case of `GHC.Rename.Expr.rnExpr` * That in turn substantially simplifies the info that the typechecker carries around in its type environment. Hooray. * The desugarer emits static bindings to top level directly; see the `HsStatic` case of `dsExpr`. * There is no longer any special static-related magic in the FloatOut pass. And the main Simplifier pipeline no longer needs a special case to run FloatOut even with -O0. Hooray. All this forced an unexpected change to the pattern match checker. It recursively invokes the main Hs desugarer when it wants to take a look at a term to spot some special cases (notably constructor applications). We don't want to emit any nested (static e) bindings to top level a second time! Yikes. That forced a modest refactor in GHC.HsToCore.Pmc: * The `dsl_nablas` field of `DsLclEnv` now has a `NoPmc` case, which says "I'm desugaring just for pattern-match checking purposes". * When that flag is set we don't emit static binds. That in turn forces a cascade of refactoring, but the net effect is an improvement; less risk of duplicated (even exponential?) work. See Note [Desugaring HsExpr during pattern-match checking]. 10% metric decrease, on some architectures, of compile-time max-bytes-used on T15304. Metric Decrease: T15304 - - - - - 7922f728 by Teo Camarasu at 2026-02-11T21:50:58-05:00 ghc-internal: avoid depending on GHC.Internal.Exts This module is mostly just re-exports. It made sense as a user-facing module, but there's no good reason ghc-internal modules should depend on it and doing so linearises the module graph - move considerAccessible to GHC.Internal.Magic Previously it lived in GHC.Internal.Exts, but it really deserves to live along with the other magic function, which are already re-exported from .Exts - move maxTupleSize to GHC.Internal.Tuple This previously lived in GHC.Internal.Exts but a comment already said it should be moved to .Tuple Resolves #26832 - - - - - b6a4a29b by Eric Lee at 2026-02-11T21:51:55-05:00 Remove unused Semigroup imports to fix GHC 9.14 bootstrapping - - - - - 99d8c146 by Simon Peyton Jones at 2026-02-12T17:36:59+00:00 Fix subtle bug in cast worker/wrapper See (CWw4) in Note [Cast worker/wrapper]. The true payload is in the change to the definition of GHC.Types.Id.Info.hasInlineUnfolding Everthing else is just documentation. There is a 2% compile time decrease for T13056; I'll take the win! Metric Decrease: T13056 - - - - - 530e8e58 by Simon Peyton Jones at 2026-02-12T20:17:23-05:00 Add regression tests for four StaticPtr bugs Tickets #26545, #24464, #24773, #16981 are all solved by the recently-landed MR commit 318ee13bcffa6aa8df42ba442ccd92aa0f7e210c Author: Simon Peyton Jones <simon.peytonjones(a)gmail.com> Date: Mon Oct 20 23:07:20 2025 +0100 Simplify the treatment of static forms This MR just adds regression tests for them. - - - - - 4157160f by Cheng Shao at 2026-02-13T06:27:04-05:00 ci: remove unused hlint-ghc-and-base job definition This patch removes the unused `hlint-ghc-and-base` job definition, it's never run since !9806. Note that hadrian lint rules still work locally, so anyone that wishes to run hlint on the codebase can continue to do so in their local worktree. - - - - - 039f1977 by Cheng Shao at 2026-02-13T06:27:47-05:00 wasm: use import.meta.main for proper distinction of nodejs main modules This patch uses `import.meta.main` for proper distinction of nodejs main modules, especially when the main module might be installed as a symlink. Fixes #26916. - - - - - 14f485ee by ARATA Mizuki at 2026-02-17T09:09:24+09:00 Support more x86 extensions: AVX-512 {BW,DQ,VL} and GFNI Also, mark AVX-512 ER and PF as deprecated. AVX-512 instructions can be used for certain 64-bit integer vector operations. GFNI can be used to implement bitReverse (currently not used by NCG, but LLVM may use it). Closes #26406 Addresses #26509 - - - - - 016f79d5 by fendor at 2026-02-17T09:16:16-05:00 Hide implementation details from base exception stack traces Ensure we hide the implementation details of the exception throwing mechanisms: * `undefined` * `throwSTM` * `throw` * `throwIO` * `error` The `HasCallStackBacktrace` should always have a length of exactly 1, not showing internal implementation details in the stack trace, as these are vastly distracting to end users. CLC proposal [#387](https://github.com/haskell/core-libraries-committee/issues/387) - - - - - 4f2840f2 by Brian J. Cardiff at 2026-02-17T17:04:08-05:00 configure: Accept happy-2.2 In Jan 2026 happy-2.2 was released. The most sensible change is https://github.com/haskell/happy/issues/335 which didn't trigger in a fresh build - - - - - 10b4d364 by Duncan Coutts at 2026-02-17T17:04:52-05:00 Fix errors in the documentation of the eventlog STOP_THREAD status codes Fix the code for BlockedOnMsgThrowTo. Document all the known historical warts. Fixes issue #26867 - - - - - c5e15b8b by Phil de Joux at 2026-02-18T05:07:36-05:00 haddock: use snippets for all list examples - generate snippet output for docs - reduce font size to better fit snippets - Use only directive to guard html snippets - Add latex snippets for lists - - - - - d388bac1 by Phil de Joux at 2026-02-18T05:07:36-05:00 haddock: Place the snippet input and output together - Put the output seemingly inside the example box - - - - - 016fa306 by Samuel Thibault at 2026-02-18T05:08:35-05:00 Fix linking against libm by moving the -lm option For those systems that need -lm for getting math functions, this is currently added on the link line very early, before the object files being linked together. Newer toolchains enable --as-needed by default, which means -lm is ignored at that point because no object requires a math function yet. With such toolchains, we thus have to add -lm after the objects, so the linker actually includes libm in the link. - - - - - 68bd0805 by Teo Camarasu at 2026-02-18T05:09:19-05:00 ghc-internal: Move GHC.Internal.Data.Bool to base This is a tiny module that only defines bool :: Bool -> a -> a -> a. We can just move this to base and delete it from ghc-internal. If we want this functionality there we can just use a case statement or if-then expression. Resolves 26865 - - - - - ae35950b by Andreas Klebinger at 2026-02-19T08:56:05+01:00 Bump hackage index state and minimum shake version for hadrian. We also add the shake version we want to stack.yaml Fixes #26884 - - - - - 276 changed files: - .gitlab-ci.yml - .gitlab/ci.sh - + .gitlab/generate-ci/cabal.project - compiler/GHC/Builtin/Names.hs - compiler/GHC/Builtin/Types.hs - compiler/GHC/CmmToAsm/Config.hs - compiler/GHC/CmmToAsm/LA64/CodeGen.hs - compiler/GHC/CmmToAsm/X86/CodeGen.hs - compiler/GHC/CmmToAsm/X86/Instr.hs - compiler/GHC/CmmToAsm/X86/Ppr.hs - compiler/GHC/CmmToLlvm/CodeGen.hs - compiler/GHC/Core/Lint.hs - compiler/GHC/Core/Opt/Pipeline.hs - compiler/GHC/Core/Opt/SetLevels.hs - compiler/GHC/Core/Opt/Simplify/Iteration.hs - compiler/GHC/Core/Opt/WorkWrap.hs - compiler/GHC/Core/TyCon.hs - compiler/GHC/CoreToStg.hs - compiler/GHC/CoreToStg/AddImplicitBinds.hs - compiler/GHC/Data/Pair.hs - compiler/GHC/Driver/Config/CmmToAsm.hs - compiler/GHC/Driver/Config/Core/Lint.hs - compiler/GHC/Driver/DynFlags.hs - compiler/GHC/Driver/Pipeline/Execute.hs - compiler/GHC/Driver/Session.hs - compiler/GHC/Hs/Binds.hs - compiler/GHC/Hs/Decls.hs - compiler/GHC/Hs/Doc.hs - compiler/GHC/Hs/Expr.hs - compiler/GHC/Hs/Instances.hs - compiler/GHC/Hs/Syn/Type.hs - compiler/GHC/Hs/Type.hs - compiler/GHC/Hs/Utils.hs - compiler/GHC/HsToCore.hs - compiler/GHC/HsToCore/Docs.hs - compiler/GHC/HsToCore/Errors/Types.hs - compiler/GHC/HsToCore/Expr.hs - compiler/GHC/HsToCore/Foreign/C.hs - compiler/GHC/HsToCore/Foreign/Call.hs - compiler/GHC/HsToCore/Foreign/Decl.hs - compiler/GHC/HsToCore/Foreign/JavaScript.hs - compiler/GHC/HsToCore/Foreign/Utils.hs - compiler/GHC/HsToCore/Foreign/Wasm.hs - compiler/GHC/HsToCore/GuardedRHSs.hs - compiler/GHC/HsToCore/Match.hs - compiler/GHC/HsToCore/Monad.hs - compiler/GHC/HsToCore/Pmc.hs - compiler/GHC/HsToCore/Pmc/Desugar.hs - compiler/GHC/HsToCore/Pmc/Solver.hs - compiler/GHC/HsToCore/Pmc/Solver/Types.hs - compiler/GHC/HsToCore/Quote.hs - compiler/GHC/HsToCore/Ticks.hs - compiler/GHC/HsToCore/Types.hs - compiler/GHC/Iface/Ext/Ast.hs - compiler/GHC/Iface/Syntax.hs - compiler/GHC/Iface/Tidy.hs - compiler/GHC/Iface/Tidy/StaticPtrTable.hs - compiler/GHC/Linker/Dynamic.hs - compiler/GHC/Linker/MacOS.hs - compiler/GHC/Parser.y - compiler/GHC/Parser/PostProcess.hs - compiler/GHC/Parser/PostProcess/Haddock.hs - compiler/GHC/Rename/Bind.hs - compiler/GHC/Rename/Expr.hs - compiler/GHC/Rename/Module.hs - compiler/GHC/Runtime/Heap/Inspect.hs - compiler/GHC/Runtime/Interpreter.hs - compiler/GHC/StgToByteCode.hs - compiler/GHC/StgToCmm/Foreign.hs - compiler/GHC/StgToJS/FFI.hs - compiler/GHC/SysTools/Cpp.hs - compiler/GHC/Tc/Deriv.hs - compiler/GHC/Tc/Errors/Ppr.hs - compiler/GHC/Tc/Errors/Types.hs - compiler/GHC/Tc/Gen/Bind.hs - compiler/GHC/Tc/Gen/Expr.hs - compiler/GHC/Tc/Gen/Foreign.hs - compiler/GHC/Tc/Gen/Head.hs - compiler/GHC/Tc/Gen/Pat.hs - compiler/GHC/Tc/Gen/Sig.hs - compiler/GHC/Tc/Module.hs - compiler/GHC/Tc/Solver.hs - compiler/GHC/Tc/Solver/Monad.hs - compiler/GHC/Tc/TyCl.hs - compiler/GHC/Tc/TyCl/Instance.hs - compiler/GHC/Tc/TyCl/Utils.hs - compiler/GHC/Tc/Types.hs - compiler/GHC/Tc/Types/BasicTypes.hs - compiler/GHC/Tc/Types/Constraint.hs - compiler/GHC/Tc/Types/ErrCtxt.hs - compiler/GHC/Tc/Types/Origin.hs - compiler/GHC/Tc/Utils/Env.hs - compiler/GHC/Tc/Utils/Monad.hs - compiler/GHC/Tc/Utils/TcMType.hs - − compiler/GHC/Tc/Utils/TcMType.hs-boot - compiler/GHC/Tc/Zonk/TcType.hs - compiler/GHC/Tc/Zonk/Type.hs - compiler/GHC/ThToHs.hs - compiler/GHC/Types/ForeignCall.hs - compiler/GHC/Types/Id/Info.hs - compiler/GHC/Types/Id/Make.hs - compiler/GHC/Types/Unique/DSet.hs - compiler/GHC/Utils/Binary.hs - compiler/GHC/Utils/Ppr/Colour.hs - compiler/Language/Haskell/Syntax/Decls.hs - + compiler/Language/Haskell/Syntax/Decls/Foreign.hs - compiler/Language/Haskell/Syntax/Extension.hs - compiler/ghc.cabal.in - configure.ac - distrib/configure.ac.in - docs/users_guide/9.16.1-notes.rst - docs/users_guide/eventlog-formats.rst - docs/users_guide/phases.rst - docs/users_guide/using.rst - ghc/GHCi/UI/Info.hs - hadrian/cabal.project - hadrian/hadrian.cabal - hadrian/stack.yaml - libraries/base/changelog.md - libraries/base/src/Data/Bool.hs - libraries/base/src/Data/Ix.hs - libraries/base/src/Data/List.hs - libraries/base/src/Data/List/NubOrdSet.hs - libraries/base/src/GHC/Exts.hs - libraries/ghc-internal/ghc-internal.cabal.in - − libraries/ghc-internal/src/GHC/Internal/Data/Bool.hs - libraries/ghc-internal/src/GHC/Internal/Data/Foldable.hs - libraries/ghc-internal/src/GHC/Internal/Data/Function.hs - − libraries/ghc-internal/src/GHC/Internal/Data/Ix.hs - libraries/ghc-internal/src/GHC/Internal/Data/Type/Bool.hs - libraries/ghc-internal/src/GHC/Internal/Data/Type/Ord.hs - libraries/ghc-internal/src/GHC/Internal/Data/Version.hs - libraries/ghc-internal/src/GHC/Internal/Exception.hs - libraries/ghc-internal/src/GHC/Internal/Exts.hs - libraries/ghc-internal/src/GHC/Internal/Heap/Closures.hs - libraries/ghc-internal/src/GHC/Internal/IO/FD.hs - libraries/ghc-internal/src/GHC/Internal/JS/Foreign/Callback.hs - libraries/ghc-internal/src/GHC/Internal/JS/Prim.hs - libraries/ghc-internal/src/GHC/Internal/JS/Prim/Internal/Build.hs - libraries/ghc-internal/src/GHC/Internal/Magic.hs - libraries/ghc-internal/src/GHC/Internal/STM.hs - libraries/ghc-internal/src/GHC/Internal/Stack/Decode.hs - libraries/ghc-internal/src/GHC/Internal/System/IO/OS.hs - libraries/ghc-internal/src/GHC/Internal/TH/Lift.hs - libraries/ghc-internal/src/GHC/Internal/Tuple.hs - libraries/ghc-internal/src/GHC/Internal/TypeError.hs - libraries/ghc-internal/src/GHC/Internal/Wasm/Prim/Exports.hs - libraries/ghc-internal/src/GHC/Internal/Wasm/Prim/Imports.hs - libraries/ghc-internal/src/GHC/Internal/Wasm/Prim/Types.hs - + libraries/ghc-internal/tests/backtraces/T15395.hs - + libraries/ghc-internal/tests/backtraces/T15395.stdout - libraries/ghc-internal/tests/backtraces/all.T - libraries/ghc-internal/tests/stack-annotation/ann_frame005.stdout - m4/fp_cmm_cpp_cmd_with_args.m4 - m4/fptools_happy.m4 - rts/include/Stg.h - rts/prim/atomic.c - testsuite/driver/cpu_features.py - − testsuite/driver/kill_extra_files.py - testsuite/driver/testlib.py - testsuite/tests/arrows/should_compile/T21301.stderr - testsuite/tests/codeGen/should_gen_asm/all.T - + testsuite/tests/codeGen/should_gen_asm/avx512-int64-minmax.asm - + testsuite/tests/codeGen/should_gen_asm/avx512-int64-minmax.hs - + testsuite/tests/codeGen/should_gen_asm/avx512-int64-mul.asm - + testsuite/tests/codeGen/should_gen_asm/avx512-int64-mul.hs - + testsuite/tests/codeGen/should_gen_asm/avx512-word64-minmax.asm - + testsuite/tests/codeGen/should_gen_asm/avx512-word64-minmax.hs - testsuite/tests/codeGen/should_run/CgStaticPointers.hs - testsuite/tests/codeGen/should_run/CgStaticPointersNoFullLazyness.hs - testsuite/tests/count-deps/CountDepsAst.stdout - testsuite/tests/count-deps/CountDepsParser.stdout - testsuite/tests/deSugar/should_fail/DsStrictFail.stderr - testsuite/tests/deSugar/should_run/T20024.stderr - testsuite/tests/deSugar/should_run/dsrun005.stderr - testsuite/tests/deSugar/should_run/dsrun007.stderr - testsuite/tests/deSugar/should_run/dsrun008.stderr - testsuite/tests/deriving/should_run/T9576.stderr - testsuite/tests/ffi/should_run/all.T - testsuite/tests/ghci/scripts/Defer02.stderr - testsuite/tests/ghci/scripts/T15325.stderr - testsuite/tests/ghci/scripts/T20150.stdout - testsuite/tests/interface-stability/base-exports.stdout - testsuite/tests/interface-stability/base-exports.stdout-javascript-unknown-ghcjs - testsuite/tests/interface-stability/base-exports.stdout-mingw32 - testsuite/tests/interface-stability/base-exports.stdout-ws-32 - testsuite/tests/interface-stability/ghc-experimental-exports.stdout - testsuite/tests/interface-stability/ghc-experimental-exports.stdout-mingw32 - testsuite/tests/interface-stability/ghc-prim-exports.stdout - testsuite/tests/interface-stability/ghc-prim-exports.stdout-mingw32 - testsuite/tests/parser/should_compile/DumpRenamedAst.stderr - testsuite/tests/parser/should_compile/T14189.stderr - testsuite/tests/patsyn/should_run/ghci.stderr - + testsuite/tests/plugins/T23110.hs - + testsuite/tests/plugins/T23110.script - + testsuite/tests/plugins/T23110.stdout - testsuite/tests/plugins/all.T - testsuite/tests/process/all.T - testsuite/tests/quotes/LiftErrMsgDefer.stderr - testsuite/tests/rename/should_fail/RnStaticPointersFail01.stderr - testsuite/tests/rename/should_fail/RnStaticPointersFail03.stderr - + testsuite/tests/rename/should_fail/T26545.hs - + testsuite/tests/rename/should_fail/T26545.stderr - testsuite/tests/rename/should_fail/all.T - testsuite/tests/safeHaskell/safeLanguage/SafeLang15.stderr - testsuite/tests/showIface/DocsInHiFile1.stdout - testsuite/tests/showIface/HaddockSpanIssueT24378.stdout - testsuite/tests/showIface/MagicHashInHaddocks.stdout - testsuite/tests/simd/should_run/all.T - + testsuite/tests/simplCore/should_compile/T26903.hs - + testsuite/tests/simplCore/should_compile/T26903.stderr - testsuite/tests/simplCore/should_compile/T8331.stderr - testsuite/tests/simplCore/should_compile/all.T - testsuite/tests/type-data/should_run/T22332a.stderr - + testsuite/tests/typecheck/should_compile/T24464.hs - testsuite/tests/typecheck/should_compile/all.T - testsuite/tests/typecheck/should_run/T10284.stderr - testsuite/tests/typecheck/should_run/T13838.stderr - + testsuite/tests/typecheck/should_run/T16981.hs - + testsuite/tests/typecheck/should_run/T16981.stdout - + testsuite/tests/typecheck/should_run/T24773.hs - + testsuite/tests/typecheck/should_run/T24773.stdout - testsuite/tests/typecheck/should_run/T9497a-run.stderr - testsuite/tests/typecheck/should_run/T9497b-run.stderr - testsuite/tests/typecheck/should_run/T9497c-run.stderr - testsuite/tests/typecheck/should_run/all.T - testsuite/tests/unsatisfiable/T23816.stderr - testsuite/tests/unsatisfiable/UnsatDefer.stderr - testsuite/tests/warnings/should_fail/CaretDiagnostics1.stderr - utils/check-exact/ExactPrint.hs - utils/ghc-toolchain/src/GHC/Toolchain/Tools/Cc.hs - utils/ghc-toolchain/src/GHC/Toolchain/Tools/Cpp.hs - utils/haddock/doc/.gitignore - utils/haddock/doc/Makefile - + utils/haddock/doc/_static/haddock-custom.css - utils/haddock/doc/conf.py - utils/haddock/doc/markup.rst - + utils/haddock/doc/snippets/.gitignore - + utils/haddock/doc/snippets/Lists.hs - + utils/haddock/doc/snippets/Makefile - + utils/haddock/doc/snippets/Snippet-List-Bulleted.html - + utils/haddock/doc/snippets/Snippet-List-Bulleted.tex - + utils/haddock/doc/snippets/Snippet-List-Definition.html - + utils/haddock/doc/snippets/Snippet-List-Definition.tex - + utils/haddock/doc/snippets/Snippet-List-Enumerated.html - + utils/haddock/doc/snippets/Snippet-List-Enumerated.tex - + utils/haddock/doc/snippets/Snippet-List-Indentation.html - + utils/haddock/doc/snippets/Snippet-List-Indentation.tex - + utils/haddock/doc/snippets/Snippet-List-Multiline-Item.html - + utils/haddock/doc/snippets/Snippet-List-Multiline-Item.tex - + utils/haddock/doc/snippets/Snippet-List-Nested-Item.html - + utils/haddock/doc/snippets/Snippet-List-Nested-Item.tex - + utils/haddock/doc/snippets/Snippet-List-Not-Newline.html - + utils/haddock/doc/snippets/Snippet-List-Not-Newline.tex - + utils/haddock/doc/snippets/Snippet-List-Not-Separated.html - + utils/haddock/doc/snippets/Snippet-List-Not-Separated.tex - utils/haddock/haddock-api/src/Haddock/Interface/LexParseRn.hs - utils/haddock/haddock-api/src/Haddock/Interface/Rename.hs - utils/haddock/haddock-api/src/Haddock/Types.hs - utils/haddock/html-test/ref/A.html - utils/haddock/html-test/ref/Bug1004.html - utils/haddock/html-test/ref/Bug1033.html - utils/haddock/html-test/ref/Bug1103.html - utils/haddock/html-test/ref/Bug548.html - utils/haddock/html-test/ref/Bug923.html - utils/haddock/html-test/ref/ConstructorPatternExport.html - utils/haddock/html-test/ref/FunArgs.html - utils/haddock/html-test/ref/Hash.html - utils/haddock/html-test/ref/Instances.html - utils/haddock/html-test/ref/LinearTypes.html - utils/haddock/html-test/ref/RedactTypeSynonyms.html - utils/haddock/html-test/ref/T23616.html - utils/haddock/html-test/ref/Test.html - utils/haddock/html-test/ref/TypeFamilies3.html - utils/jsffi/dyld.mjs - utils/jsffi/post-link.mjs The diff was not included because it is too large. View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/4526e87628aeeeb70cfc78e46af18c… -- View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/4526e87628aeeeb70cfc78e46af18c… You're receiving this email because of your account on gitlab.haskell.org.
1 0
0 0
[Git][ghc/ghc][wip/sol/explicit_namespaces-docs] 32 commits: NCG/LA64: adjust register usage to avoid src-register being clobbered
by Andreas Klebinger (@AndreasK) 18 Feb '26

18 Feb '26
Andreas Klebinger pushed to branch wip/sol/explicit_namespaces-docs at Glasgow Haskell Compiler / GHC Commits: 2e39a340 by Peng Fan at 2026-02-07T03:42:01-05:00 NCG/LA64: adjust register usage to avoid src-register being clobbered - - - - - 9faf1b35 by Teo Camarasu at 2026-02-07T03:42:43-05:00 ghc-internal: Delete unnecessary GHC.Internal.Data.Ix This module merely re-exports GHC.Internal.Ix. It was copied from `base` when `ghc-internal` was split, but there is no reason to have this now. So, let's delete it. Resolves #26848 - - - - - d112b440 by Sven Tennie at 2026-02-07T10:47:56-05:00 Add cabal.project file to generate-ci This fixes the HLS setup for our CI code generation script (generate-ci). The project file simply makes `generate-ci` of the cabal file discoverable. - - - - - 5339f6f0 by Andreas Klebinger at 2026-02-07T10:48:40-05:00 CI: Don't collapse test results. This puts test output back into the primary test log instead of a subsection removing the need to expand a section to see test results. While the intention was good in practice the old behaviour mostly wastes time by requiring expansion of the section. Fixes #26882 - - - - - 0e1cd2e0 by Evan Piro at 2026-02-08T10:35:16-08:00 Linker.MacOS reduce dynflags import - - - - - 1c79a4cd by Michael Alan Dorman at 2026-02-09T08:11:51-05:00 Remove `extra_src_files` variable from `testsuite/driver/testlib.py` While reading through the test harness code, I noticed this variable with a TODO attached that referenced #12223. Although that bug is closed, it strongly implied that this special-case variable that only affected a single test was expected to be removed at some point. I also looked at 3415bcaa0b1903b5e12dfaadb5b774718e406eab---where it was added---whose commit message suggested that it would have been desirable to remove it, but that there were special circumstances that meant it had to remain (though it doesn't elucidate what those special circumstances are). However, the special circumstances were mentioned as if the test was in a different location than is currently is, so I decided to try changing the test to use the standard `extra_files` mechanism, which works in local testing. This also seems like a reasonable time to remove the script that was originally used in the transition, since it doesn't really serve a purpose anymore. - - - - - 0020e38a by Matthew Pickering at 2026-02-09T17:29:14-05:00 determinism: Use a stable sort in WithHsDocIdentifiers binary instance `WithHsDocIdentifiers` is defined as ``` 71 data WithHsDocIdentifiers a pass = WithHsDocIdentifiers 72 { hsDocString :: !a 73 , hsDocIdentifiers :: ![Located (IdP pass)] 74 } ``` This list of names is populated from `rnHsDocIdentifiers`, which calls `lookupGRE`, which calls `lookupOccEnv_AllNameSpaces`, which calls `nonDetEltsUFM` and returns the results in an order depending on uniques. Sorting the list with a stable sort before returning the interface makes the output deterministic and follows the approach taken by other fields in `Docs`. Fixes #26858 - - - - - 89898ce6 by echoumcp1 at 2026-02-09T17:30:01-05:00 Replace putstrln with logMsg in handleSeqHValueStatus Fixes #26549 - - - - - 7c52c4f9 by John Paul Adrian Glaubitz at 2026-02-10T13:52:43-05:00 rts: Switch prim to use modern atomic compiler builtins The __sync_*() atomic compiler builtins have been deprecated in GCC for a while now and also don't provide variants for 64-bit values such as __sync_fetch_and_add_8(). Thus, replace them with the modern __atomic_*() compiler builtins and while we're at it, also drop the helper macro CAS_NAND() which is now no longer needed since we stopped using the __sync_*() compiler builtins altogether. Co-authored-by: Ilias Tsitsimpis <iliastsi(a)debian.org> Fixes #26729 - - - - - cf60850a by Recursion Ninja at 2026-02-10T13:53:27-05:00 Decoupling L.H.S.Decls from GHC.Types.ForeignCall - Adding TTG extension point for 'CCallTarget' - Adding TTG extension point for 'CType' - Adding TTG extension point for 'Header' - Moving ForeignCall types that do not need extension to new L.H.S.Decls.Foreign module - Replacing 'Bool' parameters with descriptive data-types to increase clairty and prevent "Boolean Blindness" - - - - - 11a04cbb by Eric Lee at 2026-02-11T09:20:46-05:00 Derive Semigroup/Monoid for instances believed could be derived in #25871 - - - - - 15d9ce44 by Eric Lee at 2026-02-11T09:20:46-05:00 add Ghc.Data.Pair deriving - - - - - c85dc170 by Evan Piro at 2026-02-11T09:21:45-05:00 Linker.MacOS reduce options import - - - - - a541dd83 by Chris Wendt at 2026-02-11T16:06:41-05:00 Initialize plugins for `:set +c` in GHCi Fixes #23110. - - - - - 0f5a73bc by Cheng Shao at 2026-02-11T16:07:27-05:00 compiler: add Binary Text instance This patch adds `Binary` instance for strict `Text`, in preparation of making `Text` usable in certain GHC API use cases (e.g. haddock). This also introduces `text` as a direct dependency of the `ghc` package. - - - - - 9e58b8a1 by Cheng Shao at 2026-02-11T16:08:10-05:00 ghc-toolchain: add C11 check This patch partially reverts commit b8307eab80c5809df5405d76c822bf86877f5960 that removed C99 check in autoconf/ghc-toolchain. Now we: - No longer re-implement `FP_SET_CFLAGS_C11` similar to `FP_SET_CFLAGS_C99` in the past, since autoconf doesn't provide a convenient `AC_PROG_CC_C11` function. ghc-toolchain will handle it anyway. - The Cmm CPP C99 check is relanded and repurposed for C11. - The C99 logic in ghc-toolchain is relanded and repurposed for C11. - The C99 check in Stg.h is corrected to check for C11. The obsolete _ISOC99_SOURCE trick is dropped. - Usages of `-std=gnu99` in the testsuite are corrected to use `-std=gnu11`. Closes #26908. - - - - - 4df0adf6 by Simon Peyton Jones at 2026-02-11T21:50:13-05:00 Simplify the treatment of static forms This MR implements GHC proposal 732: simplify static forms, https://github.com/ghc-proposals/ghc-proposals/pull/732 thereby addressing #26556. See `Note [Grand plan for static forms]` in GHC.Iface.Tidy.StaticPtrTable The main changes are: * There is a new, simple rule for (static e), namely that the free term variables of `e` must be bound at top level. The check is done in the `HsStatic` case of `GHC.Rename.Expr.rnExpr` * That in turn substantially simplifies the info that the typechecker carries around in its type environment. Hooray. * The desugarer emits static bindings to top level directly; see the `HsStatic` case of `dsExpr`. * There is no longer any special static-related magic in the FloatOut pass. And the main Simplifier pipeline no longer needs a special case to run FloatOut even with -O0. Hooray. All this forced an unexpected change to the pattern match checker. It recursively invokes the main Hs desugarer when it wants to take a look at a term to spot some special cases (notably constructor applications). We don't want to emit any nested (static e) bindings to top level a second time! Yikes. That forced a modest refactor in GHC.HsToCore.Pmc: * The `dsl_nablas` field of `DsLclEnv` now has a `NoPmc` case, which says "I'm desugaring just for pattern-match checking purposes". * When that flag is set we don't emit static binds. That in turn forces a cascade of refactoring, but the net effect is an improvement; less risk of duplicated (even exponential?) work. See Note [Desugaring HsExpr during pattern-match checking]. 10% metric decrease, on some architectures, of compile-time max-bytes-used on T15304. Metric Decrease: T15304 - - - - - 7922f728 by Teo Camarasu at 2026-02-11T21:50:58-05:00 ghc-internal: avoid depending on GHC.Internal.Exts This module is mostly just re-exports. It made sense as a user-facing module, but there's no good reason ghc-internal modules should depend on it and doing so linearises the module graph - move considerAccessible to GHC.Internal.Magic Previously it lived in GHC.Internal.Exts, but it really deserves to live along with the other magic function, which are already re-exported from .Exts - move maxTupleSize to GHC.Internal.Tuple This previously lived in GHC.Internal.Exts but a comment already said it should be moved to .Tuple Resolves #26832 - - - - - b6a4a29b by Eric Lee at 2026-02-11T21:51:55-05:00 Remove unused Semigroup imports to fix GHC 9.14 bootstrapping - - - - - 99d8c146 by Simon Peyton Jones at 2026-02-12T17:36:59+00:00 Fix subtle bug in cast worker/wrapper See (CWw4) in Note [Cast worker/wrapper]. The true payload is in the change to the definition of GHC.Types.Id.Info.hasInlineUnfolding Everthing else is just documentation. There is a 2% compile time decrease for T13056; I'll take the win! Metric Decrease: T13056 - - - - - 530e8e58 by Simon Peyton Jones at 2026-02-12T20:17:23-05:00 Add regression tests for four StaticPtr bugs Tickets #26545, #24464, #24773, #16981 are all solved by the recently-landed MR commit 318ee13bcffa6aa8df42ba442ccd92aa0f7e210c Author: Simon Peyton Jones <simon.peytonjones(a)gmail.com> Date: Mon Oct 20 23:07:20 2025 +0100 Simplify the treatment of static forms This MR just adds regression tests for them. - - - - - 4157160f by Cheng Shao at 2026-02-13T06:27:04-05:00 ci: remove unused hlint-ghc-and-base job definition This patch removes the unused `hlint-ghc-and-base` job definition, it's never run since !9806. Note that hadrian lint rules still work locally, so anyone that wishes to run hlint on the codebase can continue to do so in their local worktree. - - - - - 039f1977 by Cheng Shao at 2026-02-13T06:27:47-05:00 wasm: use import.meta.main for proper distinction of nodejs main modules This patch uses `import.meta.main` for proper distinction of nodejs main modules, especially when the main module might be installed as a symlink. Fixes #26916. - - - - - 14f485ee by ARATA Mizuki at 2026-02-17T09:09:24+09:00 Support more x86 extensions: AVX-512 {BW,DQ,VL} and GFNI Also, mark AVX-512 ER and PF as deprecated. AVX-512 instructions can be used for certain 64-bit integer vector operations. GFNI can be used to implement bitReverse (currently not used by NCG, but LLVM may use it). Closes #26406 Addresses #26509 - - - - - 016f79d5 by fendor at 2026-02-17T09:16:16-05:00 Hide implementation details from base exception stack traces Ensure we hide the implementation details of the exception throwing mechanisms: * `undefined` * `throwSTM` * `throw` * `throwIO` * `error` The `HasCallStackBacktrace` should always have a length of exactly 1, not showing internal implementation details in the stack trace, as these are vastly distracting to end users. CLC proposal [#387](https://github.com/haskell/core-libraries-committee/issues/387) - - - - - 4f2840f2 by Brian J. Cardiff at 2026-02-17T17:04:08-05:00 configure: Accept happy-2.2 In Jan 2026 happy-2.2 was released. The most sensible change is https://github.com/haskell/happy/issues/335 which didn't trigger in a fresh build - - - - - 10b4d364 by Duncan Coutts at 2026-02-17T17:04:52-05:00 Fix errors in the documentation of the eventlog STOP_THREAD status codes Fix the code for BlockedOnMsgThrowTo. Document all the known historical warts. Fixes issue #26867 - - - - - c5e15b8b by Phil de Joux at 2026-02-18T05:07:36-05:00 haddock: use snippets for all list examples - generate snippet output for docs - reduce font size to better fit snippets - Use only directive to guard html snippets - Add latex snippets for lists - - - - - d388bac1 by Phil de Joux at 2026-02-18T05:07:36-05:00 haddock: Place the snippet input and output together - Put the output seemingly inside the example box - - - - - 016fa306 by Samuel Thibault at 2026-02-18T05:08:35-05:00 Fix linking against libm by moving the -lm option For those systems that need -lm for getting math functions, this is currently added on the link line very early, before the object files being linked together. Newer toolchains enable --as-needed by default, which means -lm is ignored at that point because no object requires a math function yet. With such toolchains, we thus have to add -lm after the objects, so the linker actually includes libm in the link. - - - - - 68bd0805 by Teo Camarasu at 2026-02-18T05:09:19-05:00 ghc-internal: Move GHC.Internal.Data.Bool to base This is a tiny module that only defines bool :: Bool -> a -> a -> a. We can just move this to base and delete it from ghc-internal. If we want this functionality there we can just use a case statement or if-then expression. Resolves 26865 - - - - - b7a8b4f1 by Simon Hengel at 2026-02-19T08:47:24+01:00 docs: Fix grammar in explicit_namespaces.rst - - - - - 274 changed files: - .gitlab-ci.yml - .gitlab/ci.sh - + .gitlab/generate-ci/cabal.project - compiler/GHC/Builtin/Names.hs - compiler/GHC/Builtin/Types.hs - compiler/GHC/CmmToAsm/Config.hs - compiler/GHC/CmmToAsm/LA64/CodeGen.hs - compiler/GHC/CmmToAsm/X86/CodeGen.hs - compiler/GHC/CmmToAsm/X86/Instr.hs - compiler/GHC/CmmToAsm/X86/Ppr.hs - compiler/GHC/CmmToLlvm/CodeGen.hs - compiler/GHC/Core/Lint.hs - compiler/GHC/Core/Opt/Pipeline.hs - compiler/GHC/Core/Opt/SetLevels.hs - compiler/GHC/Core/Opt/Simplify/Iteration.hs - compiler/GHC/Core/Opt/WorkWrap.hs - compiler/GHC/Core/TyCon.hs - compiler/GHC/CoreToStg.hs - compiler/GHC/CoreToStg/AddImplicitBinds.hs - compiler/GHC/Data/Pair.hs - compiler/GHC/Driver/Config/CmmToAsm.hs - compiler/GHC/Driver/Config/Core/Lint.hs - compiler/GHC/Driver/DynFlags.hs - compiler/GHC/Driver/Pipeline/Execute.hs - compiler/GHC/Driver/Session.hs - compiler/GHC/Hs/Binds.hs - compiler/GHC/Hs/Decls.hs - compiler/GHC/Hs/Doc.hs - compiler/GHC/Hs/Expr.hs - compiler/GHC/Hs/Instances.hs - compiler/GHC/Hs/Syn/Type.hs - compiler/GHC/Hs/Type.hs - compiler/GHC/Hs/Utils.hs - compiler/GHC/HsToCore.hs - compiler/GHC/HsToCore/Docs.hs - compiler/GHC/HsToCore/Errors/Types.hs - compiler/GHC/HsToCore/Expr.hs - compiler/GHC/HsToCore/Foreign/C.hs - compiler/GHC/HsToCore/Foreign/Call.hs - compiler/GHC/HsToCore/Foreign/Decl.hs - compiler/GHC/HsToCore/Foreign/JavaScript.hs - compiler/GHC/HsToCore/Foreign/Utils.hs - compiler/GHC/HsToCore/Foreign/Wasm.hs - compiler/GHC/HsToCore/GuardedRHSs.hs - compiler/GHC/HsToCore/Match.hs - compiler/GHC/HsToCore/Monad.hs - compiler/GHC/HsToCore/Pmc.hs - compiler/GHC/HsToCore/Pmc/Desugar.hs - compiler/GHC/HsToCore/Pmc/Solver.hs - compiler/GHC/HsToCore/Pmc/Solver/Types.hs - compiler/GHC/HsToCore/Quote.hs - compiler/GHC/HsToCore/Ticks.hs - compiler/GHC/HsToCore/Types.hs - compiler/GHC/Iface/Ext/Ast.hs - compiler/GHC/Iface/Syntax.hs - compiler/GHC/Iface/Tidy.hs - compiler/GHC/Iface/Tidy/StaticPtrTable.hs - compiler/GHC/Linker/Dynamic.hs - compiler/GHC/Linker/MacOS.hs - compiler/GHC/Parser.y - compiler/GHC/Parser/PostProcess.hs - compiler/GHC/Parser/PostProcess/Haddock.hs - compiler/GHC/Rename/Bind.hs - compiler/GHC/Rename/Expr.hs - compiler/GHC/Rename/Module.hs - compiler/GHC/Runtime/Heap/Inspect.hs - compiler/GHC/Runtime/Interpreter.hs - compiler/GHC/StgToByteCode.hs - compiler/GHC/StgToCmm/Foreign.hs - compiler/GHC/StgToJS/FFI.hs - compiler/GHC/SysTools/Cpp.hs - compiler/GHC/Tc/Deriv.hs - compiler/GHC/Tc/Errors/Ppr.hs - compiler/GHC/Tc/Errors/Types.hs - compiler/GHC/Tc/Gen/Bind.hs - compiler/GHC/Tc/Gen/Expr.hs - compiler/GHC/Tc/Gen/Foreign.hs - compiler/GHC/Tc/Gen/Head.hs - compiler/GHC/Tc/Gen/Pat.hs - compiler/GHC/Tc/Gen/Sig.hs - compiler/GHC/Tc/Module.hs - compiler/GHC/Tc/Solver.hs - compiler/GHC/Tc/Solver/Monad.hs - compiler/GHC/Tc/TyCl.hs - compiler/GHC/Tc/TyCl/Instance.hs - compiler/GHC/Tc/TyCl/Utils.hs - compiler/GHC/Tc/Types.hs - compiler/GHC/Tc/Types/BasicTypes.hs - compiler/GHC/Tc/Types/Constraint.hs - compiler/GHC/Tc/Types/ErrCtxt.hs - compiler/GHC/Tc/Types/Origin.hs - compiler/GHC/Tc/Utils/Env.hs - compiler/GHC/Tc/Utils/Monad.hs - compiler/GHC/Tc/Utils/TcMType.hs - − compiler/GHC/Tc/Utils/TcMType.hs-boot - compiler/GHC/Tc/Zonk/TcType.hs - compiler/GHC/Tc/Zonk/Type.hs - compiler/GHC/ThToHs.hs - compiler/GHC/Types/ForeignCall.hs - compiler/GHC/Types/Id/Info.hs - compiler/GHC/Types/Id/Make.hs - compiler/GHC/Types/Unique/DSet.hs - compiler/GHC/Utils/Binary.hs - compiler/GHC/Utils/Ppr/Colour.hs - compiler/Language/Haskell/Syntax/Decls.hs - + compiler/Language/Haskell/Syntax/Decls/Foreign.hs - compiler/Language/Haskell/Syntax/Extension.hs - compiler/ghc.cabal.in - configure.ac - distrib/configure.ac.in - docs/users_guide/9.16.1-notes.rst - docs/users_guide/eventlog-formats.rst - docs/users_guide/exts/explicit_namespaces.rst - docs/users_guide/phases.rst - docs/users_guide/using.rst - ghc/GHCi/UI/Info.hs - libraries/base/changelog.md - libraries/base/src/Data/Bool.hs - libraries/base/src/Data/Ix.hs - libraries/base/src/Data/List.hs - libraries/base/src/Data/List/NubOrdSet.hs - libraries/base/src/GHC/Exts.hs - libraries/ghc-internal/ghc-internal.cabal.in - − libraries/ghc-internal/src/GHC/Internal/Data/Bool.hs - libraries/ghc-internal/src/GHC/Internal/Data/Foldable.hs - libraries/ghc-internal/src/GHC/Internal/Data/Function.hs - − libraries/ghc-internal/src/GHC/Internal/Data/Ix.hs - libraries/ghc-internal/src/GHC/Internal/Data/Type/Bool.hs - libraries/ghc-internal/src/GHC/Internal/Data/Type/Ord.hs - libraries/ghc-internal/src/GHC/Internal/Data/Version.hs - libraries/ghc-internal/src/GHC/Internal/Exception.hs - libraries/ghc-internal/src/GHC/Internal/Exts.hs - libraries/ghc-internal/src/GHC/Internal/Heap/Closures.hs - libraries/ghc-internal/src/GHC/Internal/IO/FD.hs - libraries/ghc-internal/src/GHC/Internal/JS/Foreign/Callback.hs - libraries/ghc-internal/src/GHC/Internal/JS/Prim.hs - libraries/ghc-internal/src/GHC/Internal/JS/Prim/Internal/Build.hs - libraries/ghc-internal/src/GHC/Internal/Magic.hs - libraries/ghc-internal/src/GHC/Internal/STM.hs - libraries/ghc-internal/src/GHC/Internal/Stack/Decode.hs - libraries/ghc-internal/src/GHC/Internal/System/IO/OS.hs - libraries/ghc-internal/src/GHC/Internal/TH/Lift.hs - libraries/ghc-internal/src/GHC/Internal/Tuple.hs - libraries/ghc-internal/src/GHC/Internal/TypeError.hs - libraries/ghc-internal/src/GHC/Internal/Wasm/Prim/Exports.hs - libraries/ghc-internal/src/GHC/Internal/Wasm/Prim/Imports.hs - libraries/ghc-internal/src/GHC/Internal/Wasm/Prim/Types.hs - + libraries/ghc-internal/tests/backtraces/T15395.hs - + libraries/ghc-internal/tests/backtraces/T15395.stdout - libraries/ghc-internal/tests/backtraces/all.T - libraries/ghc-internal/tests/stack-annotation/ann_frame005.stdout - m4/fp_cmm_cpp_cmd_with_args.m4 - m4/fptools_happy.m4 - rts/include/Stg.h - rts/prim/atomic.c - testsuite/driver/cpu_features.py - − testsuite/driver/kill_extra_files.py - testsuite/driver/testlib.py - testsuite/tests/arrows/should_compile/T21301.stderr - testsuite/tests/codeGen/should_gen_asm/all.T - + testsuite/tests/codeGen/should_gen_asm/avx512-int64-minmax.asm - + testsuite/tests/codeGen/should_gen_asm/avx512-int64-minmax.hs - + testsuite/tests/codeGen/should_gen_asm/avx512-int64-mul.asm - + testsuite/tests/codeGen/should_gen_asm/avx512-int64-mul.hs - + testsuite/tests/codeGen/should_gen_asm/avx512-word64-minmax.asm - + testsuite/tests/codeGen/should_gen_asm/avx512-word64-minmax.hs - testsuite/tests/codeGen/should_run/CgStaticPointers.hs - testsuite/tests/codeGen/should_run/CgStaticPointersNoFullLazyness.hs - testsuite/tests/count-deps/CountDepsAst.stdout - testsuite/tests/count-deps/CountDepsParser.stdout - testsuite/tests/deSugar/should_fail/DsStrictFail.stderr - testsuite/tests/deSugar/should_run/T20024.stderr - testsuite/tests/deSugar/should_run/dsrun005.stderr - testsuite/tests/deSugar/should_run/dsrun007.stderr - testsuite/tests/deSugar/should_run/dsrun008.stderr - testsuite/tests/deriving/should_run/T9576.stderr - testsuite/tests/ffi/should_run/all.T - testsuite/tests/ghci/scripts/Defer02.stderr - testsuite/tests/ghci/scripts/T15325.stderr - testsuite/tests/ghci/scripts/T20150.stdout - testsuite/tests/interface-stability/base-exports.stdout - testsuite/tests/interface-stability/base-exports.stdout-javascript-unknown-ghcjs - testsuite/tests/interface-stability/base-exports.stdout-mingw32 - testsuite/tests/interface-stability/base-exports.stdout-ws-32 - testsuite/tests/interface-stability/ghc-experimental-exports.stdout - testsuite/tests/interface-stability/ghc-experimental-exports.stdout-mingw32 - testsuite/tests/interface-stability/ghc-prim-exports.stdout - testsuite/tests/interface-stability/ghc-prim-exports.stdout-mingw32 - testsuite/tests/parser/should_compile/DumpRenamedAst.stderr - testsuite/tests/parser/should_compile/T14189.stderr - testsuite/tests/patsyn/should_run/ghci.stderr - + testsuite/tests/plugins/T23110.hs - + testsuite/tests/plugins/T23110.script - + testsuite/tests/plugins/T23110.stdout - testsuite/tests/plugins/all.T - testsuite/tests/process/all.T - testsuite/tests/quotes/LiftErrMsgDefer.stderr - testsuite/tests/rename/should_fail/RnStaticPointersFail01.stderr - testsuite/tests/rename/should_fail/RnStaticPointersFail03.stderr - + testsuite/tests/rename/should_fail/T26545.hs - + testsuite/tests/rename/should_fail/T26545.stderr - testsuite/tests/rename/should_fail/all.T - testsuite/tests/safeHaskell/safeLanguage/SafeLang15.stderr - testsuite/tests/showIface/DocsInHiFile1.stdout - testsuite/tests/showIface/HaddockSpanIssueT24378.stdout - testsuite/tests/showIface/MagicHashInHaddocks.stdout - testsuite/tests/simd/should_run/all.T - + testsuite/tests/simplCore/should_compile/T26903.hs - + testsuite/tests/simplCore/should_compile/T26903.stderr - testsuite/tests/simplCore/should_compile/T8331.stderr - testsuite/tests/simplCore/should_compile/all.T - testsuite/tests/type-data/should_run/T22332a.stderr - + testsuite/tests/typecheck/should_compile/T24464.hs - testsuite/tests/typecheck/should_compile/all.T - testsuite/tests/typecheck/should_run/T10284.stderr - testsuite/tests/typecheck/should_run/T13838.stderr - + testsuite/tests/typecheck/should_run/T16981.hs - + testsuite/tests/typecheck/should_run/T16981.stdout - + testsuite/tests/typecheck/should_run/T24773.hs - + testsuite/tests/typecheck/should_run/T24773.stdout - testsuite/tests/typecheck/should_run/T9497a-run.stderr - testsuite/tests/typecheck/should_run/T9497b-run.stderr - testsuite/tests/typecheck/should_run/T9497c-run.stderr - testsuite/tests/typecheck/should_run/all.T - testsuite/tests/unsatisfiable/T23816.stderr - testsuite/tests/unsatisfiable/UnsatDefer.stderr - testsuite/tests/warnings/should_fail/CaretDiagnostics1.stderr - utils/check-exact/ExactPrint.hs - utils/ghc-toolchain/src/GHC/Toolchain/Tools/Cc.hs - utils/ghc-toolchain/src/GHC/Toolchain/Tools/Cpp.hs - utils/haddock/doc/.gitignore - utils/haddock/doc/Makefile - + utils/haddock/doc/_static/haddock-custom.css - utils/haddock/doc/conf.py - utils/haddock/doc/markup.rst - + utils/haddock/doc/snippets/.gitignore - + utils/haddock/doc/snippets/Lists.hs - + utils/haddock/doc/snippets/Makefile - + utils/haddock/doc/snippets/Snippet-List-Bulleted.html - + utils/haddock/doc/snippets/Snippet-List-Bulleted.tex - + utils/haddock/doc/snippets/Snippet-List-Definition.html - + utils/haddock/doc/snippets/Snippet-List-Definition.tex - + utils/haddock/doc/snippets/Snippet-List-Enumerated.html - + utils/haddock/doc/snippets/Snippet-List-Enumerated.tex - + utils/haddock/doc/snippets/Snippet-List-Indentation.html - + utils/haddock/doc/snippets/Snippet-List-Indentation.tex - + utils/haddock/doc/snippets/Snippet-List-Multiline-Item.html - + utils/haddock/doc/snippets/Snippet-List-Multiline-Item.tex - + utils/haddock/doc/snippets/Snippet-List-Nested-Item.html - + utils/haddock/doc/snippets/Snippet-List-Nested-Item.tex - + utils/haddock/doc/snippets/Snippet-List-Not-Newline.html - + utils/haddock/doc/snippets/Snippet-List-Not-Newline.tex - + utils/haddock/doc/snippets/Snippet-List-Not-Separated.html - + utils/haddock/doc/snippets/Snippet-List-Not-Separated.tex - utils/haddock/haddock-api/src/Haddock/Interface/LexParseRn.hs - utils/haddock/haddock-api/src/Haddock/Interface/Rename.hs - utils/haddock/haddock-api/src/Haddock/Types.hs - utils/haddock/html-test/ref/A.html - utils/haddock/html-test/ref/Bug1004.html - utils/haddock/html-test/ref/Bug1033.html - utils/haddock/html-test/ref/Bug1103.html - utils/haddock/html-test/ref/Bug548.html - utils/haddock/html-test/ref/Bug923.html - utils/haddock/html-test/ref/ConstructorPatternExport.html - utils/haddock/html-test/ref/FunArgs.html - utils/haddock/html-test/ref/Hash.html - utils/haddock/html-test/ref/Instances.html - utils/haddock/html-test/ref/LinearTypes.html - utils/haddock/html-test/ref/RedactTypeSynonyms.html - utils/haddock/html-test/ref/T23616.html - utils/haddock/html-test/ref/Test.html - utils/haddock/html-test/ref/TypeFamilies3.html - utils/jsffi/dyld.mjs - utils/jsffi/post-link.mjs The diff was not included because it is too large. View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/7596dc045f47e75ec2b77f3af0ef55… -- View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/7596dc045f47e75ec2b77f3af0ef55… You're receiving this email because of your account on gitlab.haskell.org.
1 0
0 0
[Git][ghc/ghc][wip/sol/explicit_namespaces-docs] docs: Fix grammar in explicit_namespaces.rst
by Andreas Klebinger (@AndreasK) 18 Feb '26

18 Feb '26
Andreas Klebinger pushed to branch wip/sol/explicit_namespaces-docs at Glasgow Haskell Compiler / GHC Commits: 7596dc04 by Simon Hengel at 2026-02-19T08:46:47+01:00 docs: Fix grammar in explicit_namespaces.rst - - - - - 1 changed file: - docs/users_guide/exts/explicit_namespaces.rst Changes: ===================================== docs/users_guide/exts/explicit_namespaces.rst ===================================== @@ -86,7 +86,7 @@ However, how would one import only the data constructor? There are two options: import Data.Proxy (data Proxy) -- imports the data constructor only import Data.Proxy (pattern Proxy) -- imports the data constructor only -The ``data`` keyword enables the import or export a data constructor without its +The ``data`` keyword allows a data constructor to be imported or exported without its parent type constructor. The ``pattern`` keyword does the same, with only a few differences: View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/7596dc045f47e75ec2b77f3af0ef55a… -- View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/7596dc045f47e75ec2b77f3af0ef55a… You're receiving this email because of your account on gitlab.haskell.org.
1 0
0 0
[Git][ghc/ghc][wip/io-manager-deadlock-detection] 10 commits: Split posix/MIO.c out of posix/Signals.c
by Duncan Coutts (@dcoutts) 18 Feb '26

18 Feb '26
Duncan Coutts pushed to branch wip/io-manager-deadlock-detection at Glasgow Haskell Compiler / GHC Commits: 1136fd5b by Duncan Coutts at 2026-02-16T00:30:48+00:00 Split posix/MIO.c out of posix/Signals.c The MIO I/O manager was secretly living inside the Signals file. Now it gets its own file, like any other self-respecting I/O manager. - - - - - ec170e68 by Duncan Coutts at 2026-02-16T00:30:50+00:00 Rationalise some scheduler run queue utilities Move them all to the same place in the file. Make some static that were used only internally. Also remove a redundant assignment after calling truncateRunQueue that is already done within truncateRunQueue. - - - - - c4312136 by Duncan Coutts at 2026-02-16T00:30:50+00:00 Rename initIOManager{AfterFork} to {re}startIOManager These are more accurate names, since these actions happen after initialisation and are really about starting (or restarting) background threads. - - - - - 24a403ed by Duncan Coutts at 2026-02-16T00:30:50+00:00 Add a TODO to the MIO I/O manager The direction of travel is to make I/O managers per-capability and have all their state live in the struct CapIOManager. The MIO I/O manager however still has a number of global variables. It's not obvious how handle these globals however. - - - - - e9c4d16c by Duncan Coutts at 2026-02-16T00:30:50+00:00 Free per-cap I/O managers during shutdown and forkProcess Historically this was not strictly necessary. The select and win32 legacy I/O managers did not maintain any dynamically allocated resources. The new poll one does (an auxillary table), and so this should be freed. After forkProcess, all threads get deleted. This includes threads waiting on I/O or timers. So as of this patch, resetting the I/O manager is just about tidying things up. For example, for the poll I/O manager this will reset the size of the AIOP table (which otherwise grows but never shrinks). In future however the re-initialising will become neeecessary for functionality, since some I/O managers will need to re-initialise wakeup fds that are set CLOEXEC. - - - - - fcc1cdd3 by Duncan Coutts at 2026-02-16T00:30:50+00:00 Add an FdWakup module for posix I/O managers This will be used to implement wakeupIOManager for in-RTS I/O managers. It provides a notification/wakeup mechanism using FDs, suitable for situations when I/O managers are blocked on a set of fds anyway. - - - - - 4e4aa84f by Duncan Coutts at 2026-02-16T00:30:50+00:00 Add wakeupIOManager support for select I/O manager Uses the FdWakup mechanism. - - - - - 6fe60774 by Duncan Coutts at 2026-02-16T00:30:50+00:00 Add wakeupIOManager support for poll I/O manager Uses the FdWakup mechanism. A quirk we have to cope with is that we now need to poll one more fd -- the wakeup_fd_r -- but this fd has no corresponding entry in the aiop_table. This is awkward since we have set up our aiop_poll_table to be an auxilliary table with matching indicies. The solution this patch uses (and described in the comments) is to have two tables: struct pollfd *aiop_poll_table, *full_poll_table; and to have the aiop_poll_table alias the tail of the full_poll_table. The head entry in the full_poll_table is the extra fd. So we poll the full_poll_table, while the aiop_poll_table still has matching indicies with the aiop_table. Hurrah for C aliasing rules. - - - - - 24b6f621 by Duncan Coutts at 2026-02-16T00:30:50+00:00 Add wakeupIOManager support for win32 legacy I/O manager - - - - - bc97a19f by Duncan Coutts at 2026-02-16T00:30:50+00:00 wakeupIOManager is now required for all I/O managers We are going to rely on it. Previously it could be a no-op. Update the docs in the header file. Also, temporarily disable awaitCompletedTimeoutsOrIO post-condition assertion. It will become more complicated due to wakeupIOManager, and it's not yet clear how to express it. We will re-introduce a post condition after a few more changes. - - - - - 18 changed files: - rts/Capability.c - rts/IOManager.c - rts/IOManager.h - rts/IOManagerInternals.h - rts/RtsStartup.c - rts/Schedule.c - rts/Schedule.h - + rts/posix/FdWakeup.c - + rts/posix/FdWakeup.h - + rts/posix/MIO.c - + rts/posix/MIO.h - rts/posix/Poll.c - rts/posix/Poll.h - rts/posix/Select.c - rts/posix/Select.h - rts/posix/Signals.c - rts/posix/Signals.h - rts/rts.cabal Changes: ===================================== rts/Capability.c ===================================== @@ -1280,6 +1280,7 @@ shutdownCapabilities(Task *task, bool safe) static void freeCapability (Capability *cap) { + freeCapabilityIOManager(cap->iomgr); stgFree(cap->mut_lists); stgFree(cap->saved_mut_lists); if (cap->current_segments) { ===================================== rts/IOManager.c ===================================== @@ -39,7 +39,7 @@ #endif #if defined(IOMGR_ENABLED_MIO_POSIX) -#include "posix/Signals.h" +#include "posix/MIO.h" #include "Prelude.h" #endif @@ -343,9 +343,7 @@ void initCapabilityIOManager(CapIOManager *iomgr) switch (iomgr_type) { #if defined(IOMGR_ENABLED_SELECT) case IO_MANAGER_SELECT: - iomgr->blocked_queue_hd = END_TSO_QUEUE; - iomgr->blocked_queue_tl = END_TSO_QUEUE; - iomgr->sleeping_queue = END_TSO_QUEUE; + initCapabilityIOManagerSelect(iomgr); break; #endif @@ -373,11 +371,31 @@ void initCapabilityIOManager(CapIOManager *iomgr) } +void freeCapabilityIOManager(CapIOManager *iomgr) +{ + switch (iomgr_type) { +#if defined(IOMGR_ENABLED_SELECT) + case IO_MANAGER_SELECT: + freeCapabilityIOManagerSelect(iomgr); + break; +#endif + +#if defined(IOMGR_ENABLED_POLL) + case IO_MANAGER_POLL: + freeCapabilityIOManagerPoll(iomgr); + break; +#endif + default: + break; + } +} + + /* Called late in the RTS initialisation */ -void initIOManager(void) +void startIOManager(void) { - debugTrace(DEBUG_iomanager, "initialising %s I/O manager", showIOManager()); + debugTrace(DEBUG_iomanager, "starting %s I/O manager", showIOManager()); switch (iomgr_type) { @@ -441,7 +459,7 @@ void initIOManager(void) /* Called from forkProcess in the child process on the surviving capability. */ void -initIOManagerAfterFork(CapIOManager *iomgr, Capability **pcap) +restartIOManager(CapIOManager *iomgr, Capability **pcap) { switch (iomgr_type) { @@ -546,8 +564,21 @@ exitIOManager(bool wait_threads) */ void wakeupIOManager(void) { + debugTrace(DEBUG_iomanager, "Sending wakeup to I/O manager..."); switch (iomgr_type) { +#if defined(IOMGR_ENABLED_SELECT) + case IO_MANAGER_SELECT: + wakeupIOManagerSelect(MainCapability.iomgr); + break; +#endif + +#if defined(IOMGR_ENABLED_POLL) + case IO_MANAGER_POLL: + wakeupIOManagerPoll(MainCapability.iomgr); + break; +#endif + #if defined(IOMGR_ENABLED_MIO_POSIX) case IO_MANAGER_MIO_POSIX: /* MIO Posix implementation in posix/Signals.c */ @@ -572,8 +603,13 @@ void wakeupIOManager(void) #endif break; #endif - default: +#if defined(IOMGR_ENABLED_WIN32_LEGACY) + case IO_MANAGER_WIN32_LEGACY: + abandonRequestWait(); break; +#endif + default: + barf("wakeupIOManager not implemented"); } } @@ -782,7 +818,9 @@ void awaitCompletedTimeoutsOrIO(CapIOManager *iomgr) default: barf("pollCompletedTimeoutsOrIO not implemented"); } - ASSERT(!emptyRunQueue(iomgr->cap) || getSchedState() != SCHED_RUNNING); + // FIXME: the post condition is now more complicated. Await can now simply + // be interrupted by wakeupIOManager. + // ASSERT(!emptyRunQueue(iomgr->cap) || getSchedState() != SCHED_RUNNING); } ===================================== rts/IOManager.h ===================================== @@ -242,11 +242,33 @@ CapIOManager *allocCapabilityIOManager(Capability *cap); */ void initCapabilityIOManager(CapIOManager *iomgr); +/* When shutting down a capability, or after forkProcess, free the resources + * held by a CapIOManager to put it back into a state in which either it can be + * re-initialised using initCapabilityIOManager, or the whole structure freed. + * + * Note that this does not free the CapIOManager structure itself, just the + * contents. + * + * This is used during capability shutdown, during RTS shutdown. It is not used + * when reducing the number of capabilities. Capabilities are disabled rather + * than freed entirely: the I/O manager keeps running but threads that become + * runnable are migrated away. + * + * It is also used after forkProcess. + */ +void freeCapabilityIOManager(CapIOManager *iomgr); + +/* CapIOManager life cycle: + * + * alloc -> init -> free -> free struct + * ^ | + * +--------+ + */ /* Init hook: called from hs_init_ghc, very late in the startup after almost * everything else is done. */ -void initIOManager(void); +void startIOManager(void); /* Init hook: called from forkProcess in the child process on the surviving @@ -255,8 +277,8 @@ void initIOManager(void); * This is synchronous and can run Haskell code, so can change the given cap. * TODO: it would make for a cleaner API here if this were made asynchronous. */ -void initIOManagerAfterFork(CapIOManager *iomgr, - /* inout */ Capability **pcap); +void restartIOManager(CapIOManager *iomgr, + /* inout */ Capability **pcap); /* TODO: rationalise initIOManager and initIOManagerAfterFork into a single per-capability init function. @@ -283,19 +305,13 @@ void stopIOManager(void); void exitIOManager(bool wait_threads); -/* Wakeup hook: called from the scheduler's wakeUpRts (currently only in - * threaded mode). +/* Wakeup hook: called from the scheduler's wakeUpRts(). * * The I/O manager can be blocked waiting on I/O or timers. Sometimes there are * other external events where we need to wake up the I/O manager and return - * to the schedulr. - * - * At the moment, all the non-threaded I/O managers will do this automagically - * since a signal will interrupt any waiting system calls, so at the moment - * the implementation for the non-threaded I/O managers does nothing. + * to the scheduler. * - * For the I/O managers in threaded mode, this arranges to unblock the I/O - * manager if it waa blocked waiting. + * This arranges to unblock the I/O manager if it was blocked waiting. */ void wakeupIOManager(void); ===================================== rts/IOManagerInternals.h ===================================== @@ -46,6 +46,11 @@ struct _CapIOManager { StgTSO *sleeping_queue; #endif +#if defined(IOMGR_ENABLED_SELECT) || defined(IOMGR_ENABLED_POLL) + /* FDs for waking up the I/O manager when it is blocked waiting */ + int wakeup_fd_r, wakeup_fd_w; +#endif + #if defined(IOMGR_ENABLED_POLL) /* AIOP and timeout collections shared by several I/O manager impls */ ClosureTable aiop_table; @@ -53,8 +58,11 @@ struct _CapIOManager { #endif #if defined(IOMGR_ENABLED_POLL) - /* Auxiliary table with size and indexes matching the aiop_table */ - struct pollfd *aiop_poll_table; + /* Auxiliary table with size and indexes matching the aiop_table. This is + * aliased to the tail of the full poll table, which has a head entry for + * the wakeup_fd_r above, so we can also poll that fd. + */ + struct pollfd *aiop_poll_table, *full_poll_table; #endif #if defined(IOMGR_ENABLED_WIN32_LEGACY) ===================================== rts/RtsStartup.c ===================================== @@ -427,7 +427,7 @@ hs_init_ghc(int *argc, char **argv[], RtsConfig rts_config) } #endif - initIOManager(); + startIOManager(); x86_init_fpu(); ===================================== rts/Schedule.c ===================================== @@ -174,6 +174,11 @@ static void deleteAllThreads (void); static void deleteThread_(StgTSO *tso); #endif +#if defined(FORKPROCESS_PRIMOP_SUPPORTED) +static void truncateRunQueue(Capability *cap); +#endif +static StgTSO *popRunQueue (Capability *cap); + /* --------------------------------------------------------------------------- Main scheduling loop. @@ -591,38 +596,6 @@ run_thread: } /* end of while() */ } -/* ----------------------------------------------------------------------------- - * Run queue operations - * -------------------------------------------------------------------------- */ - -static void -removeFromRunQueue (Capability *cap, StgTSO *tso) -{ - if (tso->block_info.prev == END_TSO_QUEUE) { - ASSERT(cap->run_queue_hd == tso); - cap->run_queue_hd = tso->_link; - } else { - setTSOLink(cap, tso->block_info.prev, tso->_link); - } - if (tso->_link == END_TSO_QUEUE) { - ASSERT(cap->run_queue_tl == tso); - cap->run_queue_tl = tso->block_info.prev; - } else { - setTSOPrev(cap, tso->_link, tso->block_info.prev); - } - tso->_link = tso->block_info.prev = END_TSO_QUEUE; - cap->n_run_queue--; - - IF_DEBUG(sanity, checkRunQueue(cap)); -} - -void -promoteInRunQueue (Capability *cap, StgTSO *tso) -{ - removeFromRunQueue(cap, tso); - pushOnRunQueue(cap, tso); -} - /* ----------------------------------------------------------------------------- * scheduleFindWork() * @@ -2191,7 +2164,15 @@ forkProcess(HsStablePtr *entry // bound threads for which the corresponding Task does not // exist. truncateRunQueue(cap); - cap->n_run_queue = 0; + + // Reset and re-initialise the capability's I/O manager, + // to get the I/O manager ready again. + // + // Any threads waiting on I/O or timers should have been + // removed from I/O manager queues by deleteThread_ above. + // TODO: but we could assert that here. + freeCapabilityIOManager(cap->iomgr); + initCapabilityIOManager(cap->iomgr); // Any suspended C-calling Tasks are no more, their OS threads // don't exist now: @@ -2208,7 +2189,7 @@ forkProcess(HsStablePtr *entry cap->n_returning_tasks = 0; #endif - // Release all caps except 0, we'll use that for starting + // Release all caps except 0, we'll use that for restarting // the IO manager and running the client action below. if (cap->no != 0) { task->cap = cap; @@ -2232,7 +2213,7 @@ forkProcess(HsStablePtr *entry // like startup event, capabilities, process info etc traceTaskCreate(task, cap); - initIOManagerAfterFork(cap->iomgr, &cap); + restartIOManager(cap->iomgr, &cap); // start timer after the IOManager is initialized // (the idle GC may wake up the IOManager) @@ -2337,6 +2318,10 @@ setNumCapabilities (uint32_t new_n_capabilities USED_IF_THREADS) // the capability; we don't have to worry about GC data // structures, the nursery, etc. // + // This approach also handles threads blocked on I/O. Such threads + // remain blocked, and when I/O completes and threads become runnable + // then they are migrated away. + // for (n = new_n_capabilities; n < enabled_capabilities; n++) { getCapability(n)->disabled = true; traceCapDisable(getCapability(n)); @@ -2997,7 +2982,7 @@ pushOnRunQueue (Capability *cap, StgTSO *tso) cap->n_run_queue++; } -StgTSO *popRunQueue (Capability *cap) +static StgTSO *popRunQueue (Capability *cap) { ASSERT(cap->n_run_queue > 0); StgTSO *t = cap->run_queue_hd; @@ -3017,6 +3002,45 @@ StgTSO *popRunQueue (Capability *cap) return t; } +#if defined(FORKPROCESS_PRIMOP_SUPPORTED) +static void truncateRunQueue(Capability *cap) +{ + // Can only be called by the task owning the capability. + TSAN_ANNOTATE_BENIGN_RACE(&cap->run_queue_hd, "truncateRunQueue"); + TSAN_ANNOTATE_BENIGN_RACE(&cap->run_queue_tl, "truncateRunQueue"); + TSAN_ANNOTATE_BENIGN_RACE(&cap->n_run_queue, "truncateRunQueue"); + cap->run_queue_hd = END_TSO_QUEUE; + cap->run_queue_tl = END_TSO_QUEUE; + cap->n_run_queue = 0; +} +#endif + +static void removeFromRunQueue (Capability *cap, StgTSO *tso) +{ + if (tso->block_info.prev == END_TSO_QUEUE) { + ASSERT(cap->run_queue_hd == tso); + cap->run_queue_hd = tso->_link; + } else { + setTSOLink(cap, tso->block_info.prev, tso->_link); + } + if (tso->_link == END_TSO_QUEUE) { + ASSERT(cap->run_queue_tl == tso); + cap->run_queue_tl = tso->block_info.prev; + } else { + setTSOPrev(cap, tso->_link, tso->block_info.prev); + } + tso->_link = tso->block_info.prev = END_TSO_QUEUE; + cap->n_run_queue--; + + IF_DEBUG(sanity, checkRunQueue(cap)); +} + +void promoteInRunQueue (Capability *cap, StgTSO *tso) +{ + removeFromRunQueue(cap, tso); + pushOnRunQueue(cap, tso); +} + /* ----------------------------------------------------------------------------- raiseExceptionHelper ===================================== rts/Schedule.h ===================================== @@ -164,10 +164,6 @@ void appendToRunQueue (Capability *cap, StgTSO *tso); */ void pushOnRunQueue (Capability *cap, StgTSO *tso); -/* Pop the first thread off the runnable queue. - */ -StgTSO *popRunQueue (Capability *cap); - INLINE_HEADER StgTSO * peekRunQueue (Capability *cap) { @@ -184,18 +180,6 @@ emptyRunQueue(Capability *cap) return cap->n_run_queue == 0; } -INLINE_HEADER void -truncateRunQueue(Capability *cap) -{ - // Can only be called by the task owning the capability. - TSAN_ANNOTATE_BENIGN_RACE(&cap->run_queue_hd, "truncateRunQueue"); - TSAN_ANNOTATE_BENIGN_RACE(&cap->run_queue_tl, "truncateRunQueue"); - TSAN_ANNOTATE_BENIGN_RACE(&cap->n_run_queue, "truncateRunQueue"); - cap->run_queue_hd = END_TSO_QUEUE; - cap->run_queue_tl = END_TSO_QUEUE; - cap->n_run_queue = 0; -} - #endif /* !IN_STG_CODE */ #include "EndPrivate.h" ===================================== rts/posix/FdWakeup.c ===================================== @@ -0,0 +1,132 @@ +/* ----------------------------------------------------------------------------- + * + * (c) The GHC Team 2025 + * + * Utilities for a simple fd-based cross-thread wakeup mechanism. + * + * This is used in I/O managers, to provide a mechanism to wake them when they + * are blocked waiting on fds and timeouts. The mechanism works by including + * the read end fd into the set of fds the I/O manager waits on, and when a + * wake up is needed, the write end fd is used. + * + * This is implemented using either eventfd() or pipe(). + * + * Linux 2.6.22+ and FreeBSD 13+ support eventfd. It is a single fd with a + * 64bit counter. It uses less resources than a pipe, and is probably a tad + * faster. Using write() adds to the counter, while read() reads and resets + * it. This gives us event combining. + * + * Otherwise we use a classic unix pipe. + * + * -------------------------------------------------------------------------*/ + +#include "rts/PosixSource.h" +#include "Rts.h" + +#include "FdWakeup.h" + +#include <fcntl.h> +#include <unistd.h> + +#ifdef HAVE_SYS_EVENTFD_H +#include <sys/eventfd.h> +#endif + +#if !defined(HAVE_EVENTFD) \ + || (defined(HAVE_EVENTFD) && !(defined(EFD_CLOEXEC) && defined(EFD_NONBLOCK))) +static void fcntl_CLOEXEC_NONBLOCK(int fd) +{ + int res1 = fcntl(fd, F_SETFD, FD_CLOEXEC); + int res2 = fcntl(fd, F_SETFL, O_NONBLOCK); + if (RTS_UNLIKELY(res1 < 0 || res2 < 0)) { + sysErrorBelch("newFdWakeup fcntl()"); + stg_exit(EXIT_FAILURE); + } +} +#endif + +void newFdWakeup(int *wakeup_fd_r, int *wakeup_fd_w) +{ +#if defined(HAVE_EVENTFD) + int wakeup_fd; +#if defined(EFD_CLOEXEC) && defined(EFD_NONBLOCK) + wakeup_fd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); +#else + wakeup_fd = eventfd(0, 0); + if (wakeup_fd >= 0) fcntl_CLOEXEC_NONBLOCK(wakeup_fd); +#endif + if (RTS_UNLIKELY(wakeup_fd < 0)) { + sysErrorBelch("newFdWakeup eventfd()"); + stg_exit(EXIT_FAILURE); + } + /* eventfd uses the same fd for each end */ + *wakeup_fd_r = wakeup_fd; + *wakeup_fd_w = wakeup_fd; +#else + int pipefd[2]; + int res; + res = pipe(pipefd); + if (RTS_UNLIKELY(res < 0)) { + sysErrorBelch("newFdWakeup pipe"); + stg_exit(EXIT_FAILURE); + } + fcntl_CLOEXEC_NONBLOCK(pipefd[0]); + fcntl_CLOEXEC_NONBLOCK(pipefd[1]); + *wakeup_fd_r = pipefd[0]; /* read end */ + *wakeup_fd_w = pipefd[1]; /* write end */ +#endif +} + +void closeFdWakeup(int wakeup_fd_r, int wakeup_fd_w) +{ +#if defined(HAVE_EVENTFD) + ASSERT(wakeup_fd_r == wakeup_fd_w); + close(wakeup_fd_r); +#else + ASSERT(wakeup_fd_r != wakeup_fd_w); + close(wakeup_fd_r); + close(wakeup_fd_w); +#endif +} + +void sendFdWakeup(int wakeup_fd_w) +{ + int res; +#if defined(HAVE_EVENTFD) + uint64_t val = 1; + res = write(wakeup_fd_w, &val, 8); +#else + unsigned char buf = 1; + res = write(wakeup_fd_w, &buf, 1); +#endif + if (RTS_UNLIKELY(res < 0)) { + /* Unlikely the pipe buffer will fill, but it would not be an error. */ + if (errno == EAGAIN) return; + sysErrorBelch("sendFdWakeup write"); + stg_exit(EXIT_FAILURE); + } +} + +void collectFdWakeup(int wakeup_fd_r) +{ + int res; +#if defined(HAVE_EVENTFD) + uint64_t buf; + /* eventfd combines events into one counter, so a single read is enough */ + res = read(wakeup_fd_r, &buf, 8); +#else + /* Drain the pipe buffer. Multiple wakeup notifications could + * have been sent before we have a chance to collect them. + */ + uint64_t buf; + do { + res = read(wakeup_fd_r, &buf, 8); + } while (res == 8); +#endif + if (RTS_UNLIKELY(res < 0)) { + /* After the first pipe read, it could block */ + if (errno == EAGAIN) return; + sysErrorBelch("collectFdWakeup read"); + stg_exit(EXIT_FAILURE); + } +} ===================================== rts/posix/FdWakeup.h ===================================== @@ -0,0 +1,27 @@ +/* ----------------------------------------------------------------------------- + * + * (c) The GHC Team 2025 + * + * Utilities for a simple fd-based cross-thread wakeup mechanism. + * + * This is used in I/O managers, to provide a mechanism to wake them when they + * are blocked waiting on fds and timeouts. The mechanism works by including + * the read end fd into the set of fds the I/O manager waits on, and when a + * wake up is needed, the write end fd is used. + * + * Prototypes for functions in FdWakeup.c + * + * -------------------------------------------------------------------------*/ + +#pragma once + +#include "BeginPrivate.h" + +void newFdWakeup(int *fd_r, int *fd_w); +void closeFdWakeup(int fd_r, int fd_w); + +void sendFdWakeup(int fd_w); +void collectFdWakeup(int fd_r); + +#include "EndPrivate.h" + ===================================== rts/posix/MIO.c ===================================== @@ -0,0 +1,163 @@ +/* ----------------------------------------------------------------------------- + * + * (c) The GHC Team, 1998-2005 + * + * Signal processing / handling. + * + * ---------------------------------------------------------------------------*/ + +#include "rts/PosixSource.h" +#include "Rts.h" + +#include "Schedule.h" +#include "RtsUtils.h" +#include "Prelude.h" +#include "ThreadLabels.h" + +#include "MIO.h" +#include "IOManager.h" +#include "IOManagerInternals.h" + +#if defined(HAVE_ERRNO_H) +# include <errno.h> +#endif + +#include <stdlib.h> +#include <unistd.h> + +// Here's the pipe into which we will send our signals +static int io_manager_wakeup_fd = -1; +static int timer_manager_control_wr_fd = -1; +// TODO: Eliminate these globals. Put then into the CapIOManager, but the +// problem is these are shared across all caps, not per cap. + +#define IO_MANAGER_WAKEUP 0xff +#define IO_MANAGER_DIE 0xfe +#define IO_MANAGER_SYNC 0xfd + +void setTimerManagerControlFd(int fd) { + RELAXED_STORE(&timer_manager_control_wr_fd, fd); +} + +void +setIOManagerWakeupFd (int fd) +{ + // only called when THREADED_RTS, but unconditionally + // compiled here because GHC.Event.Control depends on it. + SEQ_CST_STORE(&io_manager_wakeup_fd, fd); +} + +#if defined(THREADED_RTS) +void timerManagerNotifySignal(int sig, siginfo_t *info) +{ + StgWord8 buf[sizeof(siginfo_t) + 1]; + int r; + + buf[0] = sig; + if (info == NULL) { + // info may be NULL on Solaris (see #3790) + memset(buf+1, 0, sizeof(siginfo_t)); + } else { + memcpy(buf+1, info, sizeof(siginfo_t)); + } + + int timer_control_fd = RELAXED_LOAD(&timer_manager_control_wr_fd); + if (0 <= timer_control_fd) + { + r = write(timer_control_fd, buf, sizeof(siginfo_t)+1); + if (r == -1 && errno == EAGAIN) { + errorBelch("lost signal due to full pipe: %d\n", sig); + } + } + + // If the IO manager hasn't told us what the FD of the write end + // of its pipe is, there's not much we can do here, so just ignore + // the signal.. +} +#endif + + +/* ----------------------------------------------------------------------------- + * Wake up at least one IO or timer manager HS thread. + * -------------------------------------------------------------------------- */ +void +ioManagerWakeup (void) +{ + int r; + const int wakeup_fd = SEQ_CST_LOAD(&io_manager_wakeup_fd); + // Wake up the IO Manager thread by sending a byte down its pipe + if (wakeup_fd >= 0) { +#if defined(HAVE_EVENTFD) + StgWord64 n = (StgWord64)IO_MANAGER_WAKEUP; + r = write(wakeup_fd, (char *) &n, 8); +#else + StgWord8 byte = (StgWord8)IO_MANAGER_WAKEUP; + r = write(wakeup_fd, &byte, 1); +#endif + /* N.B. If the TimerManager is shutting down as we run this + * then there is a possibility that our first read of + * io_manager_wakeup_fd is non-negative, but before we get to the + * write the file is closed. If this occurs, io_manager_wakeup_fd + * will be written into with -1 (GHC.Event.Control does this prior + * to closing), so checking this allows us to distinguish this case. + * To ensure we observe the correct ordering, we declare the + * io_manager_wakeup_fd as volatile. + * Since this is not an error condition, we do not print the error + * message in this case. + */ + if (r == -1 && SEQ_CST_LOAD(&io_manager_wakeup_fd) >= 0) { + sysErrorBelch("ioManagerWakeup: write"); + } + } +} + +#if defined(THREADED_RTS) +void +ioManagerDie (void) +{ + StgWord8 byte = (StgWord8)IO_MANAGER_DIE; + uint32_t i; + int r; + + { + // Shut down timer manager + const int fd = RELAXED_LOAD(&timer_manager_control_wr_fd); + if (0 <= fd) { + r = write(fd, &byte, 1); + if (r == -1) { sysErrorBelch("ioManagerDie: write"); } + RELAXED_STORE(&timer_manager_control_wr_fd, -1); + } + } + + { + // Shut down IO managers + for (i=0; i < getNumCapabilities(); i++) { + const int fd = RELAXED_LOAD(&getCapability(i)->iomgr->control_fd); + if (0 <= fd) { + r = write(fd, &byte, 1); + if (r == -1) { sysErrorBelch("ioManagerDie: write"); } + RELAXED_STORE(&getCapability(i)->iomgr->control_fd, -1); + } + } + } +} + +void +ioManagerStartCap (Capability **cap) +{ + rts_evalIO(cap,ensureIOManagerIsRunning_closure,NULL); +} + +void +ioManagerStart (void) +{ + // Make sure the IO manager thread is running + Capability *cap; + if (SEQ_CST_LOAD(&timer_manager_control_wr_fd) < 0 || SEQ_CST_LOAD(&io_manager_wakeup_fd) < 0) { + cap = rts_lock(); + ioManagerStartCap(&cap); + rts_unlock(cap); + } +} +#endif + ===================================== rts/posix/MIO.h ===================================== @@ -0,0 +1,30 @@ +/* ----------------------------------------------------------------------------- + * + * (c) The GHC Team, 1998-2005 + * + * Signal processing / handling. + * + * ---------------------------------------------------------------------------*/ + +#pragma once + +#include "IOManager.h" + +#if defined(HAVE_SIGNAL_H) +# include <signal.h> +#endif + +#include "BeginPrivate.h" + +/* Communicating with the IO manager thread (see GHC.Conc). + */ +void ioManagerWakeup (void); +#if defined(THREADED_RTS) +void ioManagerDie (void); +void ioManagerStart (void); +void ioManagerStartCap (/* inout */ Capability **cap); + +void timerManagerNotifySignal(int sig, siginfo_t *info); +#endif + +#include "EndPrivate.h" ===================================== rts/posix/Poll.c ===================================== @@ -41,6 +41,7 @@ #include "IOManagerInternals.h" #include "Timeout.h" +#include "FdWakeup.h" /****************************************************************************** @@ -107,8 +108,9 @@ timeout (if any) as the poll() timeout parameter. The CapIOManager structure for this I/O manager contains: ClosureTable aiop_table; - struct pollfd *aiop_poll_table; + struct pollfd *aiop_poll_table, *full_poll_table; StgTimeoutQueue *timeout_queue; + int wakeup_fd_r, wakeup_fd_w; We also support the Linux-specific ppoll API which supports higher resolution time delays -- nanoseconds rather than milliseconds as in classic poll(). It @@ -117,6 +119,15 @@ also allows the signal mask to be adjusted, but we do not make use of this. int ppoll(struct pollfd *fds, nfds_t nfds, const struct timespec *tmo_p, const sigset_t *sigmask); +We have both aiop_poll_table and full_poll_table. This is to cope with needing +to wait on the special extra file descriptor wakeup_fd_r. This fd is used to +support waking the I/O manager when we are blocked in a poll call. This +requires waiting on an extra fd that has no corresponding entry in the +aiop_table. To manage this quirk, we alias the aiop_poll_table to be the tail +of the full_poll_table and have the first entry of the full_poll_table be the +wakeup_fd_r. This means the aiop_poll_table indicies match up exactly with the +aiop_table, but still allows the full_poll_table to have an extra entry. + ******************************************************************************/ /* Forward declarations */ @@ -129,8 +140,31 @@ static void reportPollError(int res, nfds_t nfds) STG_NORETURN; void initCapabilityIOManagerPoll(CapIOManager *iomgr) { initClosureTable(&iomgr->aiop_table, ClosureTableCompact); - iomgr->aiop_poll_table = NULL; iomgr->timeout_queue = emptyTimeoutQueue(); + + newFdWakeup(&iomgr->wakeup_fd_r, &iomgr->wakeup_fd_w); + + iomgr->full_poll_table = stgMallocBytes(sizeof(struct pollfd) /* size 1 */, + "initCapabilityIOManagerPoll"); + iomgr->full_poll_table[0] = (struct pollfd) { + .fd = iomgr->wakeup_fd_r, + .events = POLLIN, + .revents = 0 + }; + iomgr->aiop_poll_table = iomgr->full_poll_table+1; /* hence empty */ +} + + +void freeCapabilityIOManagerPoll(CapIOManager *iomgr) +{ + stgFree(iomgr->full_poll_table); + closeFdWakeup(iomgr->wakeup_fd_r, iomgr->wakeup_fd_w); +} + + +void wakeupIOManagerPoll(CapIOManager *iomgr) +{ + sendFdWakeup(iomgr->wakeup_fd_w); } @@ -275,7 +309,7 @@ static void notifyIOCompletion(CapIOManager *iomgr, StgAsyncIOOp *aiop) } -static void processIOCompletions(CapIOManager *iomgr, int ncompletions) +static bool processIOCompletions(CapIOManager *iomgr, int ncompletions) { /* The scheme we use with poll is that we have a dense poll table, and a * corresponding table that maps to the closure table index. The poll @@ -285,6 +319,19 @@ static void processIOCompletions(CapIOManager *iomgr, int ncompletions) */ debugTrace(DEBUG_iomanager, "processIOCompletions(ncompletions = %d)", ncompletions); + + bool wakeup; + /* If the wakeup_fd_r is ready, collect it */ + if (iomgr->full_poll_table[0].revents) { + ASSERT(iomgr->full_poll_table[0].fd == iomgr->wakeup_fd_r); + collectFdWakeup(iomgr->wakeup_fd_r); + ncompletions--; + wakeup = true; + debugTrace(DEBUG_iomanager, "Received wakeup in poll I/O manager."); + } else { + wakeup = false; + } + struct pollfd *aiop_poll_table = iomgr->aiop_poll_table; int n = ncompletions; int i = 0; @@ -337,11 +384,14 @@ static void processIOCompletions(CapIOManager *iomgr, int ncompletions) i++; } } + return wakeup; } void pollCompletedTimeoutsOrIOPoll(CapIOManager *iomgr) { + ASSERT(iomgr->aiop_poll_table == iomgr->full_poll_table+1); + if (!isEmptyTimeoutQueue(iomgr->timeout_queue)) { Time now = getProcessElapsedTime(); processTimeoutCompletions(iomgr, now); @@ -349,20 +399,20 @@ void pollCompletedTimeoutsOrIOPoll(CapIOManager *iomgr) if (!isEmptyClosureTable(&iomgr->aiop_table)) { - nfds_t nfds = sizeClosureTable(&iomgr->aiop_table); + nfds_t nfds = sizeClosureTable(&iomgr->aiop_table) + 1; /* Poll for I/O readiness, without waiting. */ #if defined(HAVE_DECL_PPOLL) && HAVE_DECL_PPOLL == 1 /* We could use poll here, since we use no timeout, but for consistency we use the same syscall as at the other call site. */ struct timespec tv = (struct timespec) { .tv_sec = 0, .tv_nsec = 0 }; - int res = ppoll(iomgr->aiop_poll_table, nfds, &tv, NULL); + int res = ppoll(iomgr->full_poll_table, nfds, &tv, NULL); debugTrace(DEBUG_iomanager, "ppoll(nfds = %d, timeout.sec = 0, timeout.nsec = 0) = %d", nfds, res); #else - int res = poll(iomgr->aiop_poll_table, nfds, 0); + int res = poll(iomgr->full_poll_table, nfds, 0); debugTrace(DEBUG_iomanager, "poll(nfds = %d, timeout_ms = 0) = %d", @@ -390,6 +440,10 @@ void pollCompletedTimeoutsOrIOPoll(CapIOManager *iomgr) void awaitCompletedTimeoutsOrIOPoll(CapIOManager *iomgr) { + bool wakeup = false; /* got woken up via wakeupIOManager */ + + ASSERT(iomgr->aiop_poll_table == iomgr->full_poll_table+1); + /* Loop until we've woken up some threads. This loop is needed because the * poll() timing isn't accurate, we sometimes sleep for a while but not * long enough to wake up a thread in a threadDelay. Or we may need to @@ -422,9 +476,9 @@ void awaitCompletedTimeoutsOrIOPoll(CapIOManager *iomgr) #endif /* Check for I/O readiness, possibly waiting. */ - nfds_t nfds = sizeClosureTable(&iomgr->aiop_table); + nfds_t nfds = sizeClosureTable(&iomgr->aiop_table) + 1; #if defined(HAVE_DECL_PPOLL) && HAVE_DECL_PPOLL == 1 - int res = ppoll(iomgr->aiop_poll_table, nfds, timeout_ns, NULL); + int res = ppoll(iomgr->full_poll_table, nfds, timeout_ns, NULL); debugTrace(DEBUG_iomanager, "ppoll(nfds = %d, timeout.sec = %d, timeout.nsec = %d) = %d", @@ -432,7 +486,7 @@ void awaitCompletedTimeoutsOrIOPoll(CapIOManager *iomgr) timeout_ns == NULL ? 0 : timeout_ns->tv_nsec, res); #else - int res = poll(iomgr->aiop_poll_table, nfds, timeout_ms); + int res = poll(iomgr->full_poll_table, nfds, timeout_ms); debugTrace(DEBUG_iomanager, "poll(nfds = %d, timeout_ms = %d) = %d", @@ -454,7 +508,7 @@ void awaitCompletedTimeoutsOrIOPoll(CapIOManager *iomgr) } else if (res > 0) { int ncompletions = res; ASSERT(ncompletions <= (int)nfds); - processIOCompletions(iomgr, ncompletions); + wakeup = processIOCompletions(iomgr, ncompletions); } else if (errno == EINTR) { /* We got interrupted by a signal. In the non-threaded RTS, if the @@ -479,6 +533,7 @@ void awaitCompletedTimeoutsOrIOPoll(CapIOManager *iomgr) } } while (emptyRunQueue(iomgr->cap) + && !wakeup && (getSchedState() == SCHED_RUNNING)); } @@ -508,13 +563,17 @@ static bool enlargeTables(CapIOManager *iomgr) bool ok = enlargeClosureTable(iomgr->cap, &iomgr->aiop_table, newcapacity); if (RTS_UNLIKELY(!ok)) return false; - /* Update the auxiliary aiop_poll_table to match */ - struct pollfd *aiop_poll_table; - aiop_poll_table = stgReallocBytes(iomgr->aiop_poll_table, - sizeof(struct pollfd) * newcapacity, - "Poll.c: enlargeTables"); - iomgr->aiop_poll_table = aiop_poll_table; + /* Update the auxiliary aiop_poll_table to match. The full_poll_table is + * one bigger than the aiop_poll_table, since it has an extra entry at the + * front for wakeup_fd_r, with no corresponding aiop. */ + iomgr->full_poll_table = + stgReallocBytes(iomgr->full_poll_table, + sizeof(struct pollfd) * (newcapacity+1), + "Poll.c: enlargeTables"); + iomgr->aiop_poll_table = iomgr->full_poll_table+1; + /* Initialise the new part of the aiop_poll_table */ + struct pollfd *aiop_poll_table = iomgr->aiop_poll_table; for (int i = oldcapacity; i < newcapacity; i++) { aiop_poll_table[i] = (struct pollfd) { .fd = -1, ===================================== rts/posix/Poll.h ===================================== @@ -17,6 +17,8 @@ #if defined(IOMGR_ENABLED_POLL) void initCapabilityIOManagerPoll(CapIOManager *iomgr); +void freeCapabilityIOManagerPoll(CapIOManager *iomgr); +void wakeupIOManagerPoll(CapIOManager *iomgr); /* Synchronous I/O and timer operations */ bool syncIOWaitReadyPoll(CapIOManager *iomgr, StgTSO *tso, ===================================== rts/posix/Select.c ===================================== @@ -22,6 +22,7 @@ #include "IOManagerInternals.h" #include "Stats.h" #include "GetTime.h" +#include "FdWakeup.h" # if defined(HAVE_SYS_SELECT_H) # include <sys/select.h> @@ -54,6 +55,25 @@ #define TimeToLowResTimeRoundUp(t) (t) #endif +void initCapabilityIOManagerSelect(CapIOManager *iomgr) +{ + iomgr->blocked_queue_hd = END_TSO_QUEUE; + iomgr->blocked_queue_tl = END_TSO_QUEUE; + iomgr->sleeping_queue = END_TSO_QUEUE; + + newFdWakeup(&iomgr->wakeup_fd_r, &iomgr->wakeup_fd_w); +} + +void freeCapabilityIOManagerSelect(CapIOManager *iomgr) +{ + closeFdWakeup(iomgr->wakeup_fd_r, iomgr->wakeup_fd_w); +} + +void wakeupIOManagerSelect(CapIOManager *iomgr) +{ + sendFdWakeup(iomgr->wakeup_fd_w); +} + /* * Return the time since the program started, in LowResTime, * rounded down. @@ -225,6 +245,7 @@ awaitCompletedTimeoutsOrIOSelect(CapIOManager *iomgr, bool wait) bool seen_bad_fd = false; struct timeval tv, *ptv; LowResTime now; + bool wakeup = false; /* got woken up via wakeupIOManager */ IF_DEBUG(scheduler, debugBelch("scheduler: checking for threads blocked on I/O"); @@ -252,6 +273,13 @@ awaitCompletedTimeoutsOrIOSelect(CapIOManager *iomgr, bool wait) FD_ZERO(&rfd); FD_ZERO(&wfd); + /* We're always interested in our wakeup fd */ + { + int fd = iomgr->wakeup_fd_r; + maxfd = (fd > maxfd) ? fd : maxfd; + FD_SET(fd, &rfd); + } + for(tso = iomgr->blocked_queue_hd; tso != END_TSO_QUEUE; tso = next) { @@ -376,6 +404,13 @@ awaitCompletedTimeoutsOrIOSelect(CapIOManager *iomgr, bool wait) } } + /* If the wakeup_fd_r is ready, collect it */ + if (FD_ISSET(iomgr->wakeup_fd_r, &rfd)) { + collectFdWakeup(iomgr->wakeup_fd_r); + wakeup = true; + debugTrace(DEBUG_iomanager, "Received wakeup in select I/O manager."); + } + /* Step through the waiting queue, unblocking every thread that now has * a file descriptor in a ready state. */ @@ -458,7 +493,8 @@ awaitCompletedTimeoutsOrIOSelect(CapIOManager *iomgr, bool wait) } } while (wait && getSchedState() == SCHED_RUNNING - && emptyRunQueue(iomgr->cap)); + && emptyRunQueue(iomgr->cap) + && !wakeup); } #endif /* IOMGR_ENABLED_SELECT */ ===================================== rts/posix/Select.h ===================================== @@ -15,6 +15,10 @@ typedef StgWord LowResTime; LowResTime getDelayTarget (HsInt us); +void initCapabilityIOManagerSelect(CapIOManager *iomgr); +void freeCapabilityIOManagerSelect(CapIOManager *iomgr); +void wakeupIOManagerSelect(CapIOManager *iomgr); + void awaitCompletedTimeoutsOrIOSelect(CapIOManager *iomgr, bool wait); #include "EndPrivate.h" ===================================== rts/posix/Signals.c ===================================== @@ -9,21 +9,12 @@ #include "rts/PosixSource.h" #include "Rts.h" -#include "Schedule.h" #include "RtsSignals.h" -#include "Signals.h" -#include "IOManager.h" #include "RtsUtils.h" +#include "Schedule.h" #include "Prelude.h" -#include "Ticker.h" #include "ThreadLabels.h" -#include "Libdw.h" - -/* TODO: eliminate this include. This file should be about signals, not be - * part of an I/O manager implementation. The code here that are really part - * of an I/O manager should be moved into an appropriate I/O manager impl. - */ -#include "IOManagerInternals.h" +#include "MIO.h" #if defined(alpha_HOST_ARCH) # if defined(linux_HOST_OS) @@ -45,10 +36,6 @@ # include <errno.h> #endif -#if defined(HAVE_EVENTFD_H) -# include <sys/eventfd.h> -#endif - #if defined(HAVE_TERMIOS_H) #include <termios.h> #endif @@ -134,110 +121,6 @@ more_handlers(int sig) nHandlers = sig + 1; } -// Here's the pipe into which we will send our signals -static int io_manager_wakeup_fd = -1; -static int timer_manager_control_wr_fd = -1; - -#define IO_MANAGER_WAKEUP 0xff -#define IO_MANAGER_DIE 0xfe -#define IO_MANAGER_SYNC 0xfd - -void setTimerManagerControlFd(int fd) { - RELAXED_STORE(&timer_manager_control_wr_fd, fd); -} - -void -setIOManagerWakeupFd (int fd) -{ - // only called when THREADED_RTS, but unconditionally - // compiled here because GHC.Event.Control depends on it. - SEQ_CST_STORE(&io_manager_wakeup_fd, fd); -} - -/* ----------------------------------------------------------------------------- - * Wake up at least one IO or timer manager HS thread. - * -------------------------------------------------------------------------- */ -void -ioManagerWakeup (void) -{ - int r; - const int wakeup_fd = SEQ_CST_LOAD(&io_manager_wakeup_fd); - // Wake up the IO Manager thread by sending a byte down its pipe - if (wakeup_fd >= 0) { -#if defined(HAVE_EVENTFD) - StgWord64 n = (StgWord64)IO_MANAGER_WAKEUP; - r = write(wakeup_fd, (char *) &n, 8); -#else - StgWord8 byte = (StgWord8)IO_MANAGER_WAKEUP; - r = write(wakeup_fd, &byte, 1); -#endif - /* N.B. If the TimerManager is shutting down as we run this - * then there is a possibility that our first read of - * io_manager_wakeup_fd is non-negative, but before we get to the - * write the file is closed. If this occurs, io_manager_wakeup_fd - * will be written into with -1 (GHC.Event.Control does this prior - * to closing), so checking this allows us to distinguish this case. - * To ensure we observe the correct ordering, we declare the - * io_manager_wakeup_fd as volatile. - * Since this is not an error condition, we do not print the error - * message in this case. - */ - if (r == -1 && SEQ_CST_LOAD(&io_manager_wakeup_fd) >= 0) { - sysErrorBelch("ioManagerWakeup: write"); - } - } -} - -#if defined(THREADED_RTS) -void -ioManagerDie (void) -{ - StgWord8 byte = (StgWord8)IO_MANAGER_DIE; - uint32_t i; - int r; - - { - // Shut down timer manager - const int fd = RELAXED_LOAD(&timer_manager_control_wr_fd); - if (0 <= fd) { - r = write(fd, &byte, 1); - if (r == -1) { sysErrorBelch("ioManagerDie: write"); } - RELAXED_STORE(&timer_manager_control_wr_fd, -1); - } - } - - { - // Shut down IO managers - for (i=0; i < getNumCapabilities(); i++) { - const int fd = RELAXED_LOAD(&getCapability(i)->iomgr->control_fd); - if (0 <= fd) { - r = write(fd, &byte, 1); - if (r == -1) { sysErrorBelch("ioManagerDie: write"); } - RELAXED_STORE(&getCapability(i)->iomgr->control_fd, -1); - } - } - } -} - -void -ioManagerStartCap (Capability **cap) -{ - rts_evalIO(cap,ensureIOManagerIsRunning_closure,NULL); -} - -void -ioManagerStart (void) -{ - // Make sure the IO manager thread is running - Capability *cap; - if (SEQ_CST_LOAD(&timer_manager_control_wr_fd) < 0 || SEQ_CST_LOAD(&io_manager_wakeup_fd) < 0) { - cap = rts_lock(); - ioManagerStartCap(&cap); - rts_unlock(cap); - } -} -#endif - #if !defined(THREADED_RTS) #define N_PENDING_HANDLERS 16 @@ -260,31 +143,9 @@ generic_handler(int sig USED_IF_THREADS, void *p STG_UNUSED) { #if defined(THREADED_RTS) - - StgWord8 buf[sizeof(siginfo_t) + 1]; - int r; - - buf[0] = sig; - if (info == NULL) { - // info may be NULL on Solaris (see #3790) - memset(buf+1, 0, sizeof(siginfo_t)); - } else { - memcpy(buf+1, info, sizeof(siginfo_t)); - } - - int timer_control_fd = RELAXED_LOAD(&timer_manager_control_wr_fd); - if (0 <= timer_control_fd) - { - r = write(timer_control_fd, buf, sizeof(siginfo_t)+1); - if (r == -1 && errno == EAGAIN) { - errorBelch("lost signal due to full pipe: %d\n", sig); - } - } - - // If the IO manager hasn't told us what the FD of the write end - // of its pipe is, there's not much we can do here, so just ignore - // the signal.. - + //TODO: This calls MIO directly. We should go via IOManager API. + // The IOManager API should be extended to cover signals. + timerManagerNotifySignal(sig, info); #else /* not THREADED_RTS */ /* Can't call allocate from here. Probably can't call malloc ===================================== rts/posix/Signals.h ===================================== @@ -27,18 +27,6 @@ void startSignalHandlers(Capability *cap); void install_vtalrm_handler(int sig, TickProc handle_tick); -/* Communicating with the IO manager thread (see GHC.Conc). - * - * TODO: these I/O manager things are not related to signals and ought to live - * elsewhere, e.g. in a module specifically for the I/O manager. - */ -void ioManagerWakeup (void); -#if defined(THREADED_RTS) -void ioManagerDie (void); -void ioManagerStart (void); -void ioManagerStartCap (/* inout */ Capability **cap); -#endif - extern StgInt *signal_handlers; #include "EndPrivate.h" ===================================== rts/rts.cabal ===================================== @@ -569,6 +569,7 @@ library wasm/OSThreads.c wasm/JSFFI.c wasm/JSFFIGlobals.c + posix/FdWakeup.c posix/Select.c posix/Poll.c posix/Timeout.c @@ -581,6 +582,8 @@ library posix/Ticker.c posix/OSMem.c posix/OSThreads.c + posix/FdWakeup.c + posix/MIO.c posix/Poll.c posix/Select.c posix/Signals.c View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/0810b12fe3b2f9ef216752e76edfe8… -- View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/0810b12fe3b2f9ef216752e76edfe8… You're receiving this email because of your account on gitlab.haskell.org.
1 0
0 0
[Git][ghc/ghc][wip/T26875] ghc-internal: avoid depending on GHC.Internal.Control.Monad.Fix
by Teo Camarasu (@teo) 18 Feb '26

18 Feb '26
Teo Camarasu pushed to branch wip/T26875 at Glasgow Haskell Compiler / GHC Commits: cab1bead by Teo Camarasu at 2026-02-18T20:28:03+00:00 ghc-internal: avoid depending on GHC.Internal.Control.Monad.Fix This module contains the definition of MonadFix, since we want an instance for IO, that instance requires a lot of machinery and we want to avoid an orphan instance, this will naturally be quite high up in the dependency graph. So we want to avoid other modules depending on it as far as possible. Resolves #26875 - - - - - 13 changed files: - libraries/base/src/System/IO.hs - libraries/ghc-internal/src/GHC/Internal/Control/Monad/Fix.hs - libraries/ghc-internal/src/GHC/Internal/Control/Monad/ST/Lazy/Imp.hs - libraries/ghc-internal/src/GHC/Internal/Data/Functor/Identity.hs - libraries/ghc-internal/src/GHC/Internal/System/IO.hs - libraries/ghc-internal/src/GHC/Internal/TH/Monad.hs - testsuite/tests/ghci/scripts/ListTuplePunsPpr.stdout - testsuite/tests/ghci/scripts/T4175.stdout - testsuite/tests/interface-stability/base-exports.stdout - testsuite/tests/interface-stability/base-exports.stdout-javascript-unknown-ghcjs - testsuite/tests/interface-stability/base-exports.stdout-mingw32 - testsuite/tests/interface-stability/base-exports.stdout-ws-32 - testsuite/tests/mdo/should_fail/mdofail006.stderr Changes: ===================================== libraries/base/src/System/IO.hs ===================================== @@ -185,6 +185,7 @@ module System.IO ) where import GHC.Internal.System.IO +import GHC.Internal.Control.Monad.Fix (fixIO) -- $locking -- Implementations should enforce as far as possible, at least locally to the ===================================== libraries/ghc-internal/src/GHC/Internal/Control/Monad/Fix.hs ===================================== @@ -24,7 +24,8 @@ module GHC.Internal.Control.Monad.Fix ( MonadFix(mfix), - fix + fix, + fixIO, ) where import GHC.Internal.Data.Either @@ -35,11 +36,17 @@ import GHC.Internal.Data.Monoid ( Monoid, Dual(..), Sum(..), Product(..) import GHC.Internal.Data.NonEmpty ( NonEmpty(..) ) import GHC.Internal.Data.Ord ( Down(..) ) import GHC.Internal.Data.Tuple ( Solo(..), snd ) -import GHC.Internal.Base ( Monad, errorWithoutStackTrace, (.) ) +import GHC.Internal.Base ( IO, Monad, errorWithoutStackTrace, (.), return, liftM ) import GHC.Internal.Generics import GHC.Internal.List ( head, drop ) import GHC.Internal.Control.Monad.ST.Imp -import GHC.Internal.System.IO +import qualified GHC.Internal.Control.Monad.ST.Lazy.Imp as Lazy +import GHC.Internal.Data.Functor.Identity (Identity(..)) +import GHC.Internal.MVar +import GHC.Internal.IO.Unsafe +import GHC.Internal.IO.Exception +import GHC.Internal.TH.Monad +import GHC.Internal.Control.Exception -- | Monads having fixed points with a \'knot-tying\' semantics. -- Instances of 'MonadFix' should satisfy the following laws: @@ -102,6 +109,86 @@ instance MonadFix NonEmpty where instance MonadFix IO where mfix = fixIO +-- --------------------------------------------------------------------------- +-- fixIO + +-- | The implementation of 'Control.Monad.Fix.mfix' for 'IO'. +-- +-- This operation may fail with: +-- +-- * 'FixIOException' if the function passed to 'fixIO' inspects its argument. +-- +-- ==== __Examples__ +-- +-- the IO-action is only executed once. The recursion is only on the values. +-- +-- >>> take 3 <$> fixIO (\x -> putStr ":D" >> (:x) <$> readLn @Int) +-- :D +-- 2 +-- [2,2,2] +-- +-- If we are strict in the value, just as with 'Data.Function.fix', we do not get termination: +-- +-- >>> fixIO (\x -> putStr x >> pure ('x' : x)) +-- * hangs forever * +-- +-- We can tie the knot of a structure within 'IO' using 'fixIO': +-- +-- @ +-- data Node = MkNode Int (IORef Node) +-- +-- foo :: IO () +-- foo = do +-- p \<- fixIO (\p -> newIORef (MkNode 0 p)) +-- q <- output p +-- r <- output q +-- _ <- output r +-- pure () +-- +-- output :: IORef Node -> IO (IORef Node) +-- output ref = do +-- MkNode x p <- readIORef ref +-- print x +-- pure p +-- @ +-- +-- >>> foo +-- 0 +-- 0 +-- 0 +fixIO :: (a -> IO a) -> IO a +fixIO k = do + m <- newEmptyMVar + ans <- unsafeDupableInterleaveIO + (readMVar m `catch` \BlockedIndefinitelyOnMVar -> + throwIO FixIOException) + result <- k ans + putMVar m result + return result + +-- Note [Blackholing in fixIO] +-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +-- We do our own explicit black holing here, because GHC's lazy +-- blackholing isn't enough. In an infinite loop, GHC may run the IO +-- computation a few times before it notices the loop, which is wrong. +-- +-- NOTE2: the explicit black-holing with an IORef ran into trouble +-- with multiple threads (see #5421), so now we use an MVar. We used +-- to use takeMVar with unsafeInterleaveIO. This, however, uses noDuplicate#, +-- which is not particularly cheap. Better to use readMVar, which can be +-- performed in multiple threads safely, and to use unsafeDupableInterleaveIO +-- to avoid the noDuplicate cost. +-- +-- What we'd ideally want is probably an IVar, but we don't quite have those. +-- STM TVars look like an option at first, but I don't think they are: +-- we'd need to be able to write to the variable in an IO context, which can +-- only be done using 'atomically', and 'atomically' is not allowed within +-- unsafePerformIO. We can't know if someone will try to use the result +-- of fixIO with unsafePerformIO! +-- +-- See also System.IO.Unsafe.unsafeFixIO. +-- + -- | @since base-2.01 instance MonadFix ((->) r) where mfix f = \ r -> let a = f a r in a @@ -116,6 +203,10 @@ instance MonadFix (Either e) where instance MonadFix (ST s) where mfix = fixST +-- | @since base-2.01 +instance MonadFix (Lazy.ST s) where + mfix = Lazy.fixST + -- Instances of Data.Monoid wrappers -- | @since base-4.8.0.0 @@ -171,3 +262,24 @@ instance (MonadFix f, MonadFix g) => MonadFix (f :*: g) where -- | @since base-4.12.0.0 instance MonadFix Down where mfix f = Down (fix (getDown . f)) + + +-- | @since base-4.8.0.0 +instance MonadFix Identity where + mfix f = Identity (fix (runIdentity . f)) + +-- | If the function passed to 'mfix' inspects its argument, +-- the resulting action will throw a 'FixIOException'. +-- +-- @since 2.17.0.0 +instance MonadFix Q where + -- We use the same blackholing approach as in fixIO. + -- See Note [Blackholing in fixIO]. + mfix k = do + m <- runIO newEmptyMVar + ans <- runIO (unsafeDupableInterleaveIO + (readMVar m `catch` \BlockedIndefinitelyOnMVar -> + throwIO FixIOException)) + result <- k ans + runIO (putMVar m result) + return result ===================================== libraries/ghc-internal/src/GHC/Internal/Control/Monad/ST/Lazy/Imp.hs ===================================== @@ -37,7 +37,6 @@ module GHC.Internal.Control.Monad.ST.Lazy.Imp ( unsafeIOToST ) where -import GHC.Internal.Control.Monad.Fix import GHC.Internal.Data.Tuple import qualified GHC.Internal.Control.Monad.ST.Imp as ST @@ -210,10 +209,6 @@ fixST m = ST (\ s -> -- itself is demanded directly in the `let` body. See also -- Note [Lazy ST: not producing lazy pairs]. --- | @since base-2.01 -instance MonadFix (ST s) where - mfix = fixST - -- | @since base-4.23.0.0 instance Semigroup a => Semigroup (ST s a) where (<>) = liftA2 (<>) ===================================== libraries/ghc-internal/src/GHC/Internal/Data/Functor/Identity.hs ===================================== @@ -33,7 +33,6 @@ module GHC.Internal.Data.Functor.Identity ( Identity(..), ) where -import GHC.Internal.Control.Monad.Fix import GHC.Internal.Data.Bits (Bits, FiniteBits) import GHC.Internal.Data.Coerce import GHC.Internal.Data.Foldable @@ -143,7 +142,3 @@ instance Applicative Identity where -- | @since base-4.8.0.0 instance Monad Identity where m >>= k = k (runIdentity m) - --- | @since base-4.8.0.0 -instance MonadFix Identity where - mfix f = Identity (fix (runIdentity . f)) ===================================== libraries/ghc-internal/src/GHC/Internal/System/IO.hs ===================================== @@ -20,7 +20,6 @@ module GHC.Internal.System.IO ( -- * The IO monad IO, - fixIO, -- * Files and handles @@ -258,7 +257,6 @@ import GHC.Internal.IO.Encoding import GHC.Internal.Text.Read import GHC.Internal.IO.StdHandles import GHC.Internal.Show -import GHC.Internal.MVar ----------------------------------------------------------------------------- -- Standard IO @@ -602,87 +600,6 @@ hReady h = hWaitForInput h 0 hPrint :: Show a => Handle -> a -> IO () hPrint hdl = hPutStrLn hdl . show - --- --------------------------------------------------------------------------- --- fixIO - --- | The implementation of 'Control.Monad.Fix.mfix' for 'IO'. --- --- This operation may fail with: --- --- * 'FixIOException' if the function passed to 'fixIO' inspects its argument. --- --- ==== __Examples__ --- --- the IO-action is only executed once. The recursion is only on the values. --- --- >>> take 3 <$> fixIO (\x -> putStr ":D" >> (:x) <$> readLn @Int) --- :D --- 2 --- [2,2,2] --- --- If we are strict in the value, just as with 'Data.Function.fix', we do not get termination: --- --- >>> fixIO (\x -> putStr x >> pure ('x' : x)) --- * hangs forever * --- --- We can tie the knot of a structure within 'IO' using 'fixIO': --- --- @ --- data Node = MkNode Int (IORef Node) --- --- foo :: IO () --- foo = do --- p \<- fixIO (\p -> newIORef (MkNode 0 p)) --- q <- output p --- r <- output q --- _ <- output r --- pure () --- --- output :: IORef Node -> IO (IORef Node) --- output ref = do --- MkNode x p <- readIORef ref --- print x --- pure p --- @ --- --- >>> foo --- 0 --- 0 --- 0 -fixIO :: (a -> IO a) -> IO a -fixIO k = do - m <- newEmptyMVar - ans <- unsafeDupableInterleaveIO - (readMVar m `catch` \BlockedIndefinitelyOnMVar -> - throwIO FixIOException) - result <- k ans - putMVar m result - return result - --- Note [Blackholing in fixIO] --- ~~~~~~~~~~~~~~~~~~~~~~~~~~~ --- We do our own explicit black holing here, because GHC's lazy --- blackholing isn't enough. In an infinite loop, GHC may run the IO --- computation a few times before it notices the loop, which is wrong. --- --- NOTE2: the explicit black-holing with an IORef ran into trouble --- with multiple threads (see #5421), so now we use an MVar. We used --- to use takeMVar with unsafeInterleaveIO. This, however, uses noDuplicate#, --- which is not particularly cheap. Better to use readMVar, which can be --- performed in multiple threads safely, and to use unsafeDupableInterleaveIO --- to avoid the noDuplicate cost. --- --- What we'd ideally want is probably an IVar, but we don't quite have those. --- STM TVars look like an option at first, but I don't think they are: --- we'd need to be able to write to the variable in an IO context, which can --- only be done using 'atomically', and 'atomically' is not allowed within --- unsafePerformIO. We can't know if someone will try to use the result --- of fixIO with unsafePerformIO! --- --- See also System.IO.Unsafe.unsafeFixIO. --- - -- | The function creates a temporary file in ReadWrite mode. -- The created file isn\'t deleted automatically, so you need to delete it manually. -- ===================================== libraries/ghc-internal/src/GHC/Internal/TH/Monad.hs ===================================== @@ -28,13 +28,8 @@ import Data.Data hiding (Fixity(..)) import Data.IORef import System.IO.Unsafe ( unsafePerformIO ) import Control.Monad.IO.Class (MonadIO (..)) -import Control.Monad.Fix (MonadFix (..)) -import Control.Exception (BlockedIndefinitelyOnMVar (..), catch, throwIO) -import Control.Exception.Base (FixIOException (..)) -import Control.Concurrent.MVar (newEmptyMVar, readMVar, putMVar) import System.IO ( hPutStrLn, stderr ) import qualified Data.Kind as Kind (Type) -import GHC.IO.Unsafe ( unsafeDupableInterleaveIO ) import GHC.Types (TYPE, RuntimeRep(..)) #else import GHC.Internal.Base hiding (NonEmpty(..),Type, Module, sequence) @@ -46,12 +41,8 @@ import GHC.Internal.Data.Foldable import GHC.Internal.Data.Typeable import GHC.Internal.Control.Monad.IO.Class import GHC.Internal.Control.Monad.Fail -import GHC.Internal.Control.Monad.Fix -import GHC.Internal.Control.Exception import GHC.Internal.Num import GHC.Internal.IO.Unsafe -import GHC.Internal.MVar -import GHC.Internal.IO.Exception import qualified GHC.Internal.Types as Kind (Type) #endif import GHC.Internal.ForeignSrcLang @@ -258,22 +249,6 @@ instance Semigroup a => Semigroup (Q a) where instance Monoid a => Monoid (Q a) where mempty = pure mempty --- | If the function passed to 'mfix' inspects its argument, --- the resulting action will throw a 'FixIOException'. --- --- @since 2.17.0.0 -instance MonadFix Q where - -- We use the same blackholing approach as in fixIO. - -- See Note [Blackholing in fixIO] in System.IO in base. - mfix k = do - m <- runIO newEmptyMVar - ans <- runIO (unsafeDupableInterleaveIO - (readMVar m `catch` \BlockedIndefinitelyOnMVar -> - throwIO FixIOException)) - result <- k ans - runIO (putMVar m result) - return result - ----------------------------------------------------- -- ===================================== testsuite/tests/ghci/scripts/ListTuplePunsPpr.stdout ===================================== @@ -41,10 +41,10 @@ data Tuple2 a b = (,) a b -- Defined in ‘GHC.Internal.Tuple’ instance Traversable (Tuple2 a) -- Defined in ‘GHC.Internal.Data.Traversable’ -instance Foldable (Tuple2 a) - -- Defined in ‘GHC.Internal.Data.Foldable’ instance Monoid a => Applicative (Tuple2 a) -- Defined in ‘GHC.Internal.Base’ +instance Foldable (Tuple2 a) + -- Defined in ‘GHC.Internal.Data.Foldable’ instance Functor (Tuple2 a) -- Defined in ‘GHC.Internal.Base’ instance Monoid a => Monad (Tuple2 a) -- Defined in ‘GHC.Internal.Base’ ===================================== testsuite/tests/ghci/scripts/T4175.stdout ===================================== @@ -44,8 +44,8 @@ instance Traversable Maybe -- Defined in ‘GHC.Internal.Data.Traversable’ instance MonadFail Maybe -- Defined in ‘GHC.Internal.Control.Monad.Fail’ -instance Foldable Maybe -- Defined in ‘GHC.Internal.Data.Foldable’ instance Applicative Maybe -- Defined in ‘GHC.Internal.Base’ +instance Foldable Maybe -- Defined in ‘GHC.Internal.Data.Foldable’ instance Functor Maybe -- Defined in ‘GHC.Internal.Base’ instance Monad Maybe -- Defined in ‘GHC.Internal.Base’ instance Semigroup a => Monoid (Maybe a) ===================================== testsuite/tests/interface-stability/base-exports.stdout ===================================== @@ -11553,6 +11553,7 @@ instance forall e. GHC.Internal.Control.Monad.Fix.MonadFix (GHC.Internal.Data.Ei instance forall r. GHC.Internal.Control.Monad.Fix.MonadFix ((->) r) -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Data.Monoid.First -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Types.IO -- Defined in ‘GHC.Internal.Control.Monad.Fix’ +instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Data.Functor.Identity.Identity -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Data.Monoid.Last -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix [] -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance forall (f :: * -> *) i (c :: GHC.Internal.Generics.Meta). GHC.Internal.Control.Monad.Fix.MonadFix f => GHC.Internal.Control.Monad.Fix.MonadFix (GHC.Internal.Generics.M1 i c f) -- Defined in ‘GHC.Internal.Control.Monad.Fix’ @@ -11560,14 +11561,14 @@ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Maybe.Maybe -- Def instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Base.NonEmpty -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Generics.Par1 -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Data.Semigroup.Internal.Product -- Defined in ‘GHC.Internal.Control.Monad.Fix’ +instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.TH.Monad.Q -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance forall (f :: * -> *). GHC.Internal.Control.Monad.Fix.MonadFix f => GHC.Internal.Control.Monad.Fix.MonadFix (GHC.Internal.Generics.Rec1 f) -- Defined in ‘GHC.Internal.Control.Monad.Fix’ +instance forall s. GHC.Internal.Control.Monad.Fix.MonadFix (GHC.Internal.Control.Monad.ST.Lazy.Imp.ST s) -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance forall s. GHC.Internal.Control.Monad.Fix.MonadFix (GHC.Internal.ST.ST s) -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix Solo -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Data.Semigroup.Internal.Sum -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance forall a. GHC.Internal.Base.Monoid a => GHC.Internal.Control.Monad.Fix.MonadFix ((,) a) -- Defined in ‘GHC.Internal.Control.Monad.Fix’ -instance forall s. GHC.Internal.Control.Monad.Fix.MonadFix (GHC.Internal.Control.Monad.ST.Lazy.Imp.ST s) -- Defined in ‘GHC.Internal.Control.Monad.ST.Lazy.Imp’ instance GHC.Internal.Control.Monad.Fix.MonadFix Data.Complex.Complex -- Defined in ‘Data.Complex’ -instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Data.Functor.Identity.Identity -- Defined in ‘GHC.Internal.Data.Functor.Identity’ instance [safe] forall (f :: * -> *) (g :: * -> *). (GHC.Internal.Control.Monad.Fix.MonadFix f, GHC.Internal.Control.Monad.Fix.MonadFix g) => GHC.Internal.Control.Monad.Fix.MonadFix (Data.Functor.Product.Product f g) -- Defined in ‘Data.Functor.Product’ instance GHC.Internal.Control.Monad.Fix.MonadFix Data.Semigroup.First -- Defined in ‘Data.Semigroup’ instance GHC.Internal.Control.Monad.Fix.MonadFix Data.Semigroup.Last -- Defined in ‘Data.Semigroup’ ===================================== testsuite/tests/interface-stability/base-exports.stdout-javascript-unknown-ghcjs ===================================== @@ -11580,6 +11580,7 @@ instance forall e. GHC.Internal.Control.Monad.Fix.MonadFix (GHC.Internal.Data.Ei instance forall r. GHC.Internal.Control.Monad.Fix.MonadFix ((->) r) -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Data.Monoid.First -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Types.IO -- Defined in ‘GHC.Internal.Control.Monad.Fix’ +instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Data.Functor.Identity.Identity -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Data.Monoid.Last -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix [] -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance forall (f :: * -> *) i (c :: GHC.Internal.Generics.Meta). GHC.Internal.Control.Monad.Fix.MonadFix f => GHC.Internal.Control.Monad.Fix.MonadFix (GHC.Internal.Generics.M1 i c f) -- Defined in ‘GHC.Internal.Control.Monad.Fix’ @@ -11587,14 +11588,14 @@ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Maybe.Maybe -- Def instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Base.NonEmpty -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Generics.Par1 -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Data.Semigroup.Internal.Product -- Defined in ‘GHC.Internal.Control.Monad.Fix’ +instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.TH.Monad.Q -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance forall (f :: * -> *). GHC.Internal.Control.Monad.Fix.MonadFix f => GHC.Internal.Control.Monad.Fix.MonadFix (GHC.Internal.Generics.Rec1 f) -- Defined in ‘GHC.Internal.Control.Monad.Fix’ +instance forall s. GHC.Internal.Control.Monad.Fix.MonadFix (GHC.Internal.Control.Monad.ST.Lazy.Imp.ST s) -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance forall s. GHC.Internal.Control.Monad.Fix.MonadFix (GHC.Internal.ST.ST s) -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix Solo -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Data.Semigroup.Internal.Sum -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance forall a. GHC.Internal.Base.Monoid a => GHC.Internal.Control.Monad.Fix.MonadFix ((,) a) -- Defined in ‘GHC.Internal.Control.Monad.Fix’ -instance forall s. GHC.Internal.Control.Monad.Fix.MonadFix (GHC.Internal.Control.Monad.ST.Lazy.Imp.ST s) -- Defined in ‘GHC.Internal.Control.Monad.ST.Lazy.Imp’ instance GHC.Internal.Control.Monad.Fix.MonadFix Data.Complex.Complex -- Defined in ‘Data.Complex’ -instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Data.Functor.Identity.Identity -- Defined in ‘GHC.Internal.Data.Functor.Identity’ instance [safe] forall (f :: * -> *) (g :: * -> *). (GHC.Internal.Control.Monad.Fix.MonadFix f, GHC.Internal.Control.Monad.Fix.MonadFix g) => GHC.Internal.Control.Monad.Fix.MonadFix (Data.Functor.Product.Product f g) -- Defined in ‘Data.Functor.Product’ instance GHC.Internal.Control.Monad.Fix.MonadFix Data.Semigroup.First -- Defined in ‘Data.Semigroup’ instance GHC.Internal.Control.Monad.Fix.MonadFix Data.Semigroup.Last -- Defined in ‘Data.Semigroup’ ===================================== testsuite/tests/interface-stability/base-exports.stdout-mingw32 ===================================== @@ -11811,6 +11811,7 @@ instance forall e. GHC.Internal.Control.Monad.Fix.MonadFix (GHC.Internal.Data.Ei instance forall r. GHC.Internal.Control.Monad.Fix.MonadFix ((->) r) -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Data.Monoid.First -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Types.IO -- Defined in ‘GHC.Internal.Control.Monad.Fix’ +instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Data.Functor.Identity.Identity -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Data.Monoid.Last -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix [] -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance forall (f :: * -> *) i (c :: GHC.Internal.Generics.Meta). GHC.Internal.Control.Monad.Fix.MonadFix f => GHC.Internal.Control.Monad.Fix.MonadFix (GHC.Internal.Generics.M1 i c f) -- Defined in ‘GHC.Internal.Control.Monad.Fix’ @@ -11818,14 +11819,14 @@ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Maybe.Maybe -- Def instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Base.NonEmpty -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Generics.Par1 -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Data.Semigroup.Internal.Product -- Defined in ‘GHC.Internal.Control.Monad.Fix’ +instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.TH.Monad.Q -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance forall (f :: * -> *). GHC.Internal.Control.Monad.Fix.MonadFix f => GHC.Internal.Control.Monad.Fix.MonadFix (GHC.Internal.Generics.Rec1 f) -- Defined in ‘GHC.Internal.Control.Monad.Fix’ +instance forall s. GHC.Internal.Control.Monad.Fix.MonadFix (GHC.Internal.Control.Monad.ST.Lazy.Imp.ST s) -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance forall s. GHC.Internal.Control.Monad.Fix.MonadFix (GHC.Internal.ST.ST s) -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix Solo -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Data.Semigroup.Internal.Sum -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance forall a. GHC.Internal.Base.Monoid a => GHC.Internal.Control.Monad.Fix.MonadFix ((,) a) -- Defined in ‘GHC.Internal.Control.Monad.Fix’ -instance forall s. GHC.Internal.Control.Monad.Fix.MonadFix (GHC.Internal.Control.Monad.ST.Lazy.Imp.ST s) -- Defined in ‘GHC.Internal.Control.Monad.ST.Lazy.Imp’ instance GHC.Internal.Control.Monad.Fix.MonadFix Data.Complex.Complex -- Defined in ‘Data.Complex’ -instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Data.Functor.Identity.Identity -- Defined in ‘GHC.Internal.Data.Functor.Identity’ instance [safe] forall (f :: * -> *) (g :: * -> *). (GHC.Internal.Control.Monad.Fix.MonadFix f, GHC.Internal.Control.Monad.Fix.MonadFix g) => GHC.Internal.Control.Monad.Fix.MonadFix (Data.Functor.Product.Product f g) -- Defined in ‘Data.Functor.Product’ instance GHC.Internal.Control.Monad.Fix.MonadFix Data.Semigroup.First -- Defined in ‘Data.Semigroup’ instance GHC.Internal.Control.Monad.Fix.MonadFix Data.Semigroup.Last -- Defined in ‘Data.Semigroup’ ===================================== testsuite/tests/interface-stability/base-exports.stdout-ws-32 ===================================== @@ -11553,6 +11553,7 @@ instance forall e. GHC.Internal.Control.Monad.Fix.MonadFix (GHC.Internal.Data.Ei instance forall r. GHC.Internal.Control.Monad.Fix.MonadFix ((->) r) -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Data.Monoid.First -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Types.IO -- Defined in ‘GHC.Internal.Control.Monad.Fix’ +instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Data.Functor.Identity.Identity -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Data.Monoid.Last -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix [] -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance forall (f :: * -> *) i (c :: GHC.Internal.Generics.Meta). GHC.Internal.Control.Monad.Fix.MonadFix f => GHC.Internal.Control.Monad.Fix.MonadFix (GHC.Internal.Generics.M1 i c f) -- Defined in ‘GHC.Internal.Control.Monad.Fix’ @@ -11560,14 +11561,14 @@ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Maybe.Maybe -- Def instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Base.NonEmpty -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Generics.Par1 -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Data.Semigroup.Internal.Product -- Defined in ‘GHC.Internal.Control.Monad.Fix’ +instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.TH.Monad.Q -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance forall (f :: * -> *). GHC.Internal.Control.Monad.Fix.MonadFix f => GHC.Internal.Control.Monad.Fix.MonadFix (GHC.Internal.Generics.Rec1 f) -- Defined in ‘GHC.Internal.Control.Monad.Fix’ +instance forall s. GHC.Internal.Control.Monad.Fix.MonadFix (GHC.Internal.Control.Monad.ST.Lazy.Imp.ST s) -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance forall s. GHC.Internal.Control.Monad.Fix.MonadFix (GHC.Internal.ST.ST s) -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix Solo -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Data.Semigroup.Internal.Sum -- Defined in ‘GHC.Internal.Control.Monad.Fix’ instance forall a. GHC.Internal.Base.Monoid a => GHC.Internal.Control.Monad.Fix.MonadFix ((,) a) -- Defined in ‘GHC.Internal.Control.Monad.Fix’ -instance forall s. GHC.Internal.Control.Monad.Fix.MonadFix (GHC.Internal.Control.Monad.ST.Lazy.Imp.ST s) -- Defined in ‘GHC.Internal.Control.Monad.ST.Lazy.Imp’ instance GHC.Internal.Control.Monad.Fix.MonadFix Data.Complex.Complex -- Defined in ‘Data.Complex’ -instance GHC.Internal.Control.Monad.Fix.MonadFix GHC.Internal.Data.Functor.Identity.Identity -- Defined in ‘GHC.Internal.Data.Functor.Identity’ instance [safe] forall (f :: * -> *) (g :: * -> *). (GHC.Internal.Control.Monad.Fix.MonadFix f, GHC.Internal.Control.Monad.Fix.MonadFix g) => GHC.Internal.Control.Monad.Fix.MonadFix (Data.Functor.Product.Product f g) -- Defined in ‘Data.Functor.Product’ instance GHC.Internal.Control.Monad.Fix.MonadFix Data.Semigroup.First -- Defined in ‘Data.Semigroup’ instance GHC.Internal.Control.Monad.Fix.MonadFix Data.Semigroup.Last -- Defined in ‘Data.Semigroup’ ===================================== testsuite/tests/mdo/should_fail/mdofail006.stderr ===================================== @@ -5,5 +5,5 @@ cyclic evaluation in fixIO While handling thread blocked indefinitely in an MVar operation HasCallStack backtrace: - throwIO, called at libraries/ghc-internal/src/GHC/Internal/System/IO.hs:641:37 in ghc-internal:GHC.Internal.System.IO + throwIO, called at libraries/ghc-internal/src/GHC/Internal/Control/Monad/Fix.hs:167:37 in ghc-internal:GHC.Internal.Control.Monad.Fix View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/cab1bead4d6a49644936d018f252690… -- View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/cab1bead4d6a49644936d018f252690… You're receiving this email because of your account on gitlab.haskell.org.
1 0
0 0
[Git][ghc/ghc][wip/T26875] 9 commits: Support more x86 extensions: AVX-512 {BW,DQ,VL} and GFNI
by Teo Camarasu (@teo) 18 Feb '26

18 Feb '26
Teo Camarasu pushed to branch wip/T26875 at Glasgow Haskell Compiler / GHC Commits: 14f485ee by ARATA Mizuki at 2026-02-17T09:09:24+09:00 Support more x86 extensions: AVX-512 {BW,DQ,VL} and GFNI Also, mark AVX-512 ER and PF as deprecated. AVX-512 instructions can be used for certain 64-bit integer vector operations. GFNI can be used to implement bitReverse (currently not used by NCG, but LLVM may use it). Closes #26406 Addresses #26509 - - - - - 016f79d5 by fendor at 2026-02-17T09:16:16-05:00 Hide implementation details from base exception stack traces Ensure we hide the implementation details of the exception throwing mechanisms: * `undefined` * `throwSTM` * `throw` * `throwIO` * `error` The `HasCallStackBacktrace` should always have a length of exactly 1, not showing internal implementation details in the stack trace, as these are vastly distracting to end users. CLC proposal [#387](https://github.com/haskell/core-libraries-committee/issues/387) - - - - - 4f2840f2 by Brian J. Cardiff at 2026-02-17T17:04:08-05:00 configure: Accept happy-2.2 In Jan 2026 happy-2.2 was released. The most sensible change is https://github.com/haskell/happy/issues/335 which didn't trigger in a fresh build - - - - - 10b4d364 by Duncan Coutts at 2026-02-17T17:04:52-05:00 Fix errors in the documentation of the eventlog STOP_THREAD status codes Fix the code for BlockedOnMsgThrowTo. Document all the known historical warts. Fixes issue #26867 - - - - - c5e15b8b by Phil de Joux at 2026-02-18T05:07:36-05:00 haddock: use snippets for all list examples - generate snippet output for docs - reduce font size to better fit snippets - Use only directive to guard html snippets - Add latex snippets for lists - - - - - d388bac1 by Phil de Joux at 2026-02-18T05:07:36-05:00 haddock: Place the snippet input and output together - Put the output seemingly inside the example box - - - - - 016fa306 by Samuel Thibault at 2026-02-18T05:08:35-05:00 Fix linking against libm by moving the -lm option For those systems that need -lm for getting math functions, this is currently added on the link line very early, before the object files being linked together. Newer toolchains enable --as-needed by default, which means -lm is ignored at that point because no object requires a math function yet. With such toolchains, we thus have to add -lm after the objects, so the linker actually includes libm in the link. - - - - - 68bd0805 by Teo Camarasu at 2026-02-18T05:09:19-05:00 ghc-internal: Move GHC.Internal.Data.Bool to base This is a tiny module that only defines bool :: Bool -> a -> a -> a. We can just move this to base and delete it from ghc-internal. If we want this functionality there we can just use a case statement or if-then expression. Resolves 26865 - - - - - a53d58e6 by Teo Camarasu at 2026-02-18T17:28:03+00:00 ghc-internal: avoid depending on GHC.Internal.Control.Monad.Fix This module contains the definition of MonadFix, since we want an instance for IO, that instance requires a lot of machinery and we want to avoid an orphan instance, this will naturally be quite high up in the dependency graph. So we want to avoid other modules depending on it as far as possible. Resolves #26875 - - - - - 123 changed files: - compiler/GHC/Builtin/Names.hs - compiler/GHC/CmmToAsm/Config.hs - compiler/GHC/CmmToAsm/X86/CodeGen.hs - compiler/GHC/CmmToAsm/X86/Instr.hs - compiler/GHC/CmmToAsm/X86/Ppr.hs - compiler/GHC/Driver/Config/CmmToAsm.hs - compiler/GHC/Driver/DynFlags.hs - compiler/GHC/Driver/Pipeline/Execute.hs - compiler/GHC/Driver/Session.hs - compiler/GHC/Linker/Dynamic.hs - compiler/GHC/SysTools/Cpp.hs - docs/users_guide/9.16.1-notes.rst - docs/users_guide/eventlog-formats.rst - docs/users_guide/phases.rst - docs/users_guide/using.rst - libraries/base/changelog.md - libraries/base/src/Control/Arrow.hs - libraries/base/src/Data/Bool.hs - libraries/base/src/Data/List.hs - libraries/base/src/Data/List/NubOrdSet.hs - libraries/base/src/GHC/Exts.hs - libraries/base/src/System/IO.hs - libraries/ghc-internal/ghc-internal.cabal.in - libraries/ghc-internal/src/GHC/Internal/Control/Arrow.hs - libraries/ghc-internal/src/GHC/Internal/Control/Monad/Fix.hs - libraries/ghc-internal/src/GHC/Internal/Control/Monad/ST/Lazy/Imp.hs - − libraries/ghc-internal/src/GHC/Internal/Data/Bool.hs - libraries/ghc-internal/src/GHC/Internal/Data/Foldable.hs - libraries/ghc-internal/src/GHC/Internal/Data/Function.hs - libraries/ghc-internal/src/GHC/Internal/Data/Functor/Identity.hs - libraries/ghc-internal/src/GHC/Internal/Data/Type/Bool.hs - libraries/ghc-internal/src/GHC/Internal/Data/Type/Ord.hs - libraries/ghc-internal/src/GHC/Internal/Data/Version.hs - libraries/ghc-internal/src/GHC/Internal/Exception.hs - libraries/ghc-internal/src/GHC/Internal/IO/FD.hs - libraries/ghc-internal/src/GHC/Internal/JS/Prim.hs - libraries/ghc-internal/src/GHC/Internal/STM.hs - libraries/ghc-internal/src/GHC/Internal/System/IO.hs - libraries/ghc-internal/src/GHC/Internal/System/IO/OS.hs - libraries/ghc-internal/src/GHC/Internal/TH/Lift.hs - libraries/ghc-internal/src/GHC/Internal/TH/Monad.hs - libraries/ghc-internal/src/GHC/Internal/TypeError.hs - + libraries/ghc-internal/tests/backtraces/T15395.hs - + libraries/ghc-internal/tests/backtraces/T15395.stdout - libraries/ghc-internal/tests/backtraces/all.T - libraries/ghc-internal/tests/stack-annotation/ann_frame005.stdout - m4/fptools_happy.m4 - testsuite/driver/cpu_features.py - testsuite/tests/arrows/should_compile/T21301.stderr - testsuite/tests/codeGen/should_gen_asm/all.T - + testsuite/tests/codeGen/should_gen_asm/avx512-int64-minmax.asm - + testsuite/tests/codeGen/should_gen_asm/avx512-int64-minmax.hs - + testsuite/tests/codeGen/should_gen_asm/avx512-int64-mul.asm - + testsuite/tests/codeGen/should_gen_asm/avx512-int64-mul.hs - + testsuite/tests/codeGen/should_gen_asm/avx512-word64-minmax.asm - + testsuite/tests/codeGen/should_gen_asm/avx512-word64-minmax.hs - testsuite/tests/deSugar/should_fail/DsStrictFail.stderr - testsuite/tests/deSugar/should_run/T20024.stderr - testsuite/tests/deSugar/should_run/dsrun005.stderr - testsuite/tests/deSugar/should_run/dsrun007.stderr - testsuite/tests/deSugar/should_run/dsrun008.stderr - testsuite/tests/deriving/should_run/T9576.stderr - testsuite/tests/ghci/scripts/Defer02.stderr - testsuite/tests/ghci/scripts/ListTuplePunsPpr.stdout - testsuite/tests/ghci/scripts/T15325.stderr - testsuite/tests/ghci/scripts/T4175.stdout - testsuite/tests/interface-stability/base-exports.stdout - testsuite/tests/interface-stability/base-exports.stdout-javascript-unknown-ghcjs - testsuite/tests/interface-stability/base-exports.stdout-mingw32 - testsuite/tests/interface-stability/base-exports.stdout-ws-32 - testsuite/tests/interface-stability/template-haskell-exports.stdout - testsuite/tests/mdo/should_fail/mdofail006.stderr - testsuite/tests/patsyn/should_run/ghci.stderr - testsuite/tests/quotes/LiftErrMsgDefer.stderr - testsuite/tests/safeHaskell/safeLanguage/SafeLang15.stderr - testsuite/tests/simd/should_run/all.T - testsuite/tests/type-data/should_run/T22332a.stderr - testsuite/tests/typecheck/should_run/T10284.stderr - testsuite/tests/typecheck/should_run/T13838.stderr - testsuite/tests/typecheck/should_run/T9497a-run.stderr - testsuite/tests/typecheck/should_run/T9497b-run.stderr - testsuite/tests/typecheck/should_run/T9497c-run.stderr - testsuite/tests/unsatisfiable/T23816.stderr - testsuite/tests/unsatisfiable/UnsatDefer.stderr - utils/haddock/doc/.gitignore - utils/haddock/doc/Makefile - + utils/haddock/doc/_static/haddock-custom.css - utils/haddock/doc/conf.py - utils/haddock/doc/markup.rst - + utils/haddock/doc/snippets/.gitignore - + utils/haddock/doc/snippets/Lists.hs - + utils/haddock/doc/snippets/Makefile - + utils/haddock/doc/snippets/Snippet-List-Bulleted.html - + utils/haddock/doc/snippets/Snippet-List-Bulleted.tex - + utils/haddock/doc/snippets/Snippet-List-Definition.html - + utils/haddock/doc/snippets/Snippet-List-Definition.tex - + utils/haddock/doc/snippets/Snippet-List-Enumerated.html - + utils/haddock/doc/snippets/Snippet-List-Enumerated.tex - + utils/haddock/doc/snippets/Snippet-List-Indentation.html - + utils/haddock/doc/snippets/Snippet-List-Indentation.tex - + utils/haddock/doc/snippets/Snippet-List-Multiline-Item.html - + utils/haddock/doc/snippets/Snippet-List-Multiline-Item.tex - + utils/haddock/doc/snippets/Snippet-List-Nested-Item.html - + utils/haddock/doc/snippets/Snippet-List-Nested-Item.tex - + utils/haddock/doc/snippets/Snippet-List-Not-Newline.html - + utils/haddock/doc/snippets/Snippet-List-Not-Newline.tex - + utils/haddock/doc/snippets/Snippet-List-Not-Separated.html - + utils/haddock/doc/snippets/Snippet-List-Not-Separated.tex - utils/haddock/html-test/ref/A.html - utils/haddock/html-test/ref/Bug1004.html - utils/haddock/html-test/ref/Bug1033.html - utils/haddock/html-test/ref/Bug1103.html - utils/haddock/html-test/ref/Bug548.html - utils/haddock/html-test/ref/Bug923.html - utils/haddock/html-test/ref/ConstructorPatternExport.html - utils/haddock/html-test/ref/FunArgs.html - utils/haddock/html-test/ref/Hash.html - utils/haddock/html-test/ref/Instances.html - utils/haddock/html-test/ref/LinearTypes.html - utils/haddock/html-test/ref/RedactTypeSynonyms.html - utils/haddock/html-test/ref/T23616.html - utils/haddock/html-test/ref/Test.html - utils/haddock/html-test/ref/TypeFamilies3.html The diff was not included because it is too large. View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/0756bf6143fc45d433476b8707997b… -- View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/0756bf6143fc45d433476b8707997b… You're receiving this email because of your account on gitlab.haskell.org.
1 0
0 0
  • ← Newer
  • 1
  • ...
  • 549
  • 550
  • 551
  • 552
  • 553
  • 554
  • 555
  • ...
  • 1069
  • Older →

HyperKitty Powered by HyperKitty version 1.3.12.