Teo Camarasu pushed to branch wip/T22859 at Glasgow Haskell Compiler / GHC

Commits:

24 changed files:

Changes:

  • compiler/GHC/Builtin/primops.txt.pp
    ... ... @@ -4065,6 +4065,15 @@ primop SetThreadAllocationCounter "setThreadAllocationCounter#" GenPrimOp
    4065 4065
        effect = ReadWriteEffect
    
    4066 4066
        out_of_line      = True
    
    4067 4067
     
    
    4068
    +primop  SetOtherThreadAllocationCounter "setOtherThreadAllocationCounter#" GenPrimOp
    
    4069
    +   Int64# -> ThreadId# -> State# RealWorld -> State# RealWorld
    
    4070
    +   { Sets the allocation counter for the another thread to the given value.
    
    4071
    +     This doesn't take allocations into the current nursery chunk into account.
    
    4072
    +     Therefore it is only accurate if the other thread is not currently running. }
    
    4073
    +   with
    
    4074
    +   effect = ReadWriteEffect
    
    4075
    +   out_of_line      = True
    
    4076
    +
    
    4068 4077
     primtype StackSnapshot#
    
    4069 4078
        { Haskell representation of a @StgStack*@ that was created (cloned)
    
    4070 4079
          with a function in "GHC.Stack.CloneStack". Please check the
    

  • compiler/GHC/StgToCmm/Prim.hs
    ... ... @@ -1775,6 +1775,7 @@ emitPrimOp cfg primop =
    1775 1775
       TraceEventBinaryOp -> alwaysExternal
    
    1776 1776
       TraceMarkerOp -> alwaysExternal
    
    1777 1777
       SetThreadAllocationCounter -> alwaysExternal
    
    1778
    +  SetOtherThreadAllocationCounter -> alwaysExternal
    
    1778 1779
       KeepAliveOp -> alwaysExternal
    
    1779 1780
     
    
    1780 1781
      where
    

  • compiler/GHC/StgToJS/Prim.hs
    ... ... @@ -1173,6 +1173,7 @@ genPrim prof bound ty op = case op of
    1173 1173
       WhereFromOp                       -> unhandledPrimop op -- should be easily implementable with o.f.n
    
    1174 1174
     
    
    1175 1175
       SetThreadAllocationCounter        -> unhandledPrimop op
    
    1176
    +  SetOtherThreadAllocationCounter   -> unhandledPrimop op
    
    1176 1177
     
    
    1177 1178
     ------------------------------- Vector -----------------------------------------
    
    1178 1179
     -- For now, vectors are unsupported on the JS backend. Simply put, they do not
    

  • libraries/ghc-experimental/ghc-experimental.cabal.in
    ... ... @@ -38,6 +38,7 @@ library
    38 38
           GHC.RTS.Flags.Experimental
    
    39 39
           GHC.Stats.Experimental
    
    40 40
           Prelude.Experimental
    
    41
    +      System.Mem.Experimental
    
    41 42
         if arch(wasm32)
    
    42 43
             exposed-modules:  GHC.Wasm.Prim
    
    43 44
         other-extensions:
    

  • libraries/ghc-experimental/src/System/Mem/Experimental.hs
    1
    +module System.Mem.Experimental
    
    2
    +  ( setGlobalAllocationLimitHandler
    
    3
    +  , AllocationLimitKillBehaviour(..)
    
    4
    +  , getAllocationCounterFor
    
    5
    +  , setAllocationCounterFor
    
    6
    +  , enableAllocationLimitFor
    
    7
    +  , disableAllocationLimitFor
    
    8
    +  )
    
    9
    +  where
    
    10
    +import GHC.Internal.AllocationLimitHandler

  • libraries/ghc-internal/ghc-internal.cabal.in
    ... ... @@ -122,6 +122,7 @@ Library
    122 122
             rts == 1.0.*
    
    123 123
     
    
    124 124
         exposed-modules:
    
    125
    +        GHC.Internal.AllocationLimitHandler
    
    125 126
             GHC.Internal.ClosureTypes
    
    126 127
             GHC.Internal.Control.Arrow
    
    127 128
             GHC.Internal.Control.Category
    

  • libraries/ghc-internal/src/GHC/Internal/AllocationLimitHandler.hs
    1
    +{-# LANGUAGE MagicHash #-}
    
    2
    +{-# LANGUAGE UnboxedTuples #-}
    
    3
    +{-# LANGUAGE UnliftedFFITypes #-}
    
    4
    +{-# LANGUAGE GHCForeignImportPrim #-}
    
    5
    +{-# OPTIONS_HADDOCK not-home #-}
    
    6
    +module GHC.Internal.AllocationLimitHandler
    
    7
    +  ( runAllocationLimitHandler
    
    8
    +  , setGlobalAllocationLimitHandler
    
    9
    +  , AllocationLimitKillBehaviour(..)
    
    10
    +  , getAllocationCounterFor
    
    11
    +  , setAllocationCounterFor
    
    12
    +  , enableAllocationLimitFor
    
    13
    +  , disableAllocationLimitFor
    
    14
    +  )
    
    15
    +  where
    
    16
    +import GHC.Internal.Base
    
    17
    +import GHC.Internal.Conc.Sync (ThreadId(..))
    
    18
    +import GHC.Internal.Data.IORef (IORef, readIORef, writeIORef, newIORef)
    
    19
    +import GHC.Internal.Foreign.C.Types
    
    20
    +import GHC.Internal.IO (unsafePerformIO)
    
    21
    +import GHC.Internal.Int (Int64(..))
    
    22
    +
    
    23
    +
    
    24
    +{-# NOINLINE allocationLimitHandler #-}
    
    25
    +allocationLimitHandler :: IORef (ThreadId -> IO ())
    
    26
    +allocationLimitHandler = unsafePerformIO (newIORef defaultHandler)
    
    27
    +
    
    28
    +defaultHandler :: ThreadId -> IO ()
    
    29
    +defaultHandler _ = pure ()
    
    30
    +
    
    31
    +foreign import ccall "setAllocLimitKill" setAllocLimitKill :: CBool -> CBool -> IO ()
    
    32
    +
    
    33
    +runAllocationLimitHandler :: ThreadId# -> IO ()
    
    34
    +runAllocationLimitHandler tid = do
    
    35
    +  hook <- getAllocationLimitHandler
    
    36
    +  hook $ ThreadId tid
    
    37
    +
    
    38
    +getAllocationLimitHandler :: IO (ThreadId -> IO ())
    
    39
    +getAllocationLimitHandler = readIORef allocationLimitHandler
    
    40
    +
    
    41
    +data AllocationLimitKillBehaviour =
    
    42
    +  KillOnAllocationLimit
    
    43
    +  -- ^ Throw a @AllocationLimitExceeded@ async exception to the thread when the
    
    44
    +  -- allocation limit is exceeded.
    
    45
    +  | DontKillOnAllocationLimit
    
    46
    +  -- ^ Do not throw an exception when the allocation limit is exceeded.
    
    47
    +
    
    48
    +-- | Define the behaviour for handling allocation limits.
    
    49
    +-- By default we throw a @AllocationLimitExceeded@ async exception to the thread.
    
    50
    +-- This can be controlled using @AllocationLimitKillBehaviour@.
    
    51
    +--
    
    52
    +-- We can also run a user-specified handler, which can be done in addition to
    
    53
    +-- or in place of the exception.
    
    54
    +-- This allows for instance logging on the allocation limit being exceeded,
    
    55
    +-- or dynamically determining whether to terminate the thread.
    
    56
    +-- The handler is not guaranteed to run before the thread is terminated or restarted.
    
    57
    +--
    
    58
    +-- Note: that if you don't terminate the thread, then the allocation limit gets
    
    59
    +-- removed.
    
    60
    +-- If you wish to keep the allocation limit you will have to reset it using
    
    61
    +-- @setAllocationCounter@ and @enableAllocationLimit@.
    
    62
    +setGlobalAllocationLimitHandler :: AllocationLimitKillBehaviour -> Maybe (ThreadId -> IO ()) -> IO ()
    
    63
    +setGlobalAllocationLimitHandler killBehaviour mHandler = do
    
    64
    +  shouldRunHandler <- case mHandler of
    
    65
    +    Just hook -> do
    
    66
    +      writeIORef allocationLimitHandler hook
    
    67
    +      pure 1
    
    68
    +    Nothing -> do
    
    69
    +      writeIORef allocationLimitHandler defaultHandler
    
    70
    +      pure 0
    
    71
    +  let shouldKill =
    
    72
    +        case killBehaviour of
    
    73
    +          KillOnAllocationLimit -> 1
    
    74
    +          DontKillOnAllocationLimit -> 0
    
    75
    +  setAllocLimitKill shouldKill shouldRunHandler
    
    76
    +
    
    77
    +-- | Retrieves the allocation counter for the another thread.
    
    78
    +foreign import prim "stg_getOtherThreadAllocationCounterzh" getOtherThreadAllocationCounter#
    
    79
    +  :: ThreadId#
    
    80
    +  -> State# RealWorld
    
    81
    +  -> (# State# RealWorld, Int64# #)
    
    82
    +
    
    83
    +-- | Get the allocation counter for a different thread.
    
    84
    +-- Note this doesn't take the current nursery chunk into account.
    
    85
    +-- If the thread is running then it may underestimate allocations by the size of a nursery thread.
    
    86
    +getAllocationCounterFor :: ThreadId -> IO Int64
    
    87
    +getAllocationCounterFor (ThreadId t#) = IO $ \s ->
    
    88
    +  case getOtherThreadAllocationCounter# t# s of (# s', i# #)  -> (# s', I64# i# #)
    
    89
    +
    
    90
    +-- | Set the allocation counter for a different thread.
    
    91
    +-- This can be combined with 'enableAllocationLimitFor' to enable allocation limits for another thread.
    
    92
    +-- Note this doesn't take the current nursery chunk into account.
    
    93
    +-- If the thread is running then it may overestimate allocations by the size of a nursery thread,
    
    94
    +-- and trigger the limit sooner than expected.
    
    95
    +setAllocationCounterFor :: Int64 -> ThreadId -> IO ()
    
    96
    +setAllocationCounterFor (I64# i#) (ThreadId t#) = IO $ \s ->
    
    97
    +  case setOtherThreadAllocationCounter# i# t# s of s' -> (# s', () #)
    
    98
    +
    
    99
    +
    
    100
    +-- | Enable allocation limit processing the thread @t@.
    
    101
    +enableAllocationLimitFor :: ThreadId -> IO ()
    
    102
    +enableAllocationLimitFor (ThreadId t) = do
    
    103
    +  rts_enableThreadAllocationLimit t
    
    104
    +
    
    105
    +-- | Disable allocation limit processing the thread @t@.
    
    106
    +disableAllocationLimitFor :: ThreadId -> IO ()
    
    107
    +disableAllocationLimitFor (ThreadId t) = do
    
    108
    +  rts_disableThreadAllocationLimit t
    
    109
    +
    
    110
    +foreign import ccall unsafe "rts_enableThreadAllocationLimit"
    
    111
    +  rts_enableThreadAllocationLimit :: ThreadId# -> IO ()
    
    112
    +
    
    113
    +foreign import ccall unsafe "rts_disableThreadAllocationLimit"
    
    114
    +  rts_disableThreadAllocationLimit :: ThreadId# -> IO ()

  • rts/Prelude.h
    ... ... @@ -67,6 +67,7 @@ PRELUDE_CLOSURE(ghczminternal_GHCziInternalziEventziWindows_processRemoteComplet
    67 67
     
    
    68 68
     PRELUDE_CLOSURE(ghczminternal_GHCziInternalziTopHandler_flushStdHandles_closure);
    
    69 69
     PRELUDE_CLOSURE(ghczminternal_GHCziInternalziTopHandler_runMainIO_closure);
    
    70
    +PRELUDE_CLOSURE(ghczminternal_GHCziInternalziAllocationLimitHandler_runAllocationLimitHandler_closure);
    
    70 71
     
    
    71 72
     PRELUDE_INFO(ghczminternal_GHCziInternalziCString_unpackCStringzh_info);
    
    72 73
     PRELUDE_INFO(ghczminternal_GHCziInternalziTypes_Czh_con_info);
    
    ... ... @@ -102,6 +103,7 @@ PRELUDE_INFO(ghczminternal_GHCziInternalziStable_StablePtr_con_info);
    102 103
     #if defined(mingw32_HOST_OS)
    
    103 104
     #define processRemoteCompletion_closure DLL_IMPORT_DATA_REF(ghczminternal_GHCziInternalziEventziWindows_processRemoteCompletion_closure)
    
    104 105
     #endif
    
    106
    +#define runAllocationLimitHandler_closure DLL_IMPORT_DATA_REF(ghczminternal_GHCziInternalziAllocationLimitHandler_runAllocationLimitHandler_closure)
    
    105 107
     
    
    106 108
     #define flushStdHandles_closure   DLL_IMPORT_DATA_REF(ghczminternal_GHCziInternalziTopHandler_flushStdHandles_closure)
    
    107 109
     #define runMainIO_closure   DLL_IMPORT_DATA_REF(ghczminternal_GHCziInternalziTopHandler_runMainIO_closure)
    

  • rts/PrimOps.cmm
    ... ... @@ -2889,6 +2889,11 @@ stg_getThreadAllocationCounterzh ()
    2889 2889
         return (StgTSO_alloc_limit(CurrentTSO) - TO_I64(offset));
    
    2890 2890
     }
    
    2891 2891
     
    
    2892
    +stg_getOtherThreadAllocationCounterzh ( gcptr t )
    
    2893
    +{
    
    2894
    +    return (StgTSO_alloc_limit(t));
    
    2895
    +}
    
    2896
    +
    
    2892 2897
     stg_setThreadAllocationCounterzh ( I64 counter )
    
    2893 2898
     {
    
    2894 2899
         // Allocation in the current block will be subtracted by
    
    ... ... @@ -2901,6 +2906,12 @@ stg_setThreadAllocationCounterzh ( I64 counter )
    2901 2906
         return ();
    
    2902 2907
     }
    
    2903 2908
     
    
    2909
    +stg_setOtherThreadAllocationCounterzh ( I64 counter, gcptr t )
    
    2910
    +{
    
    2911
    +    StgTSO_alloc_limit(t) = counter;
    
    2912
    +    return ();
    
    2913
    +}
    
    2914
    +
    
    2904 2915
     
    
    2905 2916
     #define KEEP_ALIVE_FRAME_FIELDS(w_,p_,info_ptr,p1,p2,c)   \
    
    2906 2917
       w_ info_ptr,                                            \
    

  • rts/RtsStartup.c
    ... ... @@ -224,6 +224,7 @@ static void initBuiltinGcRoots(void)
    224 224
          * GHC.Core.Make.mkExceptionId.
    
    225 225
          */
    
    226 226
         getStablePtr((StgPtr)absentSumFieldError_closure);
    
    227
    +    getStablePtr((StgPtr)runAllocationLimitHandler_closure);
    
    227 228
     }
    
    228 229
     
    
    229 230
     void
    

  • rts/RtsSymbols.c
    ... ... @@ -916,7 +916,9 @@ extern char **environ;
    916 916
           SymI_HasDataProto(stg_traceMarkerzh)                                  \
    
    917 917
           SymI_HasDataProto(stg_traceBinaryEventzh)                             \
    
    918 918
           SymI_HasDataProto(stg_getThreadAllocationCounterzh)                   \
    
    919
    +      SymI_HasDataProto(stg_getOtherThreadAllocationCounterzh)              \
    
    919 920
           SymI_HasDataProto(stg_setThreadAllocationCounterzh)                   \
    
    921
    +      SymI_HasDataProto(stg_setOtherThreadAllocationCounterzh)              \
    
    920 922
           SymI_HasProto(getMonotonicNSec)                                   \
    
    921 923
           SymI_HasProto(lockFile)                                           \
    
    922 924
           SymI_HasProto(unlockFile)                                         \
    

  • rts/Schedule.c
    ... ... @@ -41,6 +41,7 @@
    41 41
     #include "Threads.h"
    
    42 42
     #include "Timer.h"
    
    43 43
     #include "ThreadPaused.h"
    
    44
    +#include "ThreadLabels.h"
    
    44 45
     #include "Messages.h"
    
    45 46
     #include "StablePtr.h"
    
    46 47
     #include "StableName.h"
    
    ... ... @@ -94,6 +95,10 @@ StgWord recent_activity = ACTIVITY_YES;
    94 95
      */
    
    95 96
     StgWord sched_state = SCHED_RUNNING;
    
    96 97
     
    
    98
    +
    
    99
    +bool allocLimitKill = true;
    
    100
    +bool allocLimitRunHook = false;
    
    101
    +
    
    97 102
     /*
    
    98 103
      * This mutex protects most of the global scheduler data in
    
    99 104
      * the THREADED_RTS runtime.
    
    ... ... @@ -1125,19 +1130,36 @@ schedulePostRunThread (Capability *cap, StgTSO *t)
    1125 1130
             }
    
    1126 1131
         }
    
    1127 1132
     
    
    1128
    -    //
    
    1129
    -    // If the current thread's allocation limit has run out, send it
    
    1130
    -    // the AllocationLimitExceeded exception.
    
    1133
    +    // Handle the current thread's allocation limit running out,
    
    1131 1134
     
    
    1132 1135
         if (PK_Int64((W_*)&(t->alloc_limit)) < 0 && (t->flags & TSO_ALLOC_LIMIT)) {
    
    1133
    -        // Use a throwToSelf rather than a throwToSingleThreaded, because
    
    1134
    -        // it correctly handles the case where the thread is currently
    
    1135
    -        // inside mask.  Also the thread might be blocked (e.g. on an
    
    1136
    -        // MVar), and throwToSingleThreaded doesn't unblock it
    
    1137
    -        // correctly in that case.
    
    1138
    -        throwToSelf(cap, t, allocationLimitExceeded_closure);
    
    1139
    -        ASSIGN_Int64((W_*)&(t->alloc_limit),
    
    1140
    -                     (StgInt64)RtsFlags.GcFlags.allocLimitGrace * BLOCK_SIZE);
    
    1136
    +        if(allocLimitKill) {
    
    1137
    +          // Throw the AllocationLimitExceeded exception.
    
    1138
    +          // Use a throwToSelf rather than a throwToSingleThreaded, because
    
    1139
    +          // it correctly handles the case where the thread is currently
    
    1140
    +          // inside mask.  Also the thread might be blocked (e.g. on an
    
    1141
    +          // MVar), and throwToSingleThreaded doesn't unblock it
    
    1142
    +          // correctly in that case.
    
    1143
    +          throwToSelf(cap, t, allocationLimitExceeded_closure);
    
    1144
    +          ASSIGN_Int64((W_*)&(t->alloc_limit),
    
    1145
    +                      (StgInt64)RtsFlags.GcFlags.allocLimitGrace * BLOCK_SIZE);
    
    1146
    +        } else {
    
    1147
    +          // If we aren't killing the thread, we must disable the limit
    
    1148
    +          // otherwise we will immediatelly retrigger it.
    
    1149
    +          // User defined handlers should re-enable it if wanted.
    
    1150
    +          t->flags = t->flags & ~TSO_ALLOC_LIMIT;
    
    1151
    +        }
    
    1152
    +
    
    1153
    +        if(allocLimitRunHook)
    
    1154
    +        {
    
    1155
    +          // Create a thread to run the allocation limit handler.
    
    1156
    +          StgClosure* c = rts_apply(cap, runAllocationLimitHandler_closure, (StgClosure*)t);
    
    1157
    +          StgTSO* hookThread = createIOThread(cap, RtsFlags.GcFlags.initialStkSize, c);
    
    1158
    +          setThreadLabel(cap, hookThread, "allocation limit handler thread");
    
    1159
    +          // Schedule the handler to be run immediatelly.
    
    1160
    +          pushOnRunQueue(cap, hookThread);
    
    1161
    +        }
    
    1162
    +
    
    1141 1163
         }
    
    1142 1164
     
    
    1143 1165
       /* some statistics gathering in the parallel case */
    
    ... ... @@ -3342,3 +3364,9 @@ resurrectThreads (StgTSO *threads)
    3342 3364
             }
    
    3343 3365
         }
    
    3344 3366
     }
    
    3367
    +
    
    3368
    +void setAllocLimitKill(bool shouldKill, bool shouldHook)
    
    3369
    +{
    
    3370
    +   allocLimitKill = shouldKill;
    
    3371
    +   allocLimitRunHook = shouldHook;
    
    3372
    +}

  • rts/external-symbols.list.in
    ... ... @@ -43,6 +43,7 @@ ghczminternal_GHCziInternalziTypes_Izh_con_info
    43 43
     ghczminternal_GHCziInternalziTypes_Fzh_con_info
    
    44 44
     ghczminternal_GHCziInternalziTypes_Dzh_con_info
    
    45 45
     ghczminternal_GHCziInternalziTypes_Wzh_con_info
    
    46
    +ghczminternal_GHCziInternalziAllocationLimitHandler_runAllocationLimitHandler_closure
    
    46 47
     ghczminternal_GHCziInternalziPtr_Ptr_con_info
    
    47 48
     ghczminternal_GHCziInternalziPtr_FunPtr_con_info
    
    48 49
     ghczminternal_GHCziInternalziInt_I8zh_con_info
    

  • rts/include/rts/storage/GC.h
    ... ... @@ -209,6 +209,10 @@ void flushExec(W_ len, AdjustorExecutable exec_addr);
    209 209
     // Used by GC checks in external .cmm code:
    
    210 210
     extern W_ large_alloc_lim;
    
    211 211
     
    
    212
    +// Should triggering an allocation limit kill the thread
    
    213
    +// and should we run a user-defined hook when it is triggered.
    
    214
    +void setAllocLimitKill(bool, bool);
    
    215
    +
    
    212 216
     /* -----------------------------------------------------------------------------
    
    213 217
        Performing Garbage Collection
    
    214 218
        -------------------------------------------------------------------------- */
    

  • rts/include/rts/storage/TSO.h
    ... ... @@ -157,9 +157,10 @@ typedef struct StgTSO_ {
    157 157
         /*
    
    158 158
          * The allocation limit for this thread, which is updated as the
    
    159 159
          * thread allocates.  If the value drops below zero, and
    
    160
    -     * TSO_ALLOC_LIMIT is set in flags, we raise an exception in the
    
    161
    -     * thread, and give the thread a little more space to handle the
    
    162
    -     * exception before we raise the exception again.
    
    160
    +     * TSO_ALLOC_LIMIT is set in flags, then a handler is triggerd.
    
    161
    +     * Either we raise an exception in the thread, and give the thread
    
    162
    +     * a little more space to handle the exception before we raise the
    
    163
    +     * exception again; or we run a user defined handler.
    
    163 164
          *
    
    164 165
          * This is an integer, because we might update it in a place where
    
    165 166
          * it isn't convenient to raise the exception, so we want it to
    

  • rts/include/stg/MiscClosures.h
    ... ... @@ -604,7 +604,9 @@ RTS_FUN_DECL(stg_traceEventzh);
    604 604
     RTS_FUN_DECL(stg_traceBinaryEventzh);
    
    605 605
     RTS_FUN_DECL(stg_traceMarkerzh);
    
    606 606
     RTS_FUN_DECL(stg_getThreadAllocationCounterzh);
    
    607
    +RTS_FUN_DECL(stg_getOtherThreadAllocationCounterzh);
    
    607 608
     RTS_FUN_DECL(stg_setThreadAllocationCounterzh);
    
    609
    +RTS_FUN_DECL(stg_setOtherThreadAllocationCounterzh);
    
    608 610
     
    
    609 611
     RTS_FUN_DECL(stg_castWord64ToDoublezh);
    
    610 612
     RTS_FUN_DECL(stg_castDoubleToWord64zh);
    

  • testsuite/tests/interface-stability/base-exports.stdout
    ... ... @@ -4607,6 +4607,7 @@ module GHC.Base where
    4607 4607
       sequence :: forall (m :: * -> *) a. Monad m => [m a] -> m [a]
    
    4608 4608
       setAddrRange# :: Addr# -> Int# -> Int# -> State# RealWorld -> State# RealWorld
    
    4609 4609
       setByteArray# :: forall d. MutableByteArray# d -> Int# -> Int# -> Int# -> State# d -> State# d
    
    4610
    +  setOtherThreadAllocationCounter# :: Int64# -> ThreadId# -> State# RealWorld -> State# RealWorld
    
    4610 4611
       setThreadAllocationCounter# :: Int64# -> State# RealWorld -> State# RealWorld
    
    4611 4612
       shiftL# :: Word# -> Int# -> Word#
    
    4612 4613
       shiftRL# :: Word# -> Int# -> Word#
    
    ... ... @@ -6693,6 +6694,7 @@ module GHC.Exts where
    6693 6694
       seq# :: forall a s. a -> State# s -> (# State# s, a #)
    
    6694 6695
       setAddrRange# :: Addr# -> Int# -> Int# -> State# RealWorld -> State# RealWorld
    
    6695 6696
       setByteArray# :: forall d. MutableByteArray# d -> Int# -> Int# -> Int# -> State# d -> State# d
    
    6697
    +  setOtherThreadAllocationCounter# :: Int64# -> ThreadId# -> State# RealWorld -> State# RealWorld
    
    6696 6698
       setThreadAllocationCounter# :: Int64# -> State# RealWorld -> State# RealWorld
    
    6697 6699
       shiftL# :: Word# -> Int# -> Word#
    
    6698 6700
       shiftRL# :: Word# -> Int# -> Word#
    

  • testsuite/tests/interface-stability/base-exports.stdout-javascript-unknown-ghcjs
    ... ... @@ -4607,6 +4607,7 @@ module GHC.Base where
    4607 4607
       sequence :: forall (m :: * -> *) a. Monad m => [m a] -> m [a]
    
    4608 4608
       setAddrRange# :: Addr# -> Int# -> Int# -> State# RealWorld -> State# RealWorld
    
    4609 4609
       setByteArray# :: forall d. MutableByteArray# d -> Int# -> Int# -> Int# -> State# d -> State# d
    
    4610
    +  setOtherThreadAllocationCounter# :: Int64# -> ThreadId# -> State# RealWorld -> State# RealWorld
    
    4610 4611
       setThreadAllocationCounter# :: Int64# -> State# RealWorld -> State# RealWorld
    
    4611 4612
       shiftL# :: Word# -> Int# -> Word#
    
    4612 4613
       shiftRL# :: Word# -> Int# -> Word#
    
    ... ... @@ -6665,6 +6666,7 @@ module GHC.Exts where
    6665 6666
       seq# :: forall a s. a -> State# s -> (# State# s, a #)
    
    6666 6667
       setAddrRange# :: Addr# -> Int# -> Int# -> State# RealWorld -> State# RealWorld
    
    6667 6668
       setByteArray# :: forall d. MutableByteArray# d -> Int# -> Int# -> Int# -> State# d -> State# d
    
    6669
    +  setOtherThreadAllocationCounter# :: Int64# -> ThreadId# -> State# RealWorld -> State# RealWorld
    
    6668 6670
       setThreadAllocationCounter# :: Int64# -> State# RealWorld -> State# RealWorld
    
    6669 6671
       shiftL# :: Word# -> Int# -> Word#
    
    6670 6672
       shiftRL# :: Word# -> Int# -> Word#
    

  • testsuite/tests/interface-stability/base-exports.stdout-mingw32
    ... ... @@ -4610,6 +4610,7 @@ module GHC.Base where
    4610 4610
       sequence :: forall (m :: * -> *) a. Monad m => [m a] -> m [a]
    
    4611 4611
       setAddrRange# :: Addr# -> Int# -> Int# -> State# RealWorld -> State# RealWorld
    
    4612 4612
       setByteArray# :: forall d. MutableByteArray# d -> Int# -> Int# -> Int# -> State# d -> State# d
    
    4613
    +  setOtherThreadAllocationCounter# :: Int64# -> ThreadId# -> State# RealWorld -> State# RealWorld
    
    4613 4614
       setThreadAllocationCounter# :: Int64# -> State# RealWorld -> State# RealWorld
    
    4614 4615
       shiftL# :: Word# -> Int# -> Word#
    
    4615 4616
       shiftRL# :: Word# -> Int# -> Word#
    
    ... ... @@ -6836,6 +6837,7 @@ module GHC.Exts where
    6836 6837
       seq# :: forall a s. a -> State# s -> (# State# s, a #)
    
    6837 6838
       setAddrRange# :: Addr# -> Int# -> Int# -> State# RealWorld -> State# RealWorld
    
    6838 6839
       setByteArray# :: forall d. MutableByteArray# d -> Int# -> Int# -> Int# -> State# d -> State# d
    
    6840
    +  setOtherThreadAllocationCounter# :: Int64# -> ThreadId# -> State# RealWorld -> State# RealWorld
    
    6839 6841
       setThreadAllocationCounter# :: Int64# -> State# RealWorld -> State# RealWorld
    
    6840 6842
       shiftL# :: Word# -> Int# -> Word#
    
    6841 6843
       shiftRL# :: Word# -> Int# -> Word#
    

  • testsuite/tests/interface-stability/base-exports.stdout-ws-32
    ... ... @@ -4607,6 +4607,7 @@ module GHC.Base where
    4607 4607
       sequence :: forall (m :: * -> *) a. Monad m => [m a] -> m [a]
    
    4608 4608
       setAddrRange# :: Addr# -> Int# -> Int# -> State# RealWorld -> State# RealWorld
    
    4609 4609
       setByteArray# :: forall d. MutableByteArray# d -> Int# -> Int# -> Int# -> State# d -> State# d
    
    4610
    +  setOtherThreadAllocationCounter# :: Int64# -> ThreadId# -> State# RealWorld -> State# RealWorld
    
    4610 4611
       setThreadAllocationCounter# :: Int64# -> State# RealWorld -> State# RealWorld
    
    4611 4612
       shiftL# :: Word# -> Int# -> Word#
    
    4612 4613
       shiftRL# :: Word# -> Int# -> Word#
    
    ... ... @@ -6693,6 +6694,7 @@ module GHC.Exts where
    6693 6694
       seq# :: forall a s. a -> State# s -> (# State# s, a #)
    
    6694 6695
       setAddrRange# :: Addr# -> Int# -> Int# -> State# RealWorld -> State# RealWorld
    
    6695 6696
       setByteArray# :: forall d. MutableByteArray# d -> Int# -> Int# -> Int# -> State# d -> State# d
    
    6697
    +  setOtherThreadAllocationCounter# :: Int64# -> ThreadId# -> State# RealWorld -> State# RealWorld
    
    6696 6698
       setThreadAllocationCounter# :: Int64# -> State# RealWorld -> State# RealWorld
    
    6697 6699
       shiftL# :: Word# -> Int# -> Word#
    
    6698 6700
       shiftRL# :: Word# -> Int# -> Word#
    

  • testsuite/tests/interface-stability/ghc-experimental-exports.stdout
    ... ... @@ -10916,6 +10916,16 @@ module Prelude.Experimental where
    10916 10916
       data Unit# = ...
    
    10917 10917
       getSolo :: forall a. Solo a -> a
    
    10918 10918
     
    
    10919
    +module System.Mem.Experimental where
    
    10920
    +  -- Safety: None
    
    10921
    +  type AllocationLimitKillBehaviour :: *
    
    10922
    +  data AllocationLimitKillBehaviour = KillOnAllocationLimit | DontKillOnAllocationLimit
    
    10923
    +  disableAllocationLimitFor :: GHC.Internal.Conc.Sync.ThreadId -> GHC.Types.IO ()
    
    10924
    +  enableAllocationLimitFor :: GHC.Internal.Conc.Sync.ThreadId -> GHC.Types.IO ()
    
    10925
    +  getAllocationCounterFor :: GHC.Internal.Conc.Sync.ThreadId -> GHC.Types.IO GHC.Internal.Int.Int64
    
    10926
    +  setAllocationCounterFor :: GHC.Internal.Int.Int64 -> GHC.Internal.Conc.Sync.ThreadId -> GHC.Types.IO ()
    
    10927
    +  setGlobalAllocationLimitHandler :: AllocationLimitKillBehaviour -> GHC.Internal.Maybe.Maybe (GHC.Internal.Conc.Sync.ThreadId -> GHC.Types.IO ()) -> GHC.Types.IO ()
    
    10928
    +
    
    10919 10929
     
    
    10920 10930
     -- Instances:
    
    10921 10931
     instance GHC.Internal.Base.Alternative GHC.Internal.Types.IO -- Defined in ‘GHC.Internal.Base’
    

  • testsuite/tests/rts/T22859.hs
    1
    +{-# LANGUAGE NumericUnderscores #-}
    
    2
    +{-# LANGUAGE MagicHash #-}
    
    3
    +{-# LANGUAGE UnboxedTuples #-}
    
    4
    +
    
    5
    +import Control.Exception
    
    6
    +import Control.Exception.Backtrace
    
    7
    +import Control.Concurrent
    
    8
    +import Control.Concurrent.MVar
    
    9
    +import System.Mem
    
    10
    +import System.Mem.Experimental
    
    11
    +import GHC.IO (IO (..))
    
    12
    +import GHC.Exts
    
    13
    +import System.IO
    
    14
    +
    
    15
    +-- | Just do some work and hPutStrLn to stderr to indicate that we are making progress
    
    16
    +worker :: IO ()
    
    17
    +worker = loop [] 2
    
    18
    +  where
    
    19
    +    loop !m !n
    
    20
    +      | n > 30 = hPutStrLn stderr . show $ length m
    
    21
    +      | otherwise = do
    
    22
    +          let x = show n
    
    23
    +          hPutStrLn stderr x
    
    24
    +          -- just to bulk out the allocations
    
    25
    +          IO (\s -> case newByteArray# 900000# s of (# s', arr# #) -> (# s', () #))
    
    26
    +          yield
    
    27
    +          loop (x:m) (n + 1)
    
    28
    +
    
    29
    +main :: IO ()
    
    30
    +main = do
    
    31
    +  done <- newMVar () -- we use this lock to wait for the worker to finish
    
    32
    +  started <- newEmptyMVar
    
    33
    +  let runWorker = do
    
    34
    +        forkIO . withMVar done $ \_ -> flip onException (hPutStrLn stderr "worker died") $ do
    
    35
    +          hPutStrLn stderr "worker starting"
    
    36
    +          putMVar started ()
    
    37
    +          setAllocationCounter 1_000_000
    
    38
    +          enableAllocationLimit
    
    39
    +          worker
    
    40
    +          hPutStrLn stderr "worker done"
    
    41
    +        takeMVar started
    
    42
    +        readMVar done
    
    43
    +        hFlush stderr
    
    44
    +        threadDelay 1000
    
    45
    +  -- default behaviour:
    
    46
    +  -- kill it after the limit is exceeded
    
    47
    +  hPutStrLn stderr "default behaviour"
    
    48
    +  runWorker
    
    49
    +  hPutStrLn stderr "just log once on the hook being triggered"
    
    50
    +  setGlobalAllocationLimitHandler DontKillOnAllocationLimit (Just $ \_ -> hPutStrLn stderr "allocation limit triggered 1")
    
    51
    +  runWorker
    
    52
    +  hPutStrLn stderr "just log on the hook being triggered"
    
    53
    +  setGlobalAllocationLimitHandler DontKillOnAllocationLimit . Just $ \tid -> do
    
    54
    +    hPutStrLn stderr "allocation limit triggered 2"
    
    55
    +    -- re-enable the hook
    
    56
    +    setAllocationCounterFor 1_000_000 tid
    
    57
    +    enableAllocationLimitFor tid
    
    58
    +  runWorker
    
    59
    +  hPutStrLn stderr "kill from the hook"
    
    60
    +  setGlobalAllocationLimitHandler DontKillOnAllocationLimit . Just $ \tId -> throwTo tId AllocationLimitExceeded
    
    61
    +  runWorker
    
    62
    +  -- not super helpful, but let's test it anyway
    
    63
    +  hPutStrLn stderr "do nothing"
    
    64
    +  setGlobalAllocationLimitHandler DontKillOnAllocationLimit Nothing
    
    65
    +  runWorker
    
    66
    +  -- this is possible to handle using an exception handler instead.
    
    67
    +  hPutStrLn stderr "kill and log"
    
    68
    +  setGlobalAllocationLimitHandler KillOnAllocationLimit (Just $ \_ -> hPutStrLn stderr "allocation limit triggered 3")
    
    69
    +  runWorker
    
    70
    +  threadDelay 1000
    
    71
    +  hPutStrLn stderr "done"

  • testsuite/tests/rts/T22859.stderr
    1
    +default behaviour
    
    2
    +worker starting
    
    3
    +2
    
    4
    +3
    
    5
    +worker died
    
    6
    +T22859: allocation limit exceeded
    
    7
    +HasCallStack backtrace:
    
    8
    +  collectBacktraces, called at libraries/ghc-internal/src/GHC/Internal/Exception.hs:92:13 in ghc-internal:GHC.Internal.Exception
    
    9
    +  toExceptionWithBacktrace, called at libraries/ghc-internal/src/GHC/Internal/IO.hs:260:11 in ghc-internal:GHC.Internal.IO
    
    10
    +  throwIO, called at libraries/ghc-internal/src/GHC/Internal/Control/Exception/Base.hs:195:43 in ghc-internal:GHC.Internal.Control.Exception.Base
    
    11
    +
    
    12
    +
    
    13
    +just log once on the hook being triggered
    
    14
    +worker starting
    
    15
    +2
    
    16
    +3
    
    17
    +allocation limit triggered 1
    
    18
    +4
    
    19
    +5
    
    20
    +6
    
    21
    +7
    
    22
    +8
    
    23
    +9
    
    24
    +10
    
    25
    +11
    
    26
    +12
    
    27
    +13
    
    28
    +14
    
    29
    +15
    
    30
    +16
    
    31
    +17
    
    32
    +18
    
    33
    +19
    
    34
    +20
    
    35
    +21
    
    36
    +22
    
    37
    +23
    
    38
    +24
    
    39
    +25
    
    40
    +26
    
    41
    +27
    
    42
    +28
    
    43
    +29
    
    44
    +30
    
    45
    +29
    
    46
    +worker done
    
    47
    +just log on the hook being triggered
    
    48
    +worker starting
    
    49
    +2
    
    50
    +3
    
    51
    +allocation limit triggered 2
    
    52
    +4
    
    53
    +5
    
    54
    +allocation limit triggered 2
    
    55
    +6
    
    56
    +7
    
    57
    +allocation limit triggered 2
    
    58
    +8
    
    59
    +9
    
    60
    +allocation limit triggered 2
    
    61
    +10
    
    62
    +11
    
    63
    +allocation limit triggered 2
    
    64
    +12
    
    65
    +13
    
    66
    +allocation limit triggered 2
    
    67
    +14
    
    68
    +15
    
    69
    +allocation limit triggered 2
    
    70
    +16
    
    71
    +17
    
    72
    +allocation limit triggered 2
    
    73
    +18
    
    74
    +19
    
    75
    +allocation limit triggered 2
    
    76
    +20
    
    77
    +21
    
    78
    +allocation limit triggered 2
    
    79
    +22
    
    80
    +23
    
    81
    +allocation limit triggered 2
    
    82
    +24
    
    83
    +25
    
    84
    +allocation limit triggered 2
    
    85
    +26
    
    86
    +27
    
    87
    +allocation limit triggered 2
    
    88
    +28
    
    89
    +29
    
    90
    +allocation limit triggered 2
    
    91
    +30
    
    92
    +29
    
    93
    +worker done
    
    94
    +kill from the hook
    
    95
    +worker starting
    
    96
    +2
    
    97
    +3
    
    98
    +worker died
    
    99
    +T22859: allocation limit exceeded
    
    100
    +HasCallStack backtrace:
    
    101
    +  collectBacktraces, called at libraries/ghc-internal/src/GHC/Internal/Exception.hs:92:13 in ghc-internal:GHC.Internal.Exception
    
    102
    +  toExceptionWithBacktrace, called at libraries/ghc-internal/src/GHC/Internal/IO.hs:260:11 in ghc-internal:GHC.Internal.IO
    
    103
    +  throwIO, called at libraries/ghc-internal/src/GHC/Internal/Control/Exception/Base.hs:195:43 in ghc-internal:GHC.Internal.Control.Exception.Base
    
    104
    +
    
    105
    +
    
    106
    +do nothing
    
    107
    +worker starting
    
    108
    +2
    
    109
    +3
    
    110
    +4
    
    111
    +5
    
    112
    +6
    
    113
    +7
    
    114
    +8
    
    115
    +9
    
    116
    +10
    
    117
    +11
    
    118
    +12
    
    119
    +13
    
    120
    +14
    
    121
    +15
    
    122
    +16
    
    123
    +17
    
    124
    +18
    
    125
    +19
    
    126
    +20
    
    127
    +21
    
    128
    +22
    
    129
    +23
    
    130
    +24
    
    131
    +25
    
    132
    +26
    
    133
    +27
    
    134
    +28
    
    135
    +29
    
    136
    +30
    
    137
    +29
    
    138
    +worker done
    
    139
    +kill and log
    
    140
    +worker starting
    
    141
    +2
    
    142
    +3
    
    143
    +allocation limit triggered 3
    
    144
    +worker died
    
    145
    +T22859: allocation limit exceeded
    
    146
    +HasCallStack backtrace:
    
    147
    +  collectBacktraces, called at libraries/ghc-internal/src/GHC/Internal/Exception.hs:92:13 in ghc-internal:GHC.Internal.Exception
    
    148
    +  toExceptionWithBacktrace, called at libraries/ghc-internal/src/GHC/Internal/IO.hs:260:11 in ghc-internal:GHC.Internal.IO
    
    149
    +  throwIO, called at libraries/ghc-internal/src/GHC/Internal/Control/Exception/Base.hs:195:43 in ghc-internal:GHC.Internal.Control.Exception.Base
    
    150
    +
    
    151
    +
    
    152
    +done

  • testsuite/tests/rts/all.T
    ... ... @@ -643,3 +643,4 @@ test('T25280', [unless(opsys('linux'),skip),req_process,js_skip], compile_and_ru
    643 643
     test('T25560', [req_c_rts, ignore_stderr], compile_and_run, [''])
    
    644 644
     
    
    645 645
     test('TestProddableBlockSet', [req_c_rts], multimod_compile_and_run, ['TestProddableBlockSet.c', '-no-hs-main'])
    
    646
    +test('T22859', [js_skip], compile_and_run, ['-with-rtsopts -A8K'])