Duncan Coutts pushed to branch wip/dcoutts/issue-27105-stopTicker-2 at Glasgow Haskell Compiler / GHC
Commits:
-
5ac9ce7d
by Zubin Duggal at 2026-06-10T21:26:32+05:30
-
c9015f09
by sheaf at 2026-06-11T12:40:28-04:00
-
7ab90288
by Rodrigo Mesquita at 2026-06-11T12:41:11-04:00
-
de196632
by Duncan Coutts at 2026-06-11T22:55:19+01:00
-
9cc10562
by Duncan Coutts at 2026-06-11T22:55:19+01:00
-
567ac8e7
by Duncan Coutts at 2026-06-11T22:55:19+01:00
-
f59329b3
by Duncan Coutts at 2026-06-11T22:55:19+01:00
-
0f7eb782
by Duncan Coutts at 2026-06-11T22:55:19+01:00
-
c3b93527
by Duncan Coutts at 2026-06-11T22:55:19+01:00
-
86cfb7b6
by Duncan Coutts at 2026-06-11T22:55:19+01:00
15 changed files:
- + changelog.d/T27046
- + changelog.d/hadrian-stale-package-confs-26661
- compiler/GHC/CmmToAsm/AArch64/CodeGen.hs
- hadrian/src/Hadrian/Haskell/Cabal/Parse.hs
- rts/Capability.c
- rts/Schedule.c
- rts/Timer.c
- rts/eventlog/EventLog.c
- rts/eventlog/EventLog.h
- rts/include/rts/OSThreads.h
- + testsuite/tests/codeGen/should_run/T27046.hs
- + testsuite/tests/codeGen/should_run/T27046_cmm.cmm
- testsuite/tests/codeGen/should_run/all.T
- testsuite/tests/rts/T27131.hs
- testsuite/tests/rts/T27131.stdout
Changes:
| 1 | +section: compiler
|
|
| 2 | +issues: #27046
|
|
| 3 | +mrs: !16031
|
|
| 4 | +synopsis:
|
|
| 5 | + Avoid AArch64 register clobbering bug in MUL2
|
|
| 6 | +description:
|
|
| 7 | + Fixes an issue in which, on AArch64, code generation for the MUL2 operation
|
|
| 8 | + could clobber one of the input operands when computing the lower bits, which
|
|
| 9 | + rendered invalid the subsequent computation of the high bits. |
| 1 | +section: packaging
|
|
| 2 | +synopsis: Hadrian no longer leaves stale `.conf` files in its package databases
|
|
| 3 | + when rebuilding in the same build root with different settings (e.g. another
|
|
| 4 | + flavour, or when hashes change with +hash-unit-ids).
|
|
| 5 | +issues: #26661
|
|
| 6 | +mrs: !15186 |
| ... | ... | @@ -2300,11 +2300,19 @@ genCCall target dest_regs arg_regs = do |
| 2300 | 2300 | let lo = getRegisterReg platform (CmmLocal dst_lo)
|
| 2301 | 2301 | hi = getRegisterReg platform (CmmLocal dst_hi)
|
| 2302 | 2302 | nd = getRegisterReg platform (CmmLocal dst_needed)
|
| 2303 | + |
|
| 2304 | + -- Generate a fresh virtual register for the low word computation.
|
|
| 2305 | + -- This avoids clobbering reg_a or reg_b in the first MUL instruction,
|
|
| 2306 | + -- which could for example happen if 'lo' and 'reg_a' are the same
|
|
| 2307 | + -- virtual register.
|
|
| 2308 | + tmp_lo <- getNewRegNat II64
|
|
| 2309 | + |
|
| 2303 | 2310 | return $
|
| 2304 | 2311 | code_x `appOL`
|
| 2305 | 2312 | code_y `snocOL`
|
| 2306 | - MUL II64 (OpReg W64 lo) (OpReg W64 reg_a) (OpReg W64 reg_b) `snocOL`
|
|
| 2313 | + MUL II64 (OpReg W64 tmp_lo) (OpReg W64 reg_a) (OpReg W64 reg_b) `snocOL`
|
|
| 2307 | 2314 | SMULH (OpReg W64 hi) (OpReg W64 reg_a) (OpReg W64 reg_b) `snocOL`
|
| 2315 | + MOV (OpReg W64 lo) (OpReg W64 tmp_lo) `snocOL`
|
|
| 2308 | 2316 | -- Are all high bits equal to the sign bit of the low word?
|
| 2309 | 2317 | -- nd = (hi == ASR(lo,width-1)) ? 1 : 0
|
| 2310 | 2318 | CMP (OpReg W64 hi) (OpRegShift W64 lo SASR (widthInBits w - 1)) `snocOL`
|
| ... | ... | @@ -15,6 +15,7 @@ module Hadrian.Haskell.Cabal.Parse ( |
| 15 | 15 | ) where
|
| 16 | 16 | |
| 17 | 17 | import Data.Bifunctor
|
| 18 | +import Data.Char (isDigit)
|
|
| 18 | 19 | import Data.List.Extra
|
| 19 | 20 | import Development.Shake
|
| 20 | 21 | import qualified Distribution.Compat.Graph as Graph
|
| ... | ... | @@ -55,6 +56,8 @@ import Builder |
| 55 | 56 | import Context
|
| 56 | 57 | import Settings
|
| 57 | 58 | import Distribution.Simple.LocalBuildInfo
|
| 59 | +import Distribution.Types.LocalBuildInfo (allTargetsInBuildOrder')
|
|
| 60 | +import Distribution.Types.TargetInfo (TargetInfo (..))
|
|
| 58 | 61 | import qualified Distribution.Simple.Register as C
|
| 59 | 62 | import System.Directory (getCurrentDirectory)
|
| 60 | 63 | import qualified Distribution.InstalledPackageInfo as CP
|
| ... | ... | @@ -394,35 +397,48 @@ registerPackage rs context = do |
| 394 | 397 | -- Note: the @cPath@ is ignored. The path that's used is the 'buildDir' path
|
| 395 | 398 | -- from the local build info @lbi@.
|
| 396 | 399 | lbi <- liftIO $ C.getPersistBuildConfig Nothing (C.makeSymbolicPath cPath)
|
| 397 | - liftIO $ register db_path pid pd lbi
|
|
| 400 | + -- This runs `ghc --abi-hash`, so do it outside the critical section below.
|
|
| 401 | + installedPkgInfo <- liftIO $ generateRegistrationInfo pd lbi
|
|
| 402 | + |
|
| 403 | + let pkg_name = pkgName (package context)
|
|
| 404 | + -- Is this a pkg.conf for a previous build?
|
|
| 405 | + -- we want to match "ghc-9.15.1-abcd.conf" but not "ghc-boot-9.15.1.conf"
|
|
| 406 | + isPkgConf f = case stripPrefix (pkg_name ++ "-") (takeBaseName f) of
|
|
| 407 | + Just (c:_) -> isDigit c
|
|
| 408 | + _ -> takeBaseName f == pkg_name
|
|
| 409 | + |
|
| 410 | + -- Unlike `ghc-pkg update/register` (used to populate the inplace and stage0
|
|
| 411 | + -- databases), writing the .conf file directly doesn't remove units this
|
|
| 412 | + -- package was previously registered under. Stale .conf files from earlier
|
|
| 413 | + -- builds make this package's modules ambiguous (#26661), so delete them
|
|
| 414 | + -- before writing the new .conf file.
|
|
| 415 | + withResources rs $ do
|
|
| 416 | + confs <- liftIO $ getDirectoryFilesIO db_path ["*.conf"]
|
|
| 417 | + mapM_ (removeFile . (db_path </>))
|
|
| 418 | + [ f | f <- confs, isPkgConf f, takeBaseName f /= pid ]
|
|
| 419 | + liftIO $ writeUTF8File (db_path </> pid <.> "conf")
|
|
| 420 | + (CP.showInstalledPackageInfo installedPkgInfo)
|
|
| 398 | 421 | -- Then after the register, which just writes the .conf file, do the recache step.
|
| 399 | 422 | buildWithResources rs $
|
| 400 | 423 | target context (GhcPkg Recache (stage context)) [] []
|
| 401 | 424 | |
| 402 | 425 | -- This is copied and simplified from Cabal, because we want to install the package
|
| 403 | 426 | -- into a different package database to the one it was configured against.
|
| 404 | -register :: FilePath
|
|
| 405 | - -> String -- ^ Package Identifier
|
|
| 406 | - -> C.PackageDescription
|
|
| 407 | - -> LocalBuildInfo
|
|
| 408 | - -> IO ()
|
|
| 409 | -register pkg_db pid pd lbi
|
|
| 410 | - = withLibLBI pd lbi $ \lib clbi -> do
|
|
| 411 | - |
|
| 412 | - when reloc $ error "register does not support reloc"
|
|
| 413 | - installedPkgInfo <- generateRegistrationInfo pd lbi lib clbi
|
|
| 414 | - writeRegistrationFile installedPkgInfo
|
|
| 415 | - |
|
| 416 | - where
|
|
| 417 | - regFile = pkg_db </> pid <.> "conf"
|
|
| 418 | - reloc = relocatable lbi
|
|
| 419 | - |
|
| 420 | - generateRegistrationInfo pkg lbi lib clbi = do
|
|
| 421 | - abi_hash <- C.mkAbiHash <$> GHC.libAbiHash C.silent pkg lbi lib clbi
|
|
| 422 | - return (C.absoluteInstalledPackageInfo pkg abi_hash lib lbi clbi)
|
|
| 423 | - |
|
| 424 | - writeRegistrationFile installedPkgInfo = do
|
|
| 425 | - writeUTF8File regFile (CP.showInstalledPackageInfo installedPkgInfo)
|
|
| 427 | +-- See generateRegistrationInfo in Distribution.Simple.Register. we can't use it
|
|
| 428 | +-- directly because it computes the abi-hash using Cabal's internal package
|
|
| 429 | +-- database, which hadrian never creates.
|
|
| 430 | +generateRegistrationInfo :: C.PackageDescription
|
|
| 431 | + -> LocalBuildInfo
|
|
| 432 | + -> IO Installed.InstalledPackageInfo
|
|
| 433 | +generateRegistrationInfo pd lbi = do
|
|
| 434 | + when (relocatable lbi) $ error "register does not support reloc"
|
|
| 435 | + case [ (lib, targetCLBI tgt) | tgt <- allTargetsInBuildOrder' pd lbi
|
|
| 436 | + , CLib lib <- [targetComponent tgt] ] of
|
|
| 437 | + [(lib, clbi)] -> do
|
|
| 438 | + abi_hash <- C.mkAbiHash <$> GHC.libAbiHash C.silent pd lbi lib clbi
|
|
| 439 | + return (C.absoluteInstalledPackageInfo pd abi_hash lib lbi clbi)
|
|
| 440 | + libs -> error $ "generateRegistrationInfo: expected exactly one library for "
|
|
| 441 | + ++ C.display (C.package pd) ++ ", got " ++ show (length libs)
|
|
| 426 | 442 | |
| 427 | 443 | |
| 428 | 444 | -- | Build autogenerated files @autogen/cabal_macros.h@ and @autogen/Paths_*.hs@.
|
| ... | ... | @@ -443,13 +443,6 @@ void |
| 443 | 443 | moreCapabilities (uint32_t from USED_IF_THREADS, uint32_t to USED_IF_THREADS)
|
| 444 | 444 | {
|
| 445 | 445 | #if defined(THREADED_RTS)
|
| 446 | - // We must disable the timer while we do this since the tick handler may
|
|
| 447 | - // call contextSwitchAllCapabilities, which may see the capabilities array
|
|
| 448 | - // as we free it. The alternative would be to protect the capabilities
|
|
| 449 | - // array with a lock but this seems more expensive than necessary.
|
|
| 450 | - // See #17289.
|
|
| 451 | - stopTimer();
|
|
| 452 | - |
|
| 453 | 446 | if (to == 1) {
|
| 454 | 447 | // THREADED_RTS must work on builds that don't have a mutable
|
| 455 | 448 | // BaseReg (eg. unregisterised), so in this case
|
| ... | ... | @@ -470,8 +463,6 @@ moreCapabilities (uint32_t from USED_IF_THREADS, uint32_t to USED_IF_THREADS) |
| 470 | 463 | }
|
| 471 | 464 | |
| 472 | 465 | debugTrace(DEBUG_sched, "allocated %d more capabilities", to - from);
|
| 473 | - |
|
| 474 | - startTimer();
|
|
| 475 | 466 | #endif
|
| 476 | 467 | }
|
| 477 | 468 |
| ... | ... | @@ -37,6 +37,7 @@ |
| 37 | 37 | #include "win32/AsyncWinIO.h"
|
| 38 | 38 | #endif
|
| 39 | 39 | #include "Trace.h"
|
| 40 | +#include "eventlog/EventLog.h"
|
|
| 40 | 41 | #include "RaiseAsync.h"
|
| 41 | 42 | #include "Threads.h"
|
| 42 | 43 | #include "Timer.h"
|
| ... | ... | @@ -2100,24 +2101,31 @@ forkProcess(HsStablePtr *entry |
| 2100 | 2101 | ACQUIRE_LOCK(&all_tasks_mutex);
|
| 2101 | 2102 | #endif
|
| 2102 | 2103 | |
| 2103 | - stopTimer(); // See #4074
|
|
| 2104 | - |
|
| 2105 | 2104 | #if defined(TRACING)
|
| 2106 | - flushAllCapsEventsBufs(); // so that child won't inherit dirty file buffers
|
|
| 2105 | +#if defined(HAVE_PREEMPTION)
|
|
| 2106 | + // We must hold the eventlog global mutex over the fork to prevent the
|
|
| 2107 | + // timer thread from trying to post events. While holding the mutex we need
|
|
| 2108 | + // to flush the eventlogs (global and per-cap) so that child won't inherit
|
|
| 2109 | + // dirty eventlog buffers or file buffers.
|
|
| 2110 | + ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 2111 | +#endif
|
|
| 2112 | + flushAllCapsEventsBufs_();
|
|
| 2107 | 2113 | #endif
|
| 2108 | 2114 | |
| 2109 | 2115 | pid = fork();
|
| 2110 | 2116 | |
| 2111 | 2117 | if (pid) { // parent
|
| 2112 | 2118 | |
| 2113 | - startTimer(); // #4074
|
|
| 2114 | - |
|
| 2115 | 2119 | RELEASE_LOCK(&sched_mutex);
|
| 2116 | 2120 | RELEASE_LOCK(&sm_mutex);
|
| 2117 | 2121 | RELEASE_LOCK(&stable_ptr_mutex);
|
| 2118 | 2122 | RELEASE_LOCK(&stable_name_mutex);
|
| 2119 | 2123 | RELEASE_LOCK(&task->lock);
|
| 2120 | 2124 | |
| 2125 | +#if defined(TRACING) && defined(HAVE_PREEMPTION)
|
|
| 2126 | + RELEASE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 2127 | +#endif
|
|
| 2128 | + |
|
| 2121 | 2129 | #if defined(THREADED_RTS)
|
| 2122 | 2130 | /* N.B. releaseCapability_ below may need to take all_tasks_mutex */
|
| 2123 | 2131 | RELEASE_LOCK(&all_tasks_mutex);
|
| ... | ... | @@ -2303,12 +2311,6 @@ setNumCapabilities (uint32_t new_n_capabilities USED_IF_THREADS) |
| 2303 | 2311 | cap = rts_lock();
|
| 2304 | 2312 | task = cap->running_task;
|
| 2305 | 2313 | |
| 2306 | - |
|
| 2307 | - // N.B. We must stop the interval timer while we are changing the
|
|
| 2308 | - // capabilities array lest handle_tick may try to context switch
|
|
| 2309 | - // an old capability. See #17289.
|
|
| 2310 | - stopTimer();
|
|
| 2311 | - |
|
| 2312 | 2314 | stopAllCapabilities(&cap, task);
|
| 2313 | 2315 | |
| 2314 | 2316 | if (new_n_capabilities < enabled_capabilities)
|
| ... | ... | @@ -2364,9 +2366,7 @@ setNumCapabilities (uint32_t new_n_capabilities USED_IF_THREADS) |
| 2364 | 2366 | tracingAddCapabilities(n_capabilities, new_n_capabilities);
|
| 2365 | 2367 | #endif
|
| 2366 | 2368 | |
| 2367 | - // Resize the capabilities array
|
|
| 2368 | - // NB. after this, capabilities points somewhere new. Any pointers
|
|
| 2369 | - // of type (Capability *) are now invalid.
|
|
| 2369 | + // Allocate and initialise the extra capabilities
|
|
| 2370 | 2370 | moreCapabilities(n_capabilities, new_n_capabilities);
|
| 2371 | 2371 | |
| 2372 | 2372 | // Resize and update storage manager data structures
|
| ... | ... | @@ -2394,8 +2394,6 @@ setNumCapabilities (uint32_t new_n_capabilities USED_IF_THREADS) |
| 2394 | 2394 | // Notify IO manager that the number of capabilities has changed.
|
| 2395 | 2395 | notifyIOManagerCapabilitiesChanged(&cap);
|
| 2396 | 2396 | |
| 2397 | - startTimer();
|
|
| 2398 | - |
|
| 2399 | 2397 | rts_unlock(cap);
|
| 2400 | 2398 | |
| 2401 | 2399 | #endif // THREADED_RTS
|
| ... | ... | @@ -28,11 +28,6 @@ |
| 28 | 28 | #include "RtsSignals.h"
|
| 29 | 29 | #include "rts/EventLogWriter.h"
|
| 30 | 30 | |
| 31 | -// See Note [No timer on wasm32]
|
|
| 32 | -#if !defined(wasm32_HOST_ARCH)
|
|
| 33 | -#define HAVE_PREEMPTION
|
|
| 34 | -#endif
|
|
| 35 | - |
|
| 36 | 31 | // This global counter is used to allow multiple threads to stop the
|
| 37 | 32 | // timer temporarily with a stopTimer()/startTimer() pair. If
|
| 38 | 33 | // timer_enabled == 0 timer is enabled
|
| ... | ... | @@ -129,8 +129,11 @@ typedef struct _EventsBuf { |
| 129 | 129 | static EventsBuf *capEventBuf; // one EventsBuf for each Capability
|
| 130 | 130 | |
| 131 | 131 | static EventsBuf eventBuf; // an EventsBuf not associated with any Capability
|
| 132 | -#if defined(THREADED_RTS)
|
|
| 133 | -static Mutex eventBufMutex; // protected by this mutex
|
|
| 132 | +#if defined(HAVE_PREEMPTION)
|
|
| 133 | +// Note that this mutex is used even in the non-threaded RTS, since the timer
|
|
| 134 | +// thread posts events and flushes. So _all_ uses of this mutex must use
|
|
| 135 | +// ACQUIRE_LOCK_ALWAYS/RELEASE_LOCK_ALWAYS.
|
|
| 136 | +Mutex eventBufMutex; // protects eventBuf above
|
|
| 134 | 137 | #endif
|
| 135 | 138 | |
| 136 | 139 | // Event type
|
| ... | ... | @@ -393,8 +396,10 @@ initEventLogging(void) |
| 393 | 396 | moreCapEventBufs(0, get_n_capabilities());
|
| 394 | 397 | |
| 395 | 398 | initEventsBuf(&eventBuf, EVENT_LOG_SIZE, (EventCapNo)(-1));
|
| 396 | -#if defined(THREADED_RTS)
|
|
| 399 | +#if defined(HAVE_PREEMPTION)
|
|
| 397 | 400 | initMutex(&eventBufMutex);
|
| 401 | +#endif
|
|
| 402 | +#if defined(THREADED_RTS)
|
|
| 398 | 403 | initMutex(&state_change_mutex);
|
| 399 | 404 | #endif
|
| 400 | 405 | }
|
| ... | ... | @@ -416,7 +421,7 @@ startEventLogging_(void) |
| 416 | 421 | {
|
| 417 | 422 | initEventLogWriter();
|
| 418 | 423 | |
| 419 | - ACQUIRE_LOCK(&eventBufMutex);
|
|
| 424 | + ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 420 | 425 | postHeaderEvents();
|
| 421 | 426 | |
| 422 | 427 | /*
|
| ... | ... | @@ -425,7 +430,7 @@ startEventLogging_(void) |
| 425 | 430 | */
|
| 426 | 431 | printAndClearEventBuf(&eventBuf);
|
| 427 | 432 | |
| 428 | - RELEASE_LOCK(&eventBufMutex);
|
|
| 433 | + RELEASE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 429 | 434 | |
| 430 | 435 | return true;
|
| 431 | 436 | }
|
| ... | ... | @@ -495,7 +500,7 @@ endEventLogging(void) |
| 495 | 500 | |
| 496 | 501 | flushEventLog_(NULL);
|
| 497 | 502 | |
| 498 | - ACQUIRE_LOCK(&eventBufMutex);
|
|
| 503 | + ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 499 | 504 | |
| 500 | 505 | // Mark end of events (data).
|
| 501 | 506 | postEventTypeNum(&eventBuf, EVENT_DATA_END);
|
| ... | ... | @@ -503,7 +508,7 @@ endEventLogging(void) |
| 503 | 508 | // Flush the end of data marker.
|
| 504 | 509 | printAndClearEventBuf(&eventBuf);
|
| 505 | 510 | |
| 506 | - RELEASE_LOCK(&eventBufMutex);
|
|
| 511 | + RELEASE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 507 | 512 | |
| 508 | 513 | stopEventLogWriter();
|
| 509 | 514 | event_log_writer = NULL;
|
| ... | ... | @@ -666,7 +671,7 @@ void |
| 666 | 671 | postCapEvent (EventTypeNum tag,
|
| 667 | 672 | EventCapNo capno)
|
| 668 | 673 | {
|
| 669 | - ACQUIRE_LOCK(&eventBufMutex);
|
|
| 674 | + ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 670 | 675 | ensureRoomForEvent(&eventBuf, tag);
|
| 671 | 676 | |
| 672 | 677 | postEventHeader(&eventBuf, tag);
|
| ... | ... | @@ -685,14 +690,14 @@ postCapEvent (EventTypeNum tag, |
| 685 | 690 | barf("postCapEvent: unknown event tag %d", tag);
|
| 686 | 691 | }
|
| 687 | 692 | |
| 688 | - RELEASE_LOCK(&eventBufMutex);
|
|
| 693 | + RELEASE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 689 | 694 | }
|
| 690 | 695 | |
| 691 | 696 | void postCapsetEvent (EventTypeNum tag,
|
| 692 | 697 | EventCapsetID capset,
|
| 693 | 698 | StgWord info)
|
| 694 | 699 | {
|
| 695 | - ACQUIRE_LOCK(&eventBufMutex);
|
|
| 700 | + ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 696 | 701 | ensureRoomForEvent(&eventBuf, tag);
|
| 697 | 702 | |
| 698 | 703 | postEventHeader(&eventBuf, tag);
|
| ... | ... | @@ -726,7 +731,7 @@ void postCapsetEvent (EventTypeNum tag, |
| 726 | 731 | barf("postCapsetEvent: unknown event tag %d", tag);
|
| 727 | 732 | }
|
| 728 | 733 | |
| 729 | - RELEASE_LOCK(&eventBufMutex);
|
|
| 734 | + RELEASE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 730 | 735 | }
|
| 731 | 736 | |
| 732 | 737 | void postCapsetStrEvent (EventTypeNum tag,
|
| ... | ... | @@ -740,14 +745,14 @@ void postCapsetStrEvent (EventTypeNum tag, |
| 740 | 745 | return;
|
| 741 | 746 | }
|
| 742 | 747 | |
| 743 | - ACQUIRE_LOCK(&eventBufMutex);
|
|
| 748 | + ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 744 | 749 | |
| 745 | 750 | if (!hasRoomForVariableEvent(&eventBuf, size)){
|
| 746 | 751 | printAndClearEventBuf(&eventBuf);
|
| 747 | 752 | |
| 748 | 753 | if (!hasRoomForVariableEvent(&eventBuf, size)){
|
| 749 | 754 | errorBelch("Event size exceeds buffer size, bail out");
|
| 750 | - RELEASE_LOCK(&eventBufMutex);
|
|
| 755 | + RELEASE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 751 | 756 | return;
|
| 752 | 757 | }
|
| 753 | 758 | }
|
| ... | ... | @@ -758,7 +763,7 @@ void postCapsetStrEvent (EventTypeNum tag, |
| 758 | 763 | |
| 759 | 764 | postBuf(&eventBuf, (StgWord8*) msg, strsize);
|
| 760 | 765 | |
| 761 | - RELEASE_LOCK(&eventBufMutex);
|
|
| 766 | + RELEASE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 762 | 767 | }
|
| 763 | 768 | |
| 764 | 769 | void postCapsetVecEvent (EventTypeNum tag,
|
| ... | ... | @@ -783,14 +788,14 @@ void postCapsetVecEvent (EventTypeNum tag, |
| 783 | 788 | }
|
| 784 | 789 | }
|
| 785 | 790 | |
| 786 | - ACQUIRE_LOCK(&eventBufMutex);
|
|
| 791 | + ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 787 | 792 | |
| 788 | 793 | if (!hasRoomForVariableEvent(&eventBuf, size)){
|
| 789 | 794 | printAndClearEventBuf(&eventBuf);
|
| 790 | 795 | |
| 791 | 796 | if(!hasRoomForVariableEvent(&eventBuf, size)){
|
| 792 | 797 | errorBelch("Event size exceeds buffer size, bail out");
|
| 793 | - RELEASE_LOCK(&eventBufMutex);
|
|
| 798 | + RELEASE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 794 | 799 | return;
|
| 795 | 800 | }
|
| 796 | 801 | }
|
| ... | ... | @@ -804,7 +809,7 @@ void postCapsetVecEvent (EventTypeNum tag, |
| 804 | 809 | postBuf(&eventBuf, (StgWord8*) argv[i], 1 + strlen(argv[i]));
|
| 805 | 810 | }
|
| 806 | 811 | |
| 807 | - RELEASE_LOCK(&eventBufMutex);
|
|
| 812 | + RELEASE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 808 | 813 | }
|
| 809 | 814 | |
| 810 | 815 | void postWallClockTime (EventCapsetID capset)
|
| ... | ... | @@ -813,7 +818,7 @@ void postWallClockTime (EventCapsetID capset) |
| 813 | 818 | StgWord64 sec;
|
| 814 | 819 | StgWord32 nsec;
|
| 815 | 820 | |
| 816 | - ACQUIRE_LOCK(&eventBufMutex);
|
|
| 821 | + ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 817 | 822 | |
| 818 | 823 | /* The EVENT_WALL_CLOCK_TIME event is intended to allow programs
|
| 819 | 824 | reading the eventlog to match up the event timestamps with wall
|
| ... | ... | @@ -846,7 +851,7 @@ void postWallClockTime (EventCapsetID capset) |
| 846 | 851 | postWord64(&eventBuf, sec);
|
| 847 | 852 | postWord32(&eventBuf, nsec);
|
| 848 | 853 | |
| 849 | - RELEASE_LOCK(&eventBufMutex);
|
|
| 854 | + RELEASE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 850 | 855 | }
|
| 851 | 856 | |
| 852 | 857 | /*
|
| ... | ... | @@ -885,7 +890,7 @@ void postEventHeapInfo (EventCapsetID heap_capset, |
| 885 | 890 | W_ mblockSize,
|
| 886 | 891 | W_ blockSize)
|
| 887 | 892 | {
|
| 888 | - ACQUIRE_LOCK(&eventBufMutex);
|
|
| 893 | + ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 889 | 894 | ensureRoomForEvent(&eventBuf, EVENT_HEAP_INFO_GHC);
|
| 890 | 895 | |
| 891 | 896 | postEventHeader(&eventBuf, EVENT_HEAP_INFO_GHC);
|
| ... | ... | @@ -899,7 +904,7 @@ void postEventHeapInfo (EventCapsetID heap_capset, |
| 899 | 904 | postWord64(&eventBuf, mblockSize);
|
| 900 | 905 | postWord64(&eventBuf, blockSize);
|
| 901 | 906 | |
| 902 | - RELEASE_LOCK(&eventBufMutex);
|
|
| 907 | + RELEASE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 903 | 908 | }
|
| 904 | 909 | |
| 905 | 910 | void postEventGcStats (Capability *cap,
|
| ... | ... | @@ -952,7 +957,7 @@ void postTaskCreateEvent (EventTaskId taskId, |
| 952 | 957 | EventCapNo capno,
|
| 953 | 958 | EventKernelThreadId tid)
|
| 954 | 959 | {
|
| 955 | - ACQUIRE_LOCK(&eventBufMutex);
|
|
| 960 | + ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 956 | 961 | ensureRoomForEvent(&eventBuf, EVENT_TASK_CREATE);
|
| 957 | 962 | |
| 958 | 963 | postEventHeader(&eventBuf, EVENT_TASK_CREATE);
|
| ... | ... | @@ -961,14 +966,14 @@ void postTaskCreateEvent (EventTaskId taskId, |
| 961 | 966 | postCapNo(&eventBuf, capno);
|
| 962 | 967 | postKernelThreadId(&eventBuf, tid);
|
| 963 | 968 | |
| 964 | - RELEASE_LOCK(&eventBufMutex);
|
|
| 969 | + RELEASE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 965 | 970 | }
|
| 966 | 971 | |
| 967 | 972 | void postTaskMigrateEvent (EventTaskId taskId,
|
| 968 | 973 | EventCapNo capno,
|
| 969 | 974 | EventCapNo new_capno)
|
| 970 | 975 | {
|
| 971 | - ACQUIRE_LOCK(&eventBufMutex);
|
|
| 976 | + ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 972 | 977 | ensureRoomForEvent(&eventBuf, EVENT_TASK_MIGRATE);
|
| 973 | 978 | |
| 974 | 979 | postEventHeader(&eventBuf, EVENT_TASK_MIGRATE);
|
| ... | ... | @@ -977,28 +982,28 @@ void postTaskMigrateEvent (EventTaskId taskId, |
| 977 | 982 | postCapNo(&eventBuf, capno);
|
| 978 | 983 | postCapNo(&eventBuf, new_capno);
|
| 979 | 984 | |
| 980 | - RELEASE_LOCK(&eventBufMutex);
|
|
| 985 | + RELEASE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 981 | 986 | }
|
| 982 | 987 | |
| 983 | 988 | void postTaskDeleteEvent (EventTaskId taskId)
|
| 984 | 989 | {
|
| 985 | - ACQUIRE_LOCK(&eventBufMutex);
|
|
| 990 | + ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 986 | 991 | ensureRoomForEvent(&eventBuf, EVENT_TASK_DELETE);
|
| 987 | 992 | |
| 988 | 993 | postEventHeader(&eventBuf, EVENT_TASK_DELETE);
|
| 989 | 994 | /* EVENT_TASK_DELETE (taskID) */
|
| 990 | 995 | postTaskId(&eventBuf, taskId);
|
| 991 | 996 | |
| 992 | - RELEASE_LOCK(&eventBufMutex);
|
|
| 997 | + RELEASE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 993 | 998 | }
|
| 994 | 999 | |
| 995 | 1000 | void
|
| 996 | 1001 | postEventNoCap (EventTypeNum tag)
|
| 997 | 1002 | {
|
| 998 | - ACQUIRE_LOCK(&eventBufMutex);
|
|
| 1003 | + ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 999 | 1004 | ensureRoomForEvent(&eventBuf, tag);
|
| 1000 | 1005 | postEventHeader(&eventBuf, tag);
|
| 1001 | - RELEASE_LOCK(&eventBufMutex);
|
|
| 1006 | + RELEASE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 1002 | 1007 | }
|
| 1003 | 1008 | |
| 1004 | 1009 | void
|
| ... | ... | @@ -1042,9 +1047,9 @@ void postLogMsg(EventsBuf *eb, EventTypeNum type, char *msg, va_list ap) |
| 1042 | 1047 | |
| 1043 | 1048 | void postMsg(char *msg, va_list ap)
|
| 1044 | 1049 | {
|
| 1045 | - ACQUIRE_LOCK(&eventBufMutex);
|
|
| 1050 | + ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 1046 | 1051 | postLogMsg(&eventBuf, EVENT_LOG_MSG, msg, ap);
|
| 1047 | - RELEASE_LOCK(&eventBufMutex);
|
|
| 1052 | + RELEASE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 1048 | 1053 | }
|
| 1049 | 1054 | |
| 1050 | 1055 | void postCapMsg(Capability *cap, char *msg, va_list ap)
|
| ... | ... | @@ -1138,32 +1143,32 @@ void postConcUpdRemSetFlush(Capability *cap) |
| 1138 | 1143 | |
| 1139 | 1144 | void postConcMarkEnd(StgWord32 marked_obj_count)
|
| 1140 | 1145 | {
|
| 1141 | - ACQUIRE_LOCK(&eventBufMutex);
|
|
| 1146 | + ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 1142 | 1147 | ensureRoomForEvent(&eventBuf, EVENT_CONC_MARK_END);
|
| 1143 | 1148 | postEventHeader(&eventBuf, EVENT_CONC_MARK_END);
|
| 1144 | 1149 | postWord32(&eventBuf, marked_obj_count);
|
| 1145 | - RELEASE_LOCK(&eventBufMutex);
|
|
| 1150 | + RELEASE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 1146 | 1151 | }
|
| 1147 | 1152 | |
| 1148 | 1153 | void postNonmovingHeapCensus(uint16_t blk_size,
|
| 1149 | 1154 | const struct NonmovingAllocCensus *census)
|
| 1150 | 1155 | {
|
| 1151 | - ACQUIRE_LOCK(&eventBufMutex);
|
|
| 1156 | + ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 1152 | 1157 | postEventHeader(&eventBuf, EVENT_NONMOVING_HEAP_CENSUS);
|
| 1153 | 1158 | postWord16(&eventBuf, blk_size);
|
| 1154 | 1159 | postWord32(&eventBuf, census->n_active_segs);
|
| 1155 | 1160 | postWord32(&eventBuf, census->n_filled_segs);
|
| 1156 | 1161 | postWord32(&eventBuf, census->n_live_blocks);
|
| 1157 | - RELEASE_LOCK(&eventBufMutex);
|
|
| 1162 | + RELEASE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 1158 | 1163 | }
|
| 1159 | 1164 | |
| 1160 | 1165 | void postNonmovingPrunedSegments(uint32_t pruned_segments, uint32_t free_segments)
|
| 1161 | 1166 | {
|
| 1162 | - ACQUIRE_LOCK(&eventBufMutex);
|
|
| 1167 | + ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 1163 | 1168 | postEventHeader(&eventBuf, EVENT_NONMOVING_PRUNED_SEGMENTS);
|
| 1164 | 1169 | postWord32(&eventBuf, pruned_segments);
|
| 1165 | 1170 | postWord32(&eventBuf, free_segments);
|
| 1166 | - RELEASE_LOCK(&eventBufMutex);
|
|
| 1171 | + RELEASE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 1167 | 1172 | }
|
| 1168 | 1173 | |
| 1169 | 1174 | void closeBlockMarker (EventsBuf *ebuf)
|
| ... | ... | @@ -1224,7 +1229,7 @@ static HeapProfBreakdown getHeapProfBreakdown(void) |
| 1224 | 1229 | |
| 1225 | 1230 | void postHeapProfBegin(void)
|
| 1226 | 1231 | {
|
| 1227 | - ACQUIRE_LOCK(&eventBufMutex);
|
|
| 1232 | + ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 1228 | 1233 | PROFILING_FLAGS *flags = &RtsFlags.ProfFlags;
|
| 1229 | 1234 | StgWord modSelector_len =
|
| 1230 | 1235 | flags->modSelector ? strlen(flags->modSelector) : 0;
|
| ... | ... | @@ -1258,42 +1263,42 @@ void postHeapProfBegin(void) |
| 1258 | 1263 | postStringLen(&eventBuf, flags->ccsSelector, ccsSelector_len);
|
| 1259 | 1264 | postStringLen(&eventBuf, flags->retainerSelector, retainerSelector_len);
|
| 1260 | 1265 | postStringLen(&eventBuf, flags->bioSelector, bioSelector_len);
|
| 1261 | - RELEASE_LOCK(&eventBufMutex);
|
|
| 1266 | + RELEASE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 1262 | 1267 | }
|
| 1263 | 1268 | |
| 1264 | 1269 | void postHeapProfSampleBegin(StgInt era)
|
| 1265 | 1270 | {
|
| 1266 | - ACQUIRE_LOCK(&eventBufMutex);
|
|
| 1271 | + ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 1267 | 1272 | ensureRoomForEvent(&eventBuf, EVENT_HEAP_PROF_SAMPLE_BEGIN);
|
| 1268 | 1273 | postEventHeader(&eventBuf, EVENT_HEAP_PROF_SAMPLE_BEGIN);
|
| 1269 | 1274 | postWord64(&eventBuf, era);
|
| 1270 | - RELEASE_LOCK(&eventBufMutex);
|
|
| 1275 | + RELEASE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 1271 | 1276 | }
|
| 1272 | 1277 | |
| 1273 | 1278 | |
| 1274 | 1279 | void postHeapBioProfSampleBegin(StgInt era, StgWord64 time)
|
| 1275 | 1280 | {
|
| 1276 | - ACQUIRE_LOCK(&eventBufMutex);
|
|
| 1281 | + ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 1277 | 1282 | ensureRoomForEvent(&eventBuf, EVENT_HEAP_BIO_PROF_SAMPLE_BEGIN);
|
| 1278 | 1283 | postEventHeader(&eventBuf, EVENT_HEAP_BIO_PROF_SAMPLE_BEGIN);
|
| 1279 | 1284 | postWord64(&eventBuf, era);
|
| 1280 | 1285 | postWord64(&eventBuf, time);
|
| 1281 | - RELEASE_LOCK(&eventBufMutex);
|
|
| 1286 | + RELEASE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 1282 | 1287 | }
|
| 1283 | 1288 | |
| 1284 | 1289 | void postHeapProfSampleEnd(StgInt era)
|
| 1285 | 1290 | {
|
| 1286 | - ACQUIRE_LOCK(&eventBufMutex);
|
|
| 1291 | + ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 1287 | 1292 | ensureRoomForEvent(&eventBuf, EVENT_HEAP_PROF_SAMPLE_END);
|
| 1288 | 1293 | postEventHeader(&eventBuf, EVENT_HEAP_PROF_SAMPLE_END);
|
| 1289 | 1294 | postWord64(&eventBuf, era);
|
| 1290 | - RELEASE_LOCK(&eventBufMutex);
|
|
| 1295 | + RELEASE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 1291 | 1296 | }
|
| 1292 | 1297 | |
| 1293 | 1298 | void postHeapProfSampleString(const char *label,
|
| 1294 | 1299 | StgWord64 residency)
|
| 1295 | 1300 | {
|
| 1296 | - ACQUIRE_LOCK(&eventBufMutex);
|
|
| 1301 | + ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 1297 | 1302 | StgWord label_len = strlen(label);
|
| 1298 | 1303 | StgWord len = 1+8+label_len+1;
|
| 1299 | 1304 | CHECK(!ensureRoomForVariableEvent(&eventBuf, len));
|
| ... | ... | @@ -1303,7 +1308,7 @@ void postHeapProfSampleString(const char *label, |
| 1303 | 1308 | postWord8(&eventBuf, 0);
|
| 1304 | 1309 | postWord64(&eventBuf, residency);
|
| 1305 | 1310 | postStringLen(&eventBuf, label, label_len);
|
| 1306 | - RELEASE_LOCK(&eventBufMutex);
|
|
| 1311 | + RELEASE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 1307 | 1312 | }
|
| 1308 | 1313 | |
| 1309 | 1314 | #if defined(PROFILING)
|
| ... | ... | @@ -1313,7 +1318,7 @@ void postHeapProfCostCentre(StgWord32 ccID, |
| 1313 | 1318 | const char *srcloc,
|
| 1314 | 1319 | StgBool is_caf)
|
| 1315 | 1320 | {
|
| 1316 | - ACQUIRE_LOCK(&eventBufMutex);
|
|
| 1321 | + ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 1317 | 1322 | StgWord label_len = strlen(label);
|
| 1318 | 1323 | StgWord module_len = strlen(module);
|
| 1319 | 1324 | StgWord srcloc_len = strlen(srcloc);
|
| ... | ... | @@ -1326,13 +1331,13 @@ void postHeapProfCostCentre(StgWord32 ccID, |
| 1326 | 1331 | postStringLen(&eventBuf, module, module_len);
|
| 1327 | 1332 | postStringLen(&eventBuf, srcloc, srcloc_len);
|
| 1328 | 1333 | postWord8(&eventBuf, is_caf);
|
| 1329 | - RELEASE_LOCK(&eventBufMutex);
|
|
| 1334 | + RELEASE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 1330 | 1335 | }
|
| 1331 | 1336 | |
| 1332 | 1337 | void postHeapProfSampleCostCentre(CostCentreStack *stack,
|
| 1333 | 1338 | StgWord64 residency)
|
| 1334 | 1339 | {
|
| 1335 | - ACQUIRE_LOCK(&eventBufMutex);
|
|
| 1340 | + ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 1336 | 1341 | StgWord depth = 0;
|
| 1337 | 1342 | CostCentreStack *ccs;
|
| 1338 | 1343 | for (ccs = stack; ccs != NULL && ccs != CCS_MAIN; ccs = ccs->prevStack)
|
| ... | ... | @@ -1351,7 +1356,7 @@ void postHeapProfSampleCostCentre(CostCentreStack *stack, |
| 1351 | 1356 | depth>0 && ccs != NULL && ccs != CCS_MAIN;
|
| 1352 | 1357 | ccs = ccs->prevStack, depth--)
|
| 1353 | 1358 | postWord32(&eventBuf, ccs->cc->ccID);
|
| 1354 | - RELEASE_LOCK(&eventBufMutex);
|
|
| 1359 | + RELEASE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 1355 | 1360 | }
|
| 1356 | 1361 | |
| 1357 | 1362 | |
| ... | ... | @@ -1359,7 +1364,7 @@ void postProfSampleCostCentre(Capability *cap, |
| 1359 | 1364 | CostCentreStack *stack,
|
| 1360 | 1365 | StgWord64 tick)
|
| 1361 | 1366 | {
|
| 1362 | - ACQUIRE_LOCK(&eventBufMutex);
|
|
| 1367 | + ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 1363 | 1368 | StgWord depth = 0;
|
| 1364 | 1369 | CostCentreStack *ccs;
|
| 1365 | 1370 | for (ccs = stack; ccs != NULL && ccs != CCS_MAIN; ccs = ccs->prevStack)
|
| ... | ... | @@ -1377,7 +1382,7 @@ void postProfSampleCostCentre(Capability *cap, |
| 1377 | 1382 | depth>0 && ccs != NULL && ccs != CCS_MAIN;
|
| 1378 | 1383 | ccs = ccs->prevStack, depth--)
|
| 1379 | 1384 | postWord32(&eventBuf, ccs->cc->ccID);
|
| 1380 | - RELEASE_LOCK(&eventBufMutex);
|
|
| 1385 | + RELEASE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 1381 | 1386 | }
|
| 1382 | 1387 | |
| 1383 | 1388 | // This event is output at the start of profiling so the tick interval can
|
| ... | ... | @@ -1385,11 +1390,11 @@ void postProfSampleCostCentre(Capability *cap, |
| 1385 | 1390 | // can be calculated from how many samples there are.
|
| 1386 | 1391 | void postProfBegin(void)
|
| 1387 | 1392 | {
|
| 1388 | - ACQUIRE_LOCK(&eventBufMutex);
|
|
| 1393 | + ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 1389 | 1394 | postEventHeader(&eventBuf, EVENT_PROF_BEGIN);
|
| 1390 | 1395 | // The interval that each tick was sampled, in nanoseconds
|
| 1391 | 1396 | postWord64(&eventBuf, TimeToNS(RtsFlags.MiscFlags.tickInterval));
|
| 1392 | - RELEASE_LOCK(&eventBufMutex);
|
|
| 1397 | + RELEASE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 1393 | 1398 | }
|
| 1394 | 1399 | #endif /* PROFILING */
|
| 1395 | 1400 | |
| ... | ... | @@ -1415,11 +1420,11 @@ static void postTickyCounterDef(EventsBuf *eb, StgEntCounter *p) |
| 1415 | 1420 | |
| 1416 | 1421 | void postTickyCounterDefs(StgEntCounter *counters)
|
| 1417 | 1422 | {
|
| 1418 | - ACQUIRE_LOCK(&eventBufMutex);
|
|
| 1423 | + ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 1419 | 1424 | for (StgEntCounter *p = counters; p != NULL; p = p->link) {
|
| 1420 | 1425 | postTickyCounterDef(&eventBuf, p);
|
| 1421 | 1426 | }
|
| 1422 | - RELEASE_LOCK(&eventBufMutex);
|
|
| 1427 | + RELEASE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 1423 | 1428 | }
|
| 1424 | 1429 | |
| 1425 | 1430 | static void postTickyCounterSample(EventsBuf *eb, StgEntCounter *p)
|
| ... | ... | @@ -1443,13 +1448,13 @@ static void postTickyCounterSample(EventsBuf *eb, StgEntCounter *p) |
| 1443 | 1448 | |
| 1444 | 1449 | void postTickyCounterSamples(StgEntCounter *counters)
|
| 1445 | 1450 | {
|
| 1446 | - ACQUIRE_LOCK(&eventBufMutex);
|
|
| 1451 | + ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 1447 | 1452 | ensureRoomForEvent(&eventBuf, EVENT_TICKY_COUNTER_SAMPLE);
|
| 1448 | 1453 | postEventHeader(&eventBuf, EVENT_TICKY_COUNTER_BEGIN_SAMPLE);
|
| 1449 | 1454 | for (StgEntCounter *p = counters; p != NULL; p = p->link) {
|
| 1450 | 1455 | postTickyCounterSample(&eventBuf, p);
|
| 1451 | 1456 | }
|
| 1452 | - RELEASE_LOCK(&eventBufMutex);
|
|
| 1457 | + RELEASE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 1453 | 1458 | }
|
| 1454 | 1459 | #endif /* TICKY_TICKY */
|
| 1455 | 1460 | void postIPE(const InfoProvEnt *ipe)
|
| ... | ... | @@ -1459,7 +1464,7 @@ void postIPE(const InfoProvEnt *ipe) |
| 1459 | 1464 | |
| 1460 | 1465 | // See Note [Maximum event length].
|
| 1461 | 1466 | const StgWord MAX_IPE_STRING_LEN = 65535;
|
| 1462 | - ACQUIRE_LOCK(&eventBufMutex);
|
|
| 1467 | + ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 1463 | 1468 | StgWord table_name_len = MIN(strlen(ipe->prov.table_name), MAX_IPE_STRING_LEN);
|
| 1464 | 1469 | StgWord closure_desc_len = MIN(strlen(closure_desc_buf), MAX_IPE_STRING_LEN);
|
| 1465 | 1470 | StgWord ty_desc_len = MIN(strlen(ipe->prov.ty_desc), MAX_IPE_STRING_LEN);
|
| ... | ... | @@ -1489,7 +1494,7 @@ void postIPE(const InfoProvEnt *ipe) |
| 1489 | 1494 | postBuf(&eventBuf, &colon, 1);
|
| 1490 | 1495 | postStringLen(&eventBuf, ipe->prov.src_span, src_span_len);
|
| 1491 | 1496 | |
| 1492 | - RELEASE_LOCK(&eventBufMutex);
|
|
| 1497 | + RELEASE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 1493 | 1498 | }
|
| 1494 | 1499 | |
| 1495 | 1500 | void printAndClearEventBuf (EventsBuf *ebuf)
|
| ... | ... | @@ -1601,14 +1606,21 @@ void flushLocalEventsBuf(Capability *cap) |
| 1601 | 1606 | // Flush all capabilities' event buffers when we already hold all capabilities.
|
| 1602 | 1607 | // Used during forkProcess.
|
| 1603 | 1608 | void flushAllCapsEventsBufs(void)
|
| 1609 | +{
|
|
| 1610 | + ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 1611 | + flushAllCapsEventsBufs_();
|
|
| 1612 | + RELEASE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 1613 | +}
|
|
| 1614 | + |
|
| 1615 | +// Unsafe version that does not acquire/release eventBufMutex. You must
|
|
| 1616 | +// hold the eventBufMutex, which you must acquire with ACQUIRE_LOCK_ALWAYS!
|
|
| 1617 | +void flushAllCapsEventsBufs_(void)
|
|
| 1604 | 1618 | {
|
| 1605 | 1619 | if (!event_log_writer) {
|
| 1606 | 1620 | return;
|
| 1607 | 1621 | }
|
| 1608 | 1622 | |
| 1609 | - ACQUIRE_LOCK(&eventBufMutex);
|
|
| 1610 | 1623 | printAndClearEventBuf(&eventBuf);
|
| 1611 | - RELEASE_LOCK(&eventBufMutex);
|
|
| 1612 | 1624 | |
| 1613 | 1625 | for (unsigned int i=0; i < getNumCapabilities(); i++) {
|
| 1614 | 1626 | flushLocalEventsBuf(getCapability(i));
|
| ... | ... | @@ -1641,9 +1653,9 @@ static void flushEventLog_(Capability **cap USED_IF_THREADS) |
| 1641 | 1653 | return;
|
| 1642 | 1654 | }
|
| 1643 | 1655 | |
| 1644 | - ACQUIRE_LOCK(&eventBufMutex);
|
|
| 1656 | + ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 1645 | 1657 | printAndClearEventBuf(&eventBuf);
|
| 1646 | - RELEASE_LOCK(&eventBufMutex);
|
|
| 1658 | + RELEASE_LOCK_ALWAYS(&eventBufMutex);
|
|
| 1647 | 1659 | |
| 1648 | 1660 | #if defined(THREADED_RTS)
|
| 1649 | 1661 | Task *task = newBoundTask();
|
| ... | ... | @@ -18,6 +18,13 @@ |
| 18 | 18 | #if defined(TRACING)
|
| 19 | 19 | |
| 20 | 20 | extern bool eventlog_enabled;
|
| 21 | +#if defined(HAVE_PREEMPTION)
|
|
| 22 | +// Avoid using this mutex directly if at all possible. It is needed in the
|
|
| 23 | +// implementation of forkProcess.
|
|
| 24 | +//
|
|
| 25 | +// All uses of this mutex must use ACQUIRE_LOCK_ALWAYS/RELEASE_LOCK_ALWAYS.
|
|
| 26 | +extern Mutex eventBufMutex;
|
|
| 27 | +#endif
|
|
| 21 | 28 | |
| 22 | 29 | void initEventLogging(void);
|
| 23 | 30 | void restartEventLogging(void);
|
| ... | ... | @@ -27,6 +34,7 @@ void abortEventLogging(void); // #4512 - after fork child needs to abort |
| 27 | 34 | void moreCapEventBufs (uint32_t from, uint32_t to);
|
| 28 | 35 | void flushLocalEventsBuf(Capability *cap);
|
| 29 | 36 | void flushAllCapsEventsBufs(void);
|
| 37 | +void flushAllCapsEventsBufs_(void);
|
|
| 30 | 38 | void flushAllEventsBufs(Capability *cap);
|
| 31 | 39 | |
| 32 | 40 | typedef void (*EventlogInitPost)(void);
|
| ... | ... | @@ -14,6 +14,46 @@ |
| 14 | 14 | |
| 15 | 15 | #pragma once
|
| 16 | 16 | |
| 17 | +/* Note [Threads and preemption]
|
|
| 18 | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
| 19 | + All full-fat OSs that GHC works on have OS threads, and we use them even in
|
|
| 20 | + the non-threaded RTS for a few features:
|
|
| 21 | + * Haskell thread preemption;
|
|
| 22 | + * sample-based profiling;
|
|
| 23 | + * idle GC;
|
|
| 24 | + * periodic eventlog flushing.
|
|
| 25 | + |
|
| 26 | + We use defined(HAVE_PREEMPTION) to decide if these features are implemented
|
|
| 27 | + via OS threads.
|
|
| 28 | + |
|
| 29 | + On platforms like WASM/js we do not have OS threads in any conventional
|
|
| 30 | + sense, and the features above are either not available or are implemented
|
|
| 31 | + differently. See Note [No timer on wasm32].
|
|
| 32 | + |
|
| 33 | + In future if GHC is ported to platforms like bare-metal micro-controllers,
|
|
| 34 | + RTOSs or to run directly under hypervisors then such platforms may also not
|
|
| 35 | + have threads available and they should not define HAVE_PREEMPTION here. Or
|
|
| 36 | + for some micro-controller RTOSs like Zeypher one may have a choice about
|
|
| 37 | + whether to use threads or not (at a size cost). Here would be the right
|
|
| 38 | + place to control whether the feature list above is supported.
|
|
| 39 | + */
|
|
| 40 | +#if defined(wasm32_HOST_ARCH)
|
|
| 41 | + // See Note [No timer on wasm32]
|
|
| 42 | + // To confuse matters, WASM _does_ have pthread.h but it doesnt work.
|
|
| 43 | +#elif defined(HAVE_PTHREAD_H) || defined(HAVE_WINDOWS_H)
|
|
| 44 | +#define HAVE_PREEMPTION
|
|
| 45 | +#else
|
|
| 46 | +#error Decide if this platform has threads and pre-emption or not.
|
|
| 47 | +#endif
|
|
| 48 | +// And JS does all of this differently, without using this bit of the RTS.
|
|
| 49 | + |
|
| 50 | +// Configuration sanity check
|
|
| 51 | +#if defined(THREADED_RTS) && !defined(HAVE_PREEMPTION)
|
|
| 52 | +//TODO we would like to be able to assert this:
|
|
| 53 | +// #error Configuration error: THREADED_RTS should imply HAVE_PREEMPTION
|
|
| 54 | +// however at the moment we cannot due to issue #27346.
|
|
| 55 | +#endif
|
|
| 56 | + |
|
| 17 | 57 | #if defined(HAVE_PTHREAD_H) && !defined(mingw32_HOST_OS)
|
| 18 | 58 | |
| 19 | 59 | #if defined(CMINUSMINUS)
|
| ... | ... | @@ -210,9 +250,29 @@ extern bool timedWaitCondition ( Condition* pCond, Mutex* pMut, Time timeout) |
| 210 | 250 | //
|
| 211 | 251 | // Mutexes
|
| 212 | 252 | //
|
| 253 | +// Even in the non-threaded RTS we use threads and mutexes! In particular the
|
|
| 254 | +// timer/ticker is implemented using a thread. And using threads needs locks.
|
|
| 255 | +// In particular we need locks for the data shared between the timer/ticker
|
|
| 256 | +// thread and the thread running the main capability.
|
|
| 257 | +#if defined(HAVE_PREEMPTION)
|
|
| 213 | 258 | extern void initMutex ( Mutex* pMut );
|
| 214 | 259 | extern void closeMutex ( Mutex* pMut );
|
| 215 | 260 | |
| 261 | +// The "always" variants do locking in the threaded and non-threaded RTS.
|
|
| 262 | +// The normal variants below are no-ops in the non-threaded RTS.
|
|
| 263 | +#define ACQUIRE_LOCK_ALWAYS(l) OS_ACQUIRE_LOCK(l)
|
|
| 264 | +#define TRY_ACQUIRE_LOCK_ALWAYS(l) OS_TRY_ACQUIRE_LOCK(l)
|
|
| 265 | +#define RELEASE_LOCK_ALWAYS(l) OS_RELEASE_LOCK(l)
|
|
| 266 | +#define ASSERT_LOCK_HELD_ALWAYS(l) OS_ASSERT_LOCK_HELD(l)
|
|
| 267 | +#else
|
|
| 268 | +// And just to be a bit confusing, the always variants are still no-ops when we
|
|
| 269 | +// do not HAVE_PREEMPTION, since then we don't have threads or mutexes at all.
|
|
| 270 | +#define ACQUIRE_LOCK_ALWAYS(l)
|
|
| 271 | +#define TRY_ACQUIRE_LOCK_ALWAYS(l) 0
|
|
| 272 | +#define RELEASE_LOCK_ALWAYS(l)
|
|
| 273 | +#define ASSERT_LOCK_HELD_ALWAYS(l)
|
|
| 274 | +#endif
|
|
| 275 | + |
|
| 216 | 276 | // Processors and affinity
|
| 217 | 277 | void setThreadAffinity (uint32_t n, uint32_t m);
|
| 218 | 278 | void setThreadNode (uint32_t node);
|
| ... | ... | @@ -228,6 +288,7 @@ void releaseThreadNode (void); |
| 228 | 288 | |
| 229 | 289 | #else
|
| 230 | 290 | |
| 291 | +// No-ops in the non-threaded RTS. See also the _ALWAYS variants above.
|
|
| 231 | 292 | #define ACQUIRE_LOCK(l)
|
| 232 | 293 | #define TRY_ACQUIRE_LOCK(l) 0
|
| 233 | 294 | #define RELEASE_LOCK(l)
|
| 1 | +{-# LANGUAGE MagicHash #-}
|
|
| 2 | +{-# LANGUAGE ForeignFunctionInterface, GHCForeignImportPrim, UnliftedFFITypes #-}
|
|
| 3 | + |
|
| 4 | +module Main where
|
|
| 5 | + |
|
| 6 | +import Control.Monad
|
|
| 7 | + ( unless )
|
|
| 8 | +import Data.Bits
|
|
| 9 | + ( shiftL )
|
|
| 10 | +import GHC.Exts
|
|
| 11 | + ( Int64# )
|
|
| 12 | +import GHC.Int
|
|
| 13 | + ( Int64(..) )
|
|
| 14 | + |
|
| 15 | +foreign import prim "test_mul2_clobber"
|
|
| 16 | + test_mul2_clobber :: Int64# -> Int64# -> Int64#
|
|
| 17 | + |
|
| 18 | +main :: IO ()
|
|
| 19 | +main = do
|
|
| 20 | + let
|
|
| 21 | + I64# x = 1 `shiftL` 32
|
|
| 22 | + hi = I64# $ test_mul2_clobber x x
|
|
| 23 | + |
|
| 24 | + unless ( hi == 1 ) $
|
|
| 25 | + error $ unlines
|
|
| 26 | + [ "Incorrect result for Mul2 operation."
|
|
| 27 | + , "Expected high word: 1"
|
|
| 28 | + , " Actual high word: " ++ show hi
|
|
| 29 | + ] |
| 1 | +#include "Cmm.h"
|
|
| 2 | + |
|
| 3 | +// Test for #27046
|
|
| 4 | +test_mul2_clobber (bits64 x, bits64 y)
|
|
| 5 | +{
|
|
| 6 | + bits64 hi, nd;
|
|
| 7 | + |
|
| 8 | + // Deliberately alias the destination 'lo' with the source 'x'
|
|
| 9 | + // This forces the NCG to use the same virtual register for both.
|
|
| 10 | + (nd, hi, x) = prim %mul2_64(x, y);
|
|
| 11 | + |
|
| 12 | + return (hi);
|
|
| 13 | +} |
| ... | ... | @@ -260,6 +260,13 @@ test('T25364', normal, compile_and_run, ['']) |
| 260 | 260 | test('T26061', normal, compile_and_run, [''])
|
| 261 | 261 | test('T26537', normal, compile_and_run, ['-O2 -fregs-graph'])
|
| 262 | 262 | test('T24016', normal, compile_and_run, ['-O1 -fPIC'])
|
| 263 | +test('T27046',
|
|
| 264 | + [ req_cmm
|
|
| 265 | + , when(arch('i386'), skip) # i386 does not support MO_S_Mul2 W64
|
|
| 266 | + , when(arch('wasm32'), skip)
|
|
| 267 | + , js_skip
|
|
| 268 | + , when(unregisterised(), skip) # pprCallishMachOp_for_C: MO_S_Mul2 W64 not supported
|
|
| 269 | + ], compile_and_run, ['T27046_cmm.cmm'])
|
|
| 263 | 270 | |
| 264 | 271 | # Check that GHC-generated finalizers run on Darwin. The Apple linker doesn't
|
| 265 | 272 | # support --wrap, so we can't intercept hs_spt_remove directly. Instead we
|
| ... | ... | @@ -30,16 +30,22 @@ foreign import ccall unsafe "has_local_stop_after_return" |
| 30 | 30 | main :: IO ()
|
| 31 | 31 | main = do
|
| 32 | 32 | setNumCapabilities 2
|
| 33 | - checkFlag
|
|
| 34 | - "TSO_STOP_NEXT_BREAKPOINT"
|
|
| 35 | - rts_enableStopNextBreakpoint
|
|
| 36 | - rts_disableStopNextBreakpoint
|
|
| 37 | - c_hasLocalStopNextBreakpoint
|
|
| 38 | - checkFlag
|
|
| 39 | - "TSO_STOP_AFTER_RETURN"
|
|
| 40 | - rts_enableStopAfterReturn
|
|
| 41 | - rts_disableStopAfterReturn
|
|
| 42 | - c_hasLocalStopAfterReturn
|
|
| 33 | + -- Bind to capability 0 so it can't float between capabilities while the
|
|
| 34 | + -- target thread runs on capability 1.
|
|
| 35 | + doneVar <- newEmptyMVar
|
|
| 36 | + _ <- forkOn 0 $ do
|
|
| 37 | + checkFlag
|
|
| 38 | + "TSO_STOP_NEXT_BREAKPOINT"
|
|
| 39 | + rts_enableStopNextBreakpoint
|
|
| 40 | + rts_disableStopNextBreakpoint
|
|
| 41 | + c_hasLocalStopNextBreakpoint
|
|
| 42 | + checkFlag
|
|
| 43 | + "TSO_STOP_AFTER_RETURN"
|
|
| 44 | + rts_enableStopAfterReturn
|
|
| 45 | + rts_disableStopAfterReturn
|
|
| 46 | + c_hasLocalStopAfterReturn
|
|
| 47 | + putMVar doneVar ()
|
|
| 48 | + takeMVar doneVar
|
|
| 43 | 49 | |
| 44 | 50 | checkFlag
|
| 45 | 51 | :: String
|
| ... | ... | @@ -58,6 +64,7 @@ checkFlag label enable disable isMyThreadFlagSet = do |
| 58 | 64 | ThreadId tid# <- forkOn 1 $ do
|
| 59 | 65 | replicateM_ 2 $ do
|
| 60 | 66 | replyVar <- takeMVar targetCheckVar
|
| 67 | + yield -- make sure we reprocess the mailbox
|
|
| 61 | 68 | isSet <- (/= 0) <$> isMyThreadFlagSet
|
| 62 | 69 | putMVar replyVar isSet
|
| 63 | 70 |
| 1 | -(0,False)
|
|
| 1 | +(0,True)
|
|
| 2 | 2 | TSO_STOP_NEXT_BREAKPOINT set: ok
|
| 3 | 3 | TSO_STOP_NEXT_BREAKPOINT unset: ok
|
| 4 | -(0,False)
|
|
| 4 | +(0,True)
|
|
| 5 | 5 | TSO_STOP_AFTER_RETURN set: ok
|
| 6 | 6 | TSO_STOP_AFTER_RETURN unset: ok |