Zubin pushed to branch wip/semaphore-v2 at Glasgow Haskell Compiler / GHC
Commits:
-
9d4a5f5e
by Zubin Duggal at 2026-05-13T18:17:42+05:30
19 changed files:
- + changelog.d/jobserver-leak-fix
- + changelog.d/semaphore-v2
- compiler/GHC/Driver/Errors/Ppr.hs
- compiler/GHC/Driver/Errors/Types.hs
- compiler/GHC/Driver/Flags.hs
- compiler/GHC/Driver/MakeAction.hs
- compiler/GHC/Driver/MakeSem.hs
- compiler/GHC/Driver/Session.hs
- compiler/GHC/Types/Error/Codes.hs
- docs/users_guide/using-warnings.rst
- docs/users_guide/using.rst
- hadrian/cabal.project
- hadrian/hadrian.cabal
- hadrian/src/Flavour.hs
- hadrian/src/Rules/Generate.hs
- hadrian/src/Settings/Packages.hs
- hadrian/stack.yaml
- libraries/semaphore-compat
- testsuite/tests/diagnostic-codes/codes.stdout
Changes:
| 1 | +section: compiler
|
|
| 2 | +issues: #27253
|
|
| 3 | +mrs: !15729
|
|
| 4 | +synopsis:
|
|
| 5 | + Fix a token leak in the ``-jsem`` jobserver shutdown path
|
|
| 6 | +description:
|
|
| 7 | + ``cleanupSem`` used to snapshot ``heldTokens`` and release them
|
|
| 8 | + before killing the loop, while the loop's in-flight acquire/release
|
|
| 9 | + children could still be mutating it. Cleanup now runs inside the
|
|
| 10 | + loop's own exit handler, after draining the active child via a new
|
|
| 11 | + ``activeChild`` TVar, so the snapshot has no concurrent mutator. |
| 1 | +section: compiler
|
|
| 2 | +issues: #25087
|
|
| 3 | +mrs: !15729
|
|
| 4 | +synopsis:
|
|
| 5 | + Update to semaphore-compat 2.0.0 (``-jsem`` protocol v2)
|
|
| 6 | +description:
|
|
| 7 | + On Linux and other POSIX platforms, GHC's ``-jsem`` jobserver client
|
|
| 8 | + now speaks v2 of the semaphore-compat protocol, which uses Unix
|
|
| 9 | + domain sockets in place of POSIX named semaphores. This avoids the
|
|
| 10 | + libc-ABI issues that affected the old implementation. Windows is
|
|
| 11 | + unaffected and continues to use the v1 protocol (Win32 named
|
|
| 12 | + semaphores); its reported protocol version remains v1.
|
|
| 13 | + |
|
| 14 | + When GHC receives a ``-jsem`` name whose protocol version it does not
|
|
| 15 | + support, it now emits a ``-Wsemaphore-version-mismatch`` warning and
|
|
| 16 | + falls back to ``-j<N>`` rather than crashing. ``ghc --info`` exposes the
|
|
| 17 | + supported version in a new ``"Semaphore version"`` entry so
|
|
| 18 | + cabal-install can detect a mismatch before invoking GHC.
|
|
| 19 | + |
|
| 20 | + Users on a ``cabal-install`` that predates the v2 update will continue
|
|
| 21 | + to build successfully on Linux/POSIX, but will lose the cross-process
|
|
| 22 | + ``-jsem`` coordination and fall back to ``-j<N>`` per GHC invocation.
|
|
| 23 | + To recover full parallelism, upgrade to a ``cabal-install`` that
|
|
| 24 | + supports protocol v2.
|
|
| 25 | + |
|
| 26 | + See also:
|
|
| 27 | + |
|
| 28 | + - the `GHC proposal amendment <https://github.com/ghc-proposals/ghc-proposals/pull/673>`_
|
|
| 29 | + - the `cabal-install patch <https://github.com/haskell/cabal/pull/11628>`_
|
|
| 30 | + - the `semaphore-compat library MR <https://gitlab.haskell.org/ghc/semaphore-compat/-/merge_requests/8>`_ |
| ... | ... | @@ -24,6 +24,8 @@ import GHC.Types.Hint |
| 24 | 24 | import GHC.Types.SrcLoc
|
| 25 | 25 | import Data.Version
|
| 26 | 26 | |
| 27 | +import System.Semaphore
|
|
| 28 | + ( SemaphoreError(..), getSemaphoreProtocolVersion )
|
|
| 27 | 29 | import Language.Haskell.Syntax.Decls (RuleDecl(..))
|
| 28 | 30 | import GHC.Tc.Errors.Types (TcRnMessage)
|
| 29 | 31 | import GHC.HsToCore.Errors.Types (DsMessage)
|
| ... | ... | @@ -90,6 +92,20 @@ instance Diagnostic GhcMessage where |
| 90 | 92 | instance HasDefaultDiagnosticOpts DriverMessageOpts where
|
| 91 | 93 | defaultOpts = DriverMessageOpts (defaultDiagnosticOpts @PsMessage) (defaultDiagnosticOpts @IfaceMessage)
|
| 92 | 94 | |
| 95 | +pprSemaphoreError :: SemaphoreError -> SDoc
|
|
| 96 | +pprSemaphoreError = \case
|
|
| 97 | + SemaphoreAlreadyExists nm ->
|
|
| 98 | + text "a semaphore named" <+> quotes (text nm) <+> text "already exists"
|
|
| 99 | + SemaphoreDoesNotExist nm ->
|
|
| 100 | + text "no semaphore named" <+> quotes (text nm)
|
|
| 101 | + SemaphoreIncompatibleVersion got want ->
|
|
| 102 | + text "protocol version mismatch (got v"
|
|
| 103 | + <> int (getSemaphoreProtocolVersion got)
|
|
| 104 | + <> text ", supported v"
|
|
| 105 | + <> int (getSemaphoreProtocolVersion want) <> text ")"
|
|
| 106 | + SemaphoreOtherError ioe ->
|
|
| 107 | + text (show ioe)
|
|
| 108 | + |
|
| 93 | 109 | instance Diagnostic DriverMessage where
|
| 94 | 110 | type DiagnosticOpts DriverMessage = DriverMessageOpts
|
| 95 | 111 | diagnosticMessage opts = \case
|
| ... | ... | @@ -282,6 +298,15 @@ instance Diagnostic DriverMessage where |
| 282 | 298 | -> mkSimpleDecorated $
|
| 283 | 299 | vcat [ text "The following modules are missing a linkable which is needed for creating a library:"
|
| 284 | 300 | , nest 2 $ hcat (map ppr mods) ]
|
| 301 | + DriverSemaphoreVersionMismatch received supported
|
|
| 302 | + -> mkSimpleDecorated $
|
|
| 303 | + text "Semaphore version mismatch (received v" <> int (getSemaphoreProtocolVersion received) <>
|
|
| 304 | + text ", this GHC supports v" <> int (getSemaphoreProtocolVersion supported) <>
|
|
| 305 | + text "); ignoring -jsem and compiling sequentially."
|
|
| 306 | + DriverSemaphoreOpenFailure err
|
|
| 307 | + -> mkSimpleDecorated $
|
|
| 308 | + text "Failed to open -jsem semaphore:" <+> pprSemaphoreError err <>
|
|
| 309 | + text "; ignoring -jsem and compiling sequentially."
|
|
| 285 | 310 | |
| 286 | 311 | diagnosticReason = \case
|
| 287 | 312 | DriverUnknownMessage m
|
| ... | ... | @@ -355,6 +380,10 @@ instance Diagnostic DriverMessage where |
| 355 | 380 | -> WarningWithoutFlag
|
| 356 | 381 | DriverMissingLinkableForModule {}
|
| 357 | 382 | -> ErrorWithoutFlag
|
| 383 | + DriverSemaphoreVersionMismatch {}
|
|
| 384 | + -> WarningWithFlag Opt_WarnSemaphoreVersionMismatch
|
|
| 385 | + DriverSemaphoreOpenFailure {}
|
|
| 386 | + -> WarningWithFlag Opt_WarnSemaphoreOpenFailure
|
|
| 358 | 387 | |
| 359 | 388 | diagnosticHints = \case
|
| 360 | 389 | DriverUnknownMessage m
|
| ... | ... | @@ -430,5 +459,14 @@ instance Diagnostic DriverMessage where |
| 430 | 459 | -> noHints
|
| 431 | 460 | DriverMissingLinkableForModule {}
|
| 432 | 461 | -> noHints
|
| 462 | + DriverSemaphoreVersionMismatch received _supported
|
|
| 463 | + | received < _supported
|
|
| 464 | + -> [UnknownHint (text "The parent process (e.g. cabal-install) uses an older semaphore protocol."
|
|
| 465 | + $$ text "Upgrading cabal-install may resolve this." :: SDoc)]
|
|
| 466 | + | otherwise
|
|
| 467 | + -> [UnknownHint (text "The parent process (e.g. cabal-install) uses a newer semaphore protocol."
|
|
| 468 | + $$ text "Upgrading GHC may resolve this." :: SDoc)]
|
|
| 469 | + DriverSemaphoreOpenFailure {}
|
|
| 470 | + -> noHints
|
|
| 433 | 471 | |
| 434 | 472 | diagnosticCode = constructorCode @GHC |
| ... | ... | @@ -37,6 +37,7 @@ import qualified GHC.LanguageExtensions as LangExt |
| 37 | 37 | |
| 38 | 38 | import GHC.Generics ( Generic )
|
| 39 | 39 | |
| 40 | +import System.Semaphore ( SemaphoreError, SemaphoreProtocolVersion )
|
|
| 40 | 41 | import GHC.Tc.Errors.Types
|
| 41 | 42 | import GHC.Iface.Errors.Types
|
| 42 | 43 | |
| ... | ... | @@ -419,6 +420,23 @@ data DriverMessage where |
| 419 | 420 | |
| 420 | 421 | DriverMissingLinkableForModule :: ![Module] -> DriverMessage
|
| 421 | 422 | |
| 423 | + {-| DriverSemaphoreVersionMismatch is a warning that occurs when GHC
|
|
| 424 | + receives a @-jsem@ semaphore name whose protocol version is incompatible
|
|
| 425 | + with the version this GHC supports. GHC ignores @-jsem@ and compiles
|
|
| 426 | + sequentially.
|
|
| 427 | + |
|
| 428 | + The first field is the received version (or 1 for unversioned names),
|
|
| 429 | + the second is the version this GHC supports.
|
|
| 430 | + -}
|
|
| 431 | + DriverSemaphoreVersionMismatch :: !SemaphoreProtocolVersion -> !SemaphoreProtocolVersion -> DriverMessage
|
|
| 432 | + |
|
| 433 | + {-| DriverSemaphoreOpenFailure is a warning that occurs when GHC fails to
|
|
| 434 | + open the semaphore specified by @-jsem@ (e.g. the socket does not exist
|
|
| 435 | + or a system error occurred). GHC ignores @-jsem@ and compiles
|
|
| 436 | + sequentially.
|
|
| 437 | + -}
|
|
| 438 | + DriverSemaphoreOpenFailure :: !SemaphoreError -> DriverMessage
|
|
| 439 | + |
|
| 422 | 440 | deriving instance Generic DriverMessage
|
| 423 | 441 | |
| 424 | 442 | data DriverMessageOpts =
|
| ... | ... | @@ -1115,6 +1115,8 @@ data WarningFlag = |
| 1115 | 1115 | | Opt_WarnUnusableUnpackPragmas -- ^ @since 9.14
|
| 1116 | 1116 | | Opt_WarnPatternNamespaceSpecifier -- ^ @since 9.14
|
| 1117 | 1117 | | Opt_WarnUnrecognisedModifiers -- ^ @since 10.0
|
| 1118 | + | Opt_WarnSemaphoreVersionMismatch -- Since 10.0.1
|
|
| 1119 | + | Opt_WarnSemaphoreOpenFailure -- Since 10.0.1
|
|
| 1118 | 1120 | deriving (Eq, Ord, Show, Enum, Bounded)
|
| 1119 | 1121 | |
| 1120 | 1122 | -- | Return the names of a WarningFlag
|
| ... | ... | @@ -1237,6 +1239,8 @@ warnFlagNames wflag = case wflag of |
| 1237 | 1239 | Opt_WarnUnusableUnpackPragmas -> "unusable-unpack-pragmas" :| []
|
| 1238 | 1240 | Opt_WarnPatternNamespaceSpecifier -> "pattern-namespace-specifier" :| []
|
| 1239 | 1241 | Opt_WarnUnrecognisedModifiers -> "unrecognised-modifiers" :| []
|
| 1242 | + Opt_WarnSemaphoreVersionMismatch -> "semaphore-version-mismatch" :| []
|
|
| 1243 | + Opt_WarnSemaphoreOpenFailure -> "semaphore-open-failure" :| []
|
|
| 1240 | 1244 | |
| 1241 | 1245 | -- -----------------------------------------------------------------------------
|
| 1242 | 1246 | -- Standard sets of warning options
|
| ... | ... | @@ -1383,7 +1387,9 @@ standardWarnings -- see Note [Documenting warning flags] |
| 1383 | 1387 | Opt_WarnDeprecatedPragmas,
|
| 1384 | 1388 | Opt_WarnRuleLhsEqualities,
|
| 1385 | 1389 | Opt_WarnUnusableUnpackPragmas,
|
| 1386 | - Opt_WarnUnrecognisedModifiers
|
|
| 1390 | + Opt_WarnUnrecognisedModifiers,
|
|
| 1391 | + Opt_WarnSemaphoreVersionMismatch,
|
|
| 1392 | + Opt_WarnSemaphoreOpenFailure
|
|
| 1387 | 1393 | ]
|
| 1388 | 1394 | |
| 1389 | 1395 | -- | Things you get with @-W@.
|
| ... | ... | @@ -28,6 +28,14 @@ import GHC.Driver.Errors.Types |
| 28 | 28 | import GHC.Driver.Messager
|
| 29 | 29 | import GHC.Driver.MakeSem
|
| 30 | 30 | |
| 31 | +import System.Semaphore
|
|
| 32 | + ( SemaphoreError(..) )
|
|
| 33 | + |
|
| 34 | +import GHC.Driver.Config.Diagnostic ( initDiagOpts, initPrintConfig )
|
|
| 35 | +import GHC.Driver.Errors ( printOrThrowDiagnostics )
|
|
| 36 | +import GHC.Types.Error ( singleMessage )
|
|
| 37 | +import GHC.Types.SrcLoc ( noSrcSpan )
|
|
| 38 | +import GHC.Utils.Error ( mkPlainMsgEnvelope )
|
|
| 31 | 39 | import GHC.Utils.Logger
|
| 32 | 40 | import GHC.Utils.TmpFs
|
| 33 | 41 | |
| ... | ... | @@ -49,7 +57,7 @@ mkWorkerLimit :: DynFlags -> IO WorkerLimit |
| 49 | 57 | mkWorkerLimit dflags =
|
| 50 | 58 | case parMakeCount dflags of
|
| 51 | 59 | Nothing -> pure $ num_procs 1
|
| 52 | - Just (ParMakeSemaphore h) -> pure (JSemLimit (SemaphoreName h))
|
|
| 60 | + Just (ParMakeSemaphore h) -> pure (JSemLimit h)
|
|
| 53 | 61 | Just ParMakeNumProcessors -> num_procs <$> getNumProcessors
|
| 54 | 62 | Just (ParMakeThisMany n) -> pure $ num_procs n
|
| 55 | 63 | where
|
| ... | ... | @@ -65,8 +73,8 @@ isWorkerLimitSequential (JSemLimit {}) = False |
| 65 | 73 | data WorkerLimit
|
| 66 | 74 | = NumProcessorsLimit Int
|
| 67 | 75 | | JSemLimit
|
| 68 | - SemaphoreName
|
|
| 69 | - -- ^ Semaphore name to use
|
|
| 76 | + String
|
|
| 77 | + -- ^ Raw semaphore identifier from @-jsem@
|
|
| 70 | 78 | deriving Eq
|
| 71 | 79 | |
| 72 | 80 | -- | Environment used when compiling a module
|
| ... | ... | @@ -122,17 +130,29 @@ runNjobsAbstractSem n_jobs action = do |
| 122 | 130 | resetNumCapabilities = set_num_caps n_capabilities
|
| 123 | 131 | MC.bracket_ updNumCapabilities resetNumCapabilities $ action asem
|
| 124 | 132 | |
| 125 | -runWorkerLimit :: WorkerLimit -> (AbstractSem -> IO a) -> IO a
|
|
| 133 | +runWorkerLimit :: Logger -> DynFlags -> WorkerLimit -> (AbstractSem -> IO a) -> IO a
|
|
| 126 | 134 | #if defined(wasm32_HOST_ARCH)
|
| 127 | -runWorkerLimit _ action = do
|
|
| 135 | +runWorkerLimit _logger _dflags _ action = do
|
|
| 128 | 136 | lock <- newMVar ()
|
| 129 | 137 | action $ AbstractSem (takeMVar lock) (putMVar lock ())
|
| 130 | 138 | #else
|
| 131 | -runWorkerLimit worker_limit action = case worker_limit of
|
|
| 139 | +runWorkerLimit logger dflags worker_limit action = case worker_limit of
|
|
| 132 | 140 | NumProcessorsLimit n_jobs ->
|
| 133 | 141 | runNjobsAbstractSem n_jobs action
|
| 134 | - JSemLimit sem ->
|
|
| 135 | - runJSemAbstractSem sem action
|
|
| 142 | + JSemLimit sem_ident -> do
|
|
| 143 | + result <- MC.try $ runJSemAbstractSem sem_ident action
|
|
| 144 | + case result of
|
|
| 145 | + Right a -> return a
|
|
| 146 | + Left (SemaphoreIncompatibleVersion actual expected) -> do
|
|
| 147 | + let diag = DriverSemaphoreVersionMismatch actual expected
|
|
| 148 | + msg = singleMessage $ mkPlainMsgEnvelope (initDiagOpts dflags) noSrcSpan diag
|
|
| 149 | + printOrThrowDiagnostics logger (initPrintConfig dflags) (initDiagOpts dflags) (GhcDriverMessage <$> msg)
|
|
| 150 | + runNjobsAbstractSem 1 action
|
|
| 151 | + Left (err :: SemaphoreError) -> do
|
|
| 152 | + let diag = DriverSemaphoreOpenFailure err
|
|
| 153 | + msg = singleMessage $ mkPlainMsgEnvelope (initDiagOpts dflags) noSrcSpan diag
|
|
| 154 | + printOrThrowDiagnostics logger (initPrintConfig dflags) (initDiagOpts dflags) (GhcDriverMessage <$> msg)
|
|
| 155 | + runNjobsAbstractSem 1 action
|
|
| 136 | 156 | #endif
|
| 137 | 157 | |
| 138 | 158 | -- | Build and run a pipeline
|
| ... | ... | @@ -159,7 +179,7 @@ runParPipelines worker_limit plugin_hsc_env diag_wrapper mHscMessager all_pipeli |
| 159 | 179 | thread_safe_logger <- liftIO $ makeThreadSafe (hsc_logger plugin_hsc_env)
|
| 160 | 180 | let thread_safe_hsc_env = plugin_hsc_env { hsc_logger = thread_safe_logger }
|
| 161 | 181 | |
| 162 | - runWorkerLimit worker_limit $ \abstract_sem -> do
|
|
| 182 | + runWorkerLimit (hsc_logger plugin_hsc_env) (hsc_dflags plugin_hsc_env) worker_limit $ \abstract_sem -> do
|
|
| 163 | 183 | let env = MakeEnv { hsc_env = thread_safe_hsc_env
|
| 164 | 184 | , withLogger = withParLog log_queue_queue_var
|
| 165 | 185 | , compile_sem = abstract_sem
|
| ... | ... | @@ -9,9 +9,6 @@ module GHC.Driver.MakeSem |
| 9 | 9 | -- by a system semaphore (Posix/Windows)
|
| 10 | 10 | runJSemAbstractSem
|
| 11 | 11 | |
| 12 | - -- * System semaphores
|
|
| 13 | - , Semaphore, SemaphoreName(..)
|
|
| 14 | - |
|
| 15 | 12 | -- * Abstract semaphores
|
| 16 | 13 | , AbstractSem(..)
|
| 17 | 14 | , withAbstractSem
|
| ... | ... | @@ -46,11 +43,14 @@ import Debug.Trace |
| 46 | 43 | -- available from the semaphore.
|
| 47 | 44 | data Jobserver
|
| 48 | 45 | = Jobserver
|
| 49 | - { jSemaphore :: !Semaphore
|
|
| 46 | + { jSemaphore :: !ClientSemaphore
|
|
| 50 | 47 | -- ^ The semaphore which controls available resources
|
| 51 | 48 | , jobs :: !(TVar JobResources)
|
| 52 | 49 | -- ^ The currently pending jobs, and the resources
|
| 53 | 50 | -- obtained from the semaphore
|
| 51 | + , activeChild :: !(TVar (Maybe (ThreadId, TMVar (Maybe MC.SomeException))))
|
|
| 52 | + -- ^ Handle on the current acquire thread (if any). The loop's exit
|
|
| 53 | + -- handler reads this to drain a still-running child on shutdown.
|
|
| 54 | 54 | }
|
| 55 | 55 | |
| 56 | 56 | data JobserverOptions
|
| ... | ... | @@ -81,6 +81,9 @@ data JobResources |
| 81 | 81 | , jobsWaiting :: !(OrdList (TMVar ()))
|
| 82 | 82 | -- ^ Pending jobs waiting on a token, the job will be blocked on the TMVar so putting into
|
| 83 | 83 | -- the TMVar will allow the job to continue.
|
| 84 | + , heldTokens :: [SemaphoreToken]
|
|
| 85 | + -- ^ Actual semaphore tokens (for release/cleanup).
|
|
| 86 | + -- Length should equal tokensOwned - 1 (the implicit token has no SemaphoreToken).
|
|
| 84 | 87 | }
|
| 85 | 88 | |
| 86 | 89 | instance Outputable JobResources where
|
| ... | ... | @@ -93,9 +96,9 @@ instance Outputable JobResources where |
| 93 | 96 | ] )
|
| 94 | 97 | |
| 95 | 98 | -- | Add one new token.
|
| 96 | -addToken :: JobResources -> JobResources
|
|
| 97 | -addToken jobs@( Jobs { tokensOwned = owned, tokensFree = free })
|
|
| 98 | - = jobs { tokensOwned = owned + 1, tokensFree = free + 1 }
|
|
| 99 | +addToken :: SemaphoreToken -> JobResources -> JobResources
|
|
| 100 | +addToken tok jobs@( Jobs { tokensOwned = owned, tokensFree = free, heldTokens = toks })
|
|
| 101 | + = jobs { tokensOwned = owned + 1, tokensFree = free + 1, heldTokens = tok : toks }
|
|
| 99 | 102 | |
| 100 | 103 | -- | Free one token.
|
| 101 | 104 | addFreeToken :: JobResources -> JobResources
|
| ... | ... | @@ -111,12 +114,14 @@ removeFreeToken jobs@( Jobs { tokensFree = free }) |
| 111 | 114 | (text "removeFreeToken:" <+> ppr free)
|
| 112 | 115 | $ jobs { tokensFree = free - 1 }
|
| 113 | 116 | |
| 114 | --- | Return one owned token.
|
|
| 115 | -removeOwnedToken :: JobResources -> JobResources
|
|
| 116 | -removeOwnedToken jobs@( Jobs { tokensOwned = owned })
|
|
| 117 | +-- | Return one owned token, extracting the 'SemaphoreToken' for release.
|
|
| 118 | +removeOwnedToken :: JobResources -> (SemaphoreToken, JobResources)
|
|
| 119 | +removeOwnedToken jobs@( Jobs { tokensOwned = owned, heldTokens = toks })
|
|
| 117 | 120 | = assertPpr (owned > 1)
|
| 118 | 121 | (text "removeOwnedToken:" <+> ppr owned)
|
| 119 | - $ jobs { tokensOwned = owned - 1 }
|
|
| 122 | + $ case toks of
|
|
| 123 | + (t:rest) -> (t, jobs { tokensOwned = owned - 1, heldTokens = rest })
|
|
| 124 | + [] -> panic "removeOwnedToken: no held tokens"
|
|
| 120 | 125 | |
| 121 | 126 | -- | Add one new job to the end of the list of pending jobs.
|
| 122 | 127 | addJob :: TMVar () -> JobResources -> JobResources
|
| ... | ... | @@ -143,7 +148,7 @@ data JobserverAction |
| 143 | 148 | = Idle
|
| 144 | 149 | -- | A thread is waiting for a token on the semaphore.
|
| 145 | 150 | | Acquiring
|
| 146 | - { activeWaitId :: WaitId
|
|
| 151 | + { activeThreadId :: ThreadId
|
|
| 147 | 152 | , threadFinished :: TMVar (Maybe MC.SomeException) }
|
| 148 | 153 | |
| 149 | 154 | -- | Retrieve the 'TMVar' that signals if the current thread has finished,
|
| ... | ... | @@ -189,17 +194,26 @@ releaseJob jobs_tvar = do |
| 189 | 194 | return ((), addFreeToken jobs)
|
| 190 | 195 | |
| 191 | 196 | |
| 192 | --- | Release all tokens owned from the semaphore (to clean up
|
|
| 193 | --- the jobserver at the end).
|
|
| 194 | -cleanupJobserver :: Jobserver -> IO ()
|
|
| 195 | -cleanupJobserver (Jobserver { jSemaphore = sem
|
|
| 196 | - , jobs = jobs_tvar })
|
|
| 197 | - = do
|
|
| 198 | - Jobs { tokensOwned = owned } <- readTVarIO jobs_tvar
|
|
| 199 | - let toks_to_release = owned - 1
|
|
| 200 | - -- Subtract off the implicit token: whoever spawned the ghc process
|
|
| 201 | - -- in the first place is responsible for that token.
|
|
| 202 | - releaseSemaphore sem toks_to_release
|
|
| 197 | +-- | Kill the current acquire thread, if any, and wait for it to exit.
|
|
| 198 | +--
|
|
| 199 | +-- Relies on the invariant from 'acquireThread' that a forked child always
|
|
| 200 | +-- fills its 'threadFinished' TMVar before it dies; this is what lets the
|
|
| 201 | +-- 'takeTMVar' below terminate after the 'killThread'.
|
|
| 202 | +drainActiveChild :: Jobserver -> IO ()
|
|
| 203 | +drainActiveChild (Jobserver { activeChild = active_tvar }) = do
|
|
| 204 | + mb <- readTVarIO active_tvar
|
|
| 205 | + for_ mb $ \(tid, tmv) -> do
|
|
| 206 | + killThread tid
|
|
| 207 | + void $ atomically (takeTMVar tmv)
|
|
| 208 | + atomically $ writeTVar active_tvar Nothing
|
|
| 209 | + |
|
| 210 | +-- | Release every token currently in 'heldTokens'. Safe to call only when
|
|
| 211 | +-- nothing else is mutating the 'JobResources' TVar.
|
|
| 212 | +releaseAllHeld :: Jobserver -> IO ()
|
|
| 213 | +releaseAllHeld (Jobserver { jobs = jobs_tvar }) = do
|
|
| 214 | + Jobs { heldTokens = toks } <- readTVarIO jobs_tvar
|
|
| 215 | + forM_ toks $ \t ->
|
|
| 216 | + void $ MC.try @_ @MC.SomeException (releaseSemaphoreToken t)
|
|
| 203 | 217 | |
| 204 | 218 | -- | Dispatch the available tokens acquired from the semaphore
|
| 205 | 219 | -- to the pending jobs in the job server.
|
| ... | ... | @@ -252,7 +266,7 @@ tracedAtomically origin act = do |
| 252 | 266 | return a
|
| 253 | 267 | |
| 254 | 268 | renderJobResources :: String -> JobResources -> String
|
| 255 | -renderJobResources origin (Jobs own free pending) = showSDocUnsafe $ renderJSON $
|
|
| 269 | +renderJobResources origin (Jobs own free pending _heldToks) = showSDocUnsafe $ renderJSON $
|
|
| 256 | 270 | JSObject [ ("name", JSString origin)
|
| 257 | 271 | , ("owned", JSInt own)
|
| 258 | 272 | , ("free", JSInt free)
|
| ... | ... | @@ -262,61 +276,66 @@ renderJobResources origin (Jobs own free pending) = showSDocUnsafe $ renderJSON |
| 262 | 276 | |
| 263 | 277 | -- | Spawn a new thread that waits on the semaphore in order to acquire
|
| 264 | 278 | -- an additional token.
|
| 279 | +--
|
|
| 280 | +-- The child is forked masked so the only async-exception delivery point
|
|
| 281 | +-- is the interruptible 'waitOnSemaphore'; the STM commit afterwards then
|
|
| 282 | +-- always runs to completion, so 'threadFinished' is always filled.
|
|
| 283 | +--
|
|
| 284 | +-- The (tid, threadFinished) pair is also published to 'activeChild' so
|
|
| 285 | +-- shutdown can drain the child even after the in-loop 'JobserverState'
|
|
| 286 | +-- is gone.
|
|
| 265 | 287 | acquireThread :: Jobserver -> IO JobserverAction
|
| 266 | -acquireThread (Jobserver { jSemaphore = sem, jobs = jobs_tvar }) = do
|
|
| 288 | +acquireThread (Jobserver { jSemaphore = sem, jobs = jobs_tvar, activeChild = active_tvar }) = do
|
|
| 267 | 289 | threadFinished_tmvar <- newEmptyTMVarIO
|
| 268 | - let
|
|
| 269 | - wait_result_action :: Either MC.SomeException Bool -> IO ()
|
|
| 270 | - wait_result_action wait_res =
|
|
| 290 | + tid <- MC.mask_ $ do
|
|
| 291 | + tid <- forkIO $ do
|
|
| 292 | + wait_res <- MC.try @_ @MC.SomeException $ waitOnSemaphore sem
|
|
| 271 | 293 | tracedAtomically_ "acquire_thread" do
|
| 272 | 294 | (r, jb) <- case wait_res of
|
| 273 | 295 | Left (e :: MC.SomeException) -> do
|
| 274 | 296 | return $ (Just e, Nothing)
|
| 275 | - Right success -> do
|
|
| 276 | - if success
|
|
| 277 | - then do
|
|
| 278 | - modifyJobResources jobs_tvar \ jobs ->
|
|
| 279 | - return (Nothing, addToken jobs)
|
|
| 280 | - else
|
|
| 281 | - return (Nothing, Nothing)
|
|
| 297 | + Right tok -> do
|
|
| 298 | + modifyJobResources jobs_tvar \ jobs ->
|
|
| 299 | + return (Nothing, addToken tok jobs)
|
|
| 282 | 300 | putTMVar threadFinished_tmvar r
|
| 283 | 301 | return jb
|
| 284 | - wait_id <- forkWaitOnSemaphoreInterruptible sem wait_result_action
|
|
| 285 | - labelThread (waitingThreadId wait_id) "acquire_thread"
|
|
| 286 | - return $ Acquiring { activeWaitId = wait_id
|
|
| 302 | + atomically $ writeTVar active_tvar (Just (tid, threadFinished_tmvar))
|
|
| 303 | + return tid
|
|
| 304 | + labelThread tid "acquire_thread"
|
|
| 305 | + return $ Acquiring { activeThreadId = tid
|
|
| 287 | 306 | , threadFinished = threadFinished_tmvar }
|
| 288 | 307 | |
| 289 | 308 | -- | Spawn a thread to release ownership of one resource from the semaphore,
|
| 290 | 309 | -- provided we have spare resources and no pending jobs.
|
| 291 | 310 | releaseThread :: Jobserver -> IO JobserverAction
|
| 292 | -releaseThread (Jobserver { jSemaphore = sem, jobs = jobs_tvar }) = do
|
|
| 311 | +releaseThread (Jobserver { jobs = jobs_tvar }) = do
|
|
| 293 | 312 | threadFinished_tmvar <- newEmptyTMVarIO
|
| 294 | 313 | MC.mask_ do
|
| 295 | 314 | -- Pre-release the resource so that another thread doesn't take control of it
|
| 296 | 315 | -- just as we release the lock on the semaphore.
|
| 297 | - still_ok_to_release
|
|
| 316 | + mb_tok
|
|
| 298 | 317 | <- tracedAtomically "pre_release" $
|
| 299 | 318 | modifyJobResources jobs_tvar \ jobs ->
|
| 300 | 319 | if guardRelease jobs
|
| 301 | - -- TODO: should this also debounce?
|
|
| 302 | - then return (True , removeOwnedToken $ removeFreeToken jobs)
|
|
| 303 | - else return (False, jobs)
|
|
| 304 | - if not still_ok_to_release
|
|
| 305 | - then return Idle
|
|
| 306 | - else do
|
|
| 307 | - tid <- forkIO $ do
|
|
| 308 | - x <- MC.try $ releaseSemaphore sem 1
|
|
| 309 | - tracedAtomically_ "post-release" $ do
|
|
| 310 | - (r, jobs) <- case x of
|
|
| 311 | - Left (e :: MC.SomeException) -> do
|
|
| 312 | - modifyJobResources jobs_tvar \ jobs ->
|
|
| 313 | - return (Just e, addToken jobs)
|
|
| 314 | - Right _ -> do
|
|
| 315 | - return (Nothing, Nothing)
|
|
| 316 | - putTMVar threadFinished_tmvar r
|
|
| 317 | - return jobs
|
|
| 318 | - labelThread tid "release_thread"
|
|
| 319 | - return Idle
|
|
| 320 | + then let (tok, jobs') = removeOwnedToken $ removeFreeToken jobs
|
|
| 321 | + in return (Just tok, jobs')
|
|
| 322 | + else return (Nothing, jobs)
|
|
| 323 | + case mb_tok of
|
|
| 324 | + Nothing -> return Idle
|
|
| 325 | + Just tok -> do
|
|
| 326 | + tid <- forkIO $ do
|
|
| 327 | + x <- MC.try $ releaseSemaphoreToken tok
|
|
| 328 | + tracedAtomically_ "post-release" $ do
|
|
| 329 | + (r, jobs) <- case x of
|
|
| 330 | + Left (e :: MC.SomeException) -> do
|
|
| 331 | + modifyJobResources jobs_tvar \ jobs ->
|
|
| 332 | + return (Just e, addToken tok jobs)
|
|
| 333 | + Right _ -> do
|
|
| 334 | + return (Nothing, Nothing)
|
|
| 335 | + putTMVar threadFinished_tmvar r
|
|
| 336 | + return jobs
|
|
| 337 | + labelThread tid "release_thread"
|
|
| 338 | + return Idle
|
|
| 320 | 339 | |
| 321 | 340 | -- | When there are pending jobs but no free tokens,
|
| 322 | 341 | -- spawn a thread to acquire a new token from the semaphore.
|
| ... | ... | @@ -363,13 +382,14 @@ tryRelease _ _ = retry |
| 363 | 382 | -- | Wait for an active thread to finish. Once it finishes:
|
| 364 | 383 | --
|
| 365 | 384 | -- - set the 'JobserverAction' to 'Idle',
|
| 385 | +-- - clear the 'activeChild' handle,
|
|
| 366 | 386 | -- - update the number of capabilities to reflect the number
|
| 367 | 387 | -- of owned tokens from the semaphore.
|
| 368 | 388 | tryNoticeIdle :: JobserverOptions
|
| 369 | - -> TVar JobResources
|
|
| 389 | + -> Jobserver
|
|
| 370 | 390 | -> JobserverState
|
| 371 | 391 | -> STM (IO JobserverState)
|
| 372 | -tryNoticeIdle opts jobs_tvar jobserver_state
|
|
| 392 | +tryNoticeIdle opts (Jobserver { jobs = jobs_tvar, activeChild = active_tvar }) jobserver_state
|
|
| 373 | 393 | | Just threadFinished_tmvar <- activeThread_maybe $ jobserverAction jobserver_state
|
| 374 | 394 | = sync_num_caps (canChangeNumCaps jobserver_state) threadFinished_tmvar
|
| 375 | 395 | | otherwise
|
| ... | ... | @@ -381,6 +401,7 @@ tryNoticeIdle opts jobs_tvar jobserver_state |
| 381 | 401 | sync_num_caps can_change_numcaps_tvar threadFinished_tmvar = do
|
| 382 | 402 | mb_ex <- takeTMVar threadFinished_tmvar
|
| 383 | 403 | for_ mb_ex MC.throwM
|
| 404 | + writeTVar active_tvar Nothing
|
|
| 384 | 405 | Jobs { tokensOwned } <- readTVar jobs_tvar
|
| 385 | 406 | can_change_numcaps <- readTVar can_change_numcaps_tvar
|
| 386 | 407 | guard can_change_numcaps
|
| ... | ... | @@ -404,11 +425,11 @@ tryStopThread :: TVar JobResources |
| 404 | 425 | -> STM (IO JobserverState)
|
| 405 | 426 | tryStopThread jobs_tvar jsj = do
|
| 406 | 427 | case jobserverAction jsj of
|
| 407 | - Acquiring { activeWaitId = wait_id } -> do
|
|
| 428 | + Acquiring { activeThreadId = tid } -> do
|
|
| 408 | 429 | jobs <- readTVar jobs_tvar
|
| 409 | 430 | guard $ null (jobsWaiting jobs)
|
| 410 | 431 | return do
|
| 411 | - interruptWaitOnSemaphore wait_id
|
|
| 432 | + killThread tid
|
|
| 412 | 433 | return $ jsj { jobserverAction = Idle }
|
| 413 | 434 | _ -> retry
|
| 414 | 435 | |
| ... | ... | @@ -430,30 +451,38 @@ jobserverLoop opts sjs@(Jobserver { jobs = jobs_tvar }) |
| 430 | 451 | action <- atomically $ asum $ (\x -> x s) <$>
|
| 431 | 452 | [ tryRelease sjs
|
| 432 | 453 | , tryAcquire opts sjs
|
| 433 | - , tryNoticeIdle opts jobs_tvar
|
|
| 454 | + , tryNoticeIdle opts sjs
|
|
| 434 | 455 | , tryStopThread jobs_tvar
|
| 435 | 456 | ]
|
| 436 | 457 | s <- action
|
| 437 | 458 | loop s
|
| 438 | 459 | |
| 439 | --- | Create a new jobserver using the given semaphore handle.
|
|
| 440 | -makeJobserver :: SemaphoreName -> IO (AbstractSem, IO ())
|
|
| 441 | -makeJobserver sem_name = do
|
|
| 442 | - semaphore <- openSemaphore sem_name
|
|
| 460 | +-- | Create a new jobserver using the given semaphore identifier.
|
|
| 461 | +makeJobserver :: String -> IO (AbstractSem, IO ())
|
|
| 462 | +makeJobserver sem_ident = do
|
|
| 463 | + semaphore <- openSemaphore sem_ident >>= either MC.throwM pure
|
|
| 443 | 464 | let
|
| 444 | 465 | init_jobs =
|
| 445 | 466 | Jobs { tokensOwned = 1
|
| 446 | 467 | , tokensFree = 1
|
| 447 | 468 | , jobsWaiting = NilOL
|
| 469 | + , heldTokens = []
|
|
| 448 | 470 | }
|
| 449 | 471 | jobs_tvar <- newTVarIO init_jobs
|
| 472 | + active_tvar <- newTVarIO Nothing
|
|
| 450 | 473 | let
|
| 451 | 474 | opts = defaultJobserverOptions -- TODO: allow this to be configured
|
| 452 | - sjs = Jobserver { jSemaphore = semaphore
|
|
| 453 | - , jobs = jobs_tvar }
|
|
| 475 | + sjs = Jobserver { jSemaphore = semaphore
|
|
| 476 | + , jobs = jobs_tvar
|
|
| 477 | + , activeChild = active_tvar }
|
|
| 454 | 478 | loop_finished_mvar <- newEmptyMVar
|
| 455 | 479 | loop_tid <- forkIOWithUnmask \ unmask -> do
|
| 456 | 480 | r <- try $ unmask $ jobserverLoop opts sjs
|
| 481 | + -- Always-run exit handler: any child the loop spawned is still alive
|
|
| 482 | + -- in its own thread, so drain it before touching jobs_tvar. No one
|
|
| 483 | + -- else can mutate the resources once both are dead.
|
|
| 484 | + drainActiveChild sjs
|
|
| 485 | + releaseAllHeld sjs
|
|
| 457 | 486 | putMVar loop_finished_mvar $
|
| 458 | 487 | case r of
|
| 459 | 488 | Left e
|
| ... | ... | @@ -467,8 +496,8 @@ makeJobserver sem_name = do |
| 467 | 496 | acquireSem = acquireJob jobs_tvar
|
| 468 | 497 | releaseSem = releaseJob jobs_tvar
|
| 469 | 498 | cleanupSem = do
|
| 470 | - -- this is interruptible
|
|
| 471 | - cleanupJobserver sjs
|
|
| 499 | + -- Trigger the loop's exit handler; it drains the active child and
|
|
| 500 | + -- releases all held tokens, then signals loop_finished_mvar.
|
|
| 472 | 501 | killThread loop_tid
|
| 473 | 502 | mb_ex <- takeMVar loop_finished_mvar
|
| 474 | 503 | for_ mb_ex MC.throwM
|
| ... | ... | @@ -477,12 +506,12 @@ makeJobserver sem_name = do |
| 477 | 506 | |
| 478 | 507 | -- | Implement an abstract semaphore using a semaphore 'Jobserver'
|
| 479 | 508 | -- which queries the system semaphore of the given name for resources.
|
| 480 | -runJSemAbstractSem :: SemaphoreName -- ^ the system semaphore to use
|
|
| 509 | +runJSemAbstractSem :: String -- ^ the semaphore identifier (from @-jsem@)
|
|
| 481 | 510 | -> (AbstractSem -> IO a) -- ^ the operation to run
|
| 482 | 511 | -- which requires a semaphore
|
| 483 | 512 | -> IO a
|
| 484 | -runJSemAbstractSem sem action = MC.mask \ unmask -> do
|
|
| 485 | - (abs, cleanup) <- makeJobserver sem
|
|
| 513 | +runJSemAbstractSem sem_ident action = MC.mask \ unmask -> do
|
|
| 514 | + (abs, cleanup) <- makeJobserver sem_ident
|
|
| 486 | 515 | r <- try $ unmask $ action abs
|
| 487 | 516 | case r of
|
| 488 | 517 | Left (e1 :: MC.SomeException) -> do
|
| ... | ... | @@ -517,8 +546,13 @@ increases the number of `free` jobs. If there are more pending jobs when the fre |
| 517 | 546 | is increased, the token is immediately reused (see `modifyJobResources`).
|
| 518 | 547 | |
| 519 | 548 | The `jobServerLoop` interacts with the system semaphore: when there are pending
|
| 520 | -jobs, `acquireThread` blocks, waiting for a token from the semaphore. Once a
|
|
| 521 | -token is obtained, it increases the owned count.
|
|
| 549 | +jobs, `acquireThread` forks a child that calls the interruptible
|
|
| 550 | +`waitOnSemaphore`. The child is forked in the masked state, so the only place
|
|
| 551 | +an async exception can be delivered is the wait itself; once the wait returns,
|
|
| 552 | +the child's STM commit always completes, recording either the new token in
|
|
| 553 | +`heldTokens` or the failure exception in `threadFinished`. The (tid, tmvar)
|
|
| 554 | +pair is also published in `activeChild` so the loop's exit handler can drain
|
|
| 555 | +the child on shutdown even after the in-loop `JobserverState` is gone.
|
|
| 522 | 556 | |
| 523 | 557 | When GHC has free tokens (tokens from the semaphore that it is not using),
|
| 524 | 558 | no pending jobs, and the debounce has expired, then `releaseThread` will
|
| ... | ... | @@ -531,6 +565,12 @@ This second token is no longer needed, so we should cancel the wait |
| 531 | 565 | (as it would not be used to do any work, and not be returned until the debounce).
|
| 532 | 566 | We only need to kill `acquireJob`, because `releaseJob` never blocks.
|
| 533 | 567 | |
| 568 | +Shutdown starts with `killThread loop_tid`. The loop's exit handler then
|
|
| 569 | +runs `drainActiveChild` followed by `releaseAllHeld`; only then does the
|
|
| 570 | +loop signal `loop_finished_mvar`. This sequence makes the heldTokens
|
|
| 571 | +snapshot consistent because no other thread can mutate it once the loop and
|
|
| 572 | +its child are both dead.
|
|
| 573 | + |
|
| 534 | 574 | Note [Eventlog Messages for jsem]
|
| 535 | 575 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
| 536 | 576 | It can be tricky to verify that the work is shared adequately across different
|
| ... | ... | @@ -2445,6 +2445,8 @@ wWarningFlagsDeps = [minBound..maxBound] >>= \x -> case x of |
| 2445 | 2445 | Opt_WarnUnusableUnpackPragmas -> warnSpec x
|
| 2446 | 2446 | Opt_WarnPatternNamespaceSpecifier -> warnSpec x
|
| 2447 | 2447 | Opt_WarnUnrecognisedModifiers -> warnSpec x
|
| 2448 | + Opt_WarnSemaphoreVersionMismatch -> warnSpec x
|
|
| 2449 | + Opt_WarnSemaphoreOpenFailure -> warnSpec x
|
|
| 2448 | 2450 | |
| 2449 | 2451 | warningGroupsDeps :: [(Deprecation, FlagSpec WarningGroup)]
|
| 2450 | 2452 | warningGroupsDeps = map mk warningGroups
|
| ... | ... | @@ -403,6 +403,8 @@ type family GhcDiagnosticCode c = n | n -> c where |
| 403 | 403 | GhcDiagnosticCode "DriverInstantiationNodeInDependencyGeneration" = 74284
|
| 404 | 404 | GhcDiagnosticCode "DriverNoConfiguredLLVMToolchain" = 66599
|
| 405 | 405 | GhcDiagnosticCode "DriverMissingLinkableForModule" = 47338
|
| 406 | + GhcDiagnosticCode "DriverSemaphoreVersionMismatch" = 56206
|
|
| 407 | + GhcDiagnosticCode "DriverSemaphoreOpenFailure" = 19877
|
|
| 406 | 408 | |
| 407 | 409 | -- Constraint solver diagnostic codes
|
| 408 | 410 | GhcDiagnosticCode "BadTelescope" = 97739
|
| ... | ... | @@ -2721,6 +2721,37 @@ of ``-W(no-)*``. |
| 2721 | 2721 | f :: a %True -> a
|
| 2722 | 2722 | g :: a %(k :: Int) -> a
|
| 2723 | 2723 | |
| 2724 | +.. ghc-flag:: -Wsemaphore-version-mismatch
|
|
| 2725 | + :shortdesc: warn when GHC receives a ``-jsem`` semaphore whose protocol
|
|
| 2726 | + version is incompatible with the version this GHC supports.
|
|
| 2727 | + :type: dynamic
|
|
| 2728 | + :reverse: -Wno-semaphore-version-mismatch
|
|
| 2729 | + :category:
|
|
| 2730 | + |
|
| 2731 | + :since: 10.0.1
|
|
| 2732 | + |
|
| 2733 | + Warn when GHC is invoked with :ghc-flag:`-jsem` but the semaphore
|
|
| 2734 | + name indicates a protocol version that is incompatible with this GHC
|
|
| 2735 | + (e.g. an unversioned v1 name passed to a v2 GHC, or vice versa).
|
|
| 2736 | + When this occurs, GHC ignores ``-jsem`` and compiles modules sequentially.
|
|
| 2737 | + |
|
| 2738 | + This situation typically arises when ``cabal-install`` and GHC are built
|
|
| 2739 | + against different versions of the ``semaphore-compat`` library. Upgrading
|
|
| 2740 | + both to versions that use the same protocol resolves the mismatch.
|
|
| 2741 | + |
|
| 2742 | +.. ghc-flag:: -Wsemaphore-open-failure
|
|
| 2743 | + :shortdesc: warn when GHC cannot open the ``-jsem`` semaphore.
|
|
| 2744 | + :type: dynamic
|
|
| 2745 | + :reverse: -Wno-semaphore-open-failure
|
|
| 2746 | + :category:
|
|
| 2747 | + |
|
| 2748 | + :since: 10.0.1
|
|
| 2749 | + |
|
| 2750 | + Warn when GHC is invoked with :ghc-flag:`-jsem` but the semaphore
|
|
| 2751 | + cannot be opened (e.g. the socket does not exist or a system error
|
|
| 2752 | + occurred). When this occurs, GHC ignores ``-jsem`` and compiles
|
|
| 2753 | + modules sequentially.
|
|
| 2754 | + |
|
| 2724 | 2755 | ----
|
| 2725 | 2756 | |
| 2726 | 2757 | If you're feeling really paranoid, the :ghc-flag:`-dcore-lint` option is a good choice.
|
| ... | ... | @@ -797,7 +797,14 @@ There are two kinds of participants in the GHC Jobserver protocol: |
| 797 | 797 | |
| 798 | 798 | Perform compilation in parallel when possible, coordinating with other
|
| 799 | 799 | processes through the semaphore ⟨sem⟩ (specified as a string).
|
| 800 | - Error if the semaphore doesn't exist.
|
|
| 800 | + |
|
| 801 | + If the semaphore's protocol version is incompatible, GHC emits a
|
|
| 802 | + :ghc-flag:`-Wsemaphore-version-mismatch` warning and compiles
|
|
| 803 | + sequentially. If the semaphore cannot be opened for other reasons
|
|
| 804 | + (e.g. the socket does not exist), GHC emits a
|
|
| 805 | + :ghc-flag:`-Wsemaphore-open-failure` warning and compiles
|
|
| 806 | + sequentially. In both cases GHC uses only the implicit token
|
|
| 807 | + inherited from the parent process.
|
|
| 801 | 808 | |
| 802 | 809 | Use of ``-jsem`` will override use of :ghc-flag:`-j[⟨n⟩]`,
|
| 803 | 810 | and vice-versa.
|
| 1 | 1 | packages: ./
|
| 2 | 2 | ../utils/ghc-toolchain/
|
| 3 | 3 | ../libraries/ghc-platform/
|
| 4 | + ../libraries/semaphore-compat/
|
|
| 4 | 5 | |
| 5 | 6 | -- This essentially freezes the build plan for hadrian
|
| 6 | 7 | -- It would be wise to keep this up to date with the state set in .gitlab/ci.sh.
|
| ... | ... | @@ -172,6 +172,7 @@ executable hadrian |
| 172 | 172 | , base16-bytestring >= 0.1.1 && < 1.1.0.0
|
| 173 | 173 | , ghc-platform
|
| 174 | 174 | , ghc-toolchain
|
| 175 | + , semaphore-compat
|
|
| 175 | 176 | ghc-options: -Wall
|
| 176 | 177 | -Wincomplete-record-updates
|
| 177 | 178 | -Wredundant-constraints
|
| ... | ... | @@ -149,10 +149,6 @@ werror = |
| 149 | 149 | -- unix has many unused imports
|
| 150 | 150 | , package unix
|
| 151 | 151 | ? mconcat [arg "-Wwarn=unused-imports", arg "-Wwarn=unused-top-binds"]
|
| 152 | - -- semaphore-compat relies on sem_getvalue as provided by unix, which is
|
|
| 153 | - -- not implemented on Darwin and therefore throws a deprecation warning
|
|
| 154 | - , package semaphoreCompat
|
|
| 155 | - ? mconcat [arg "-Wwarn=deprecations"]
|
|
| 156 | 152 | ]
|
| 157 | 153 | , builder Ghc
|
| 158 | 154 | ? package rts
|
| ... | ... | @@ -25,6 +25,7 @@ import Utilities |
| 25 | 25 | import GHC.Toolchain as Toolchain hiding (HsCpp(HsCpp))
|
| 26 | 26 | import GHC.Platform.ArchOS
|
| 27 | 27 | import Settings.Program (ghcWithInterpreter)
|
| 28 | +import System.Semaphore (semaphoreVersion, getSemaphoreProtocolVersion)
|
|
| 28 | 29 | |
| 29 | 30 | -- | Track this file to rebuild generated files whenever it changes.
|
| 30 | 31 | trackGenerateHs :: Expr ()
|
| ... | ... | @@ -488,6 +489,7 @@ generateSettings settingsFile = do |
| 488 | 489 | , ("RTS ways", escapeArgs . map show . Set.toList <$> getRtsWays)
|
| 489 | 490 | , ("Relative Global Package DB", pure rel_pkg_db)
|
| 490 | 491 | , ("base unit-id", pure base_unit_id)
|
| 492 | + , ("Semaphore version", pure (show (getSemaphoreProtocolVersion semaphoreVersion)))
|
|
| 491 | 493 | ]
|
| 492 | 494 | let showTuple (k, v) = "(" ++ show k ++ ", " ++ show v ++ ")"
|
| 493 | 495 | pure $ case settings of
|
| ... | ... | @@ -231,6 +231,10 @@ packageArgs = do |
| 231 | 231 | , package hpcBin
|
| 232 | 232 | ? builder (Cabal Flags) ? arg "-build-tool-depends"
|
| 233 | 233 | |
| 234 | + ------------------------------ semaphore-compat ----------------------------
|
|
| 235 | + , package semaphoreCompat
|
|
| 236 | + ? builder (Cabal Flags) ? arg "-build-testing"
|
|
| 237 | + |
|
| 234 | 238 | ]
|
| 235 | 239 | |
| 236 | 240 | ghcInternalArgs :: Args
|
| ... | ... | @@ -16,6 +16,7 @@ packages: |
| 16 | 16 | - '.'
|
| 17 | 17 | - '../utils/ghc-toolchain'
|
| 18 | 18 | - '../libraries/ghc-platform'
|
| 19 | +- '../libraries/semaphore-compat'
|
|
| 19 | 20 | |
| 20 | 21 | nix:
|
| 21 | 22 | enable: false
|
| 1 | -Subproject commit 7929702401d49bc64d809c501ed5fe80aebc3cc1 |
|
| 1 | +Subproject commit 2453a03c00e25e30e321816d53c8dbdb113de08b |
| ... | ... | @@ -21,6 +21,8 @@ |
| 21 | 21 | [GHC-29747] is untested (constructor = DriverMissingSafeHaskellMode)
|
| 22 | 22 | [GHC-74284] is untested (constructor = DriverInstantiationNodeInDependencyGeneration)
|
| 23 | 23 | [GHC-66599] is untested (constructor = DriverNoConfiguredLLVMToolchain)
|
| 24 | +[GHC-56206] is untested (constructor = DriverSemaphoreVersionMismatch)
|
|
| 25 | +[GHC-19877] is untested (constructor = DriverSemaphoreOpenFailure)
|
|
| 24 | 26 | [GHC-81325] is untested (constructor = ExpectingMoreArguments)
|
| 25 | 27 | [GHC-78125] is untested (constructor = AmbiguityPreventsSolvingCt)
|
| 26 | 28 | [GHC-84170] is untested (constructor = TcRnModMissingRealSrcSpan)
|