Marge Bot pushed to branch master at Glasgow Haskell Compiler / GHC
Commits:
-
a48dcdf3
by Duncan Coutts at 2025-07-07T20:45:18-04:00
-
de5b528c
by Duncan Coutts at 2025-07-07T20:45:18-04:00
-
b321319d
by Duncan Coutts at 2025-07-07T20:45:18-04:00
-
1d557ffb
by Duncan Coutts at 2025-07-07T20:45:18-04:00
-
e59a1430
by Duncan Coutts at 2025-07-07T20:45:18-04:00
-
482df1c9
by Duncan Coutts at 2025-07-07T20:45:18-04:00
-
4d3ec8f9
by Duncan Coutts at 2025-07-07T20:45:18-04:00
-
0c4f2fde
by Duncan Coutts at 2025-07-07T20:45:18-04:00
-
a3354ad9
by Duncan Coutts at 2025-07-07T20:45:18-04:00
14 changed files:
- + rts/AllocArray.c
- + rts/AllocArray.h
- rts/Heap.c
- rts/PrimOps.cmm
- rts/RtsUtils.c
- rts/ThreadLabels.c
- rts/Threads.c
- rts/Weak.c
- rts/include/Rts.h
- rts/include/rts/prof/CCS.h
- rts/include/rts/storage/GC.h
- rts/include/rts/storage/Heap.h
- rts/rts.cabal
- rts/sm/Storage.c
Changes:
| 1 | +#include "rts/PosixSource.h"
|
|
| 2 | +#include "Rts.h"
|
|
| 3 | + |
|
| 4 | +#include "AllocArray.h"
|
|
| 5 | + |
|
| 6 | +StgMutArrPtrs *allocateMutArrPtrs (Capability *cap,
|
|
| 7 | + StgWord nelements,
|
|
| 8 | + CostCentreStack *ccs USED_IF_PROFILING)
|
|
| 9 | +{
|
|
| 10 | + /* All sizes in words */
|
|
| 11 | + |
|
| 12 | + /* The card table contains one byte for each 2^MUT_ARR_PTRS_CARD_BITS words
|
|
| 13 | + * in the array, making sure we round up, and then rounding up to a whole
|
|
| 14 | + * number of words. */
|
|
| 15 | + StgWord cardsize = mutArrPtrsCardTableSize(nelements); /* card table */
|
|
| 16 | + StgWord arrsize = nelements + cardsize; /* +array size */
|
|
| 17 | + StgWord objsize = sizeofW(StgMutArrPtrs) + arrsize; /* +header size */
|
|
| 18 | + StgMutArrPtrs *arr;
|
|
| 19 | + arr = (StgMutArrPtrs *)allocateMightFail(cap, objsize);
|
|
| 20 | + if (RTS_UNLIKELY(arr == NULL)) return NULL;
|
|
| 21 | + TICK_ALLOC_PRIM(sizeofW(StgMutArrPtrs), arrsize, 0);
|
|
| 22 | + |
|
| 23 | + /* No write barrier needed since this is a new allocation. */
|
|
| 24 | + SET_HDR(arr, &stg_MUT_ARR_PTRS_DIRTY_info, ccs);
|
|
| 25 | + arr->ptrs = nelements;
|
|
| 26 | + arr->size = arrsize;
|
|
| 27 | + |
|
| 28 | + /* Initialize the card array. Note that memset needs sizes in bytes. */
|
|
| 29 | + memset(&(arr->payload[nelements]), 0, mutArrPtrsCards(nelements));
|
|
| 30 | + |
|
| 31 | + return arr;
|
|
| 32 | +}
|
|
| 33 | + |
|
| 34 | +StgSmallMutArrPtrs *allocateSmallMutArrPtrs (Capability *cap,
|
|
| 35 | + StgWord nelements,
|
|
| 36 | + CostCentreStack *ccs
|
|
| 37 | + USED_IF_PROFILING)
|
|
| 38 | +{
|
|
| 39 | + /* All sizes in words */
|
|
| 40 | + StgWord arrsize = nelements; /* array size */
|
|
| 41 | + StgWord objsize = sizeofW(StgSmallMutArrPtrs) + arrsize; /* +header size */
|
|
| 42 | + StgSmallMutArrPtrs *arr;
|
|
| 43 | + arr = (StgSmallMutArrPtrs *)allocateMightFail(cap, objsize);
|
|
| 44 | + if (RTS_UNLIKELY(arr == NULL)) return NULL;
|
|
| 45 | + TICK_ALLOC_PRIM(sizeofW(StgSmallMutArrPtrs), arrsize, 0);
|
|
| 46 | + |
|
| 47 | + /* No write barrier needed since this is a new allocation. */
|
|
| 48 | + SET_HDR(arr, &stg_SMALL_MUT_ARR_PTRS_DIRTY_info, ccs);
|
|
| 49 | + arr->ptrs = nelements;
|
|
| 50 | + return arr;
|
|
| 51 | +}
|
|
| 52 | + |
|
| 53 | +StgArrBytes *allocateArrBytes (Capability *cap,
|
|
| 54 | + StgWord arrbytes,
|
|
| 55 | + CostCentreStack *ccs USED_IF_PROFILING)
|
|
| 56 | +{
|
|
| 57 | + /* All sizes in words */
|
|
| 58 | + StgWord arrwords = ROUNDUP_BYTES_TO_WDS(arrbytes);
|
|
| 59 | + StgWord objsize = sizeofW(StgArrBytes) + arrwords;
|
|
| 60 | + StgArrBytes *arr;
|
|
| 61 | + arr = (StgArrBytes *)allocateMightFail(cap, objsize);
|
|
| 62 | + if (RTS_UNLIKELY(arr == NULL)) return NULL;
|
|
| 63 | + TICK_ALLOC_PRIM(sizeofW(StgArrBytes), arrwords, 0);
|
|
| 64 | + /* No write barrier needed since this is a new allocation. */
|
|
| 65 | + SET_HDR(arr, &stg_ARR_WORDS_info, ccs);
|
|
| 66 | + arr->bytes = arrbytes;
|
|
| 67 | + return arr;
|
|
| 68 | +}
|
|
| 69 | + |
|
| 70 | +StgArrBytes *allocateArrBytesPinned (Capability *cap,
|
|
| 71 | + StgWord arrbytes,
|
|
| 72 | + StgWord alignment,
|
|
| 73 | + CostCentreStack *ccs USED_IF_PROFILING)
|
|
| 74 | +{
|
|
| 75 | + /* we always supply at least word-aligned memory, so there's no
|
|
| 76 | + need to allow extra space for alignment if the requirement is less
|
|
| 77 | + than a word. This also prevents mischief with alignment == 0. */
|
|
| 78 | + if (alignment <= sizeof(StgWord)) { alignment = sizeof(StgWord); }
|
|
| 79 | + |
|
| 80 | + /* All sizes in words */
|
|
| 81 | + StgWord arrwords = ROUNDUP_BYTES_TO_WDS(arrbytes);
|
|
| 82 | + StgWord objsize = sizeofW(StgArrBytes) + arrwords;
|
|
| 83 | + StgWord alignoff = sizeof(StgArrBytes); // it's the payload to be aligned
|
|
| 84 | + StgArrBytes *arr;
|
|
| 85 | + arr = (StgArrBytes *)allocatePinned(cap, objsize, alignment, alignoff);
|
|
| 86 | + if (RTS_UNLIKELY(arr == NULL)) return NULL;
|
|
| 87 | + TICK_ALLOC_PRIM(sizeofW(StgArrBytes), arrwords, 0);
|
|
| 88 | + /* No write barrier needed since this is a new allocation. */
|
|
| 89 | + SET_HDR(arr, &stg_ARR_WORDS_info, ccs);
|
|
| 90 | + arr->bytes = arrbytes;
|
|
| 91 | + return arr;
|
|
| 92 | +} |
| 1 | +/* -----------------------------------------------------------------------------
|
|
| 2 | + *
|
|
| 3 | + * (c) The GHC Team 2025
|
|
| 4 | + *
|
|
| 5 | + * Prototypes for functions in AllocArray.c
|
|
| 6 | + *
|
|
| 7 | + * RTS internal utilities for allocating arrays of pointers (StgMutArrPtrs) and
|
|
| 8 | + * arrays of bytes (StgArrBytes).
|
|
| 9 | + * -------------------------------------------------------------------------*/
|
|
| 10 | + |
|
| 11 | +#pragma once
|
|
| 12 | + |
|
| 13 | +#include "Capability.h"
|
|
| 14 | + |
|
| 15 | +#include "BeginPrivate.h"
|
|
| 16 | + |
|
| 17 | +/* All these allocation functions return NULL on failure. If the context
|
|
| 18 | + * allows, then propagatethe failure upwards, e.g. to a CMM primop where a
|
|
| 19 | + * heap overflow exception can be thrown. Otherwise, use:
|
|
| 20 | + * if (RTS_UNLIKELY(p == NULL)) exitHeapOverflow();
|
|
| 21 | + */
|
|
| 22 | + |
|
| 23 | +/* Allocate a StgMutArrPtrs for a given number of elements. It is allocated in
|
|
| 24 | + * the DIRTY state.
|
|
| 25 | + */
|
|
| 26 | +StgMutArrPtrs *allocateMutArrPtrs (Capability *cap,
|
|
| 27 | + StgWord nelements,
|
|
| 28 | + CostCentreStack *ccs);
|
|
| 29 | + |
|
| 30 | +/* Allocate a StgSmallMutArrPtrs for a given number of elements.
|
|
| 31 | + */
|
|
| 32 | +StgSmallMutArrPtrs *allocateSmallMutArrPtrs (Capability *cap,
|
|
| 33 | + StgWord nelements,
|
|
| 34 | + CostCentreStack *ccs);
|
|
| 35 | + |
|
| 36 | +/* Allocate a StgArrBytes for a given number of bytes.
|
|
| 37 | + */
|
|
| 38 | +StgArrBytes *allocateArrBytes (Capability *cap,
|
|
| 39 | + StgWord nbytes,
|
|
| 40 | + CostCentreStack *ccs);
|
|
| 41 | + |
|
| 42 | +/* Allocate a pinned (and optionally aligned) StgArrBytes for a given number
|
|
| 43 | + * of bytes.
|
|
| 44 | + */
|
|
| 45 | +StgArrBytes *allocateArrBytesPinned (Capability *cap,
|
|
| 46 | + StgWord nbytes,
|
|
| 47 | + StgWord alignment,
|
|
| 48 | + CostCentreStack *ccs);
|
|
| 49 | + |
|
| 50 | +#include "EndPrivate.h" |
| ... | ... | @@ -13,6 +13,7 @@ |
| 13 | 13 | |
| 14 | 14 | #include "Capability.h"
|
| 15 | 15 | #include "Printer.h"
|
| 16 | +#include "AllocArray.h"
|
|
| 16 | 17 | |
| 17 | 18 | StgWord heap_view_closureSize(StgClosure *closure) {
|
| 18 | 19 | ASSERT(LOOKS_LIKE_CLOSURE_PTR(closure));
|
| ... | ... | @@ -278,18 +279,14 @@ StgMutArrPtrs *heap_view_closurePtrs(Capability *cap, StgClosure *closure) { |
| 278 | 279 | StgClosure **ptrs = (StgClosure **) stgMallocBytes(sizeof(StgClosure *) * size, "heap_view_closurePtrs");
|
| 279 | 280 | StgWord nptrs = collect_pointers(closure, ptrs);
|
| 280 | 281 | |
| 281 | - size = nptrs + mutArrPtrsCardTableSize(nptrs);
|
|
| 282 | - StgMutArrPtrs *arr =
|
|
| 283 | - (StgMutArrPtrs *)allocate(cap, sizeofW(StgMutArrPtrs) + size);
|
|
| 284 | - TICK_ALLOC_PRIM(sizeofW(StgMutArrPtrs), nptrs, 0);
|
|
| 285 | - SET_HDR(arr, &stg_MUT_ARR_PTRS_FROZEN_CLEAN_info, cap->r.rCCCS);
|
|
| 286 | - arr->ptrs = nptrs;
|
|
| 287 | - arr->size = size;
|
|
| 282 | + StgMutArrPtrs *arr = allocateMutArrPtrs(cap, nptrs, cap->r.rCCCS);
|
|
| 283 | + if (RTS_UNLIKELY(arr == NULL)) goto end;
|
|
| 284 | + SET_INFO((StgClosure *) arr, &stg_MUT_ARR_PTRS_FROZEN_CLEAN_info);
|
|
| 288 | 285 | |
| 289 | 286 | for (StgWord i = 0; i<nptrs; i++) {
|
| 290 | 287 | arr->payload[i] = ptrs[i];
|
| 291 | 288 | }
|
| 289 | +end:
|
|
| 292 | 290 | stgFree(ptrs);
|
| 293 | - |
|
| 294 | 291 | return arr;
|
| 295 | 292 | } |
| ... | ... | @@ -112,20 +112,14 @@ import CLOSURE stg_sel_0_upd_info; |
| 112 | 112 | |
| 113 | 113 | stg_newByteArrayzh ( W_ n )
|
| 114 | 114 | {
|
| 115 | - W_ words, payload_words;
|
|
| 116 | 115 | gcptr p;
|
| 117 | 116 | |
| 118 | 117 | MAYBE_GC_N(stg_newByteArrayzh, n);
|
| 119 | 118 | |
| 120 | - payload_words = ROUNDUP_BYTES_TO_WDS(n);
|
|
| 121 | - words = BYTES_TO_WDS(SIZEOF_StgArrBytes) + payload_words;
|
|
| 122 | - ("ptr" p) = ccall allocateMightFail(MyCapability() "ptr", words);
|
|
| 123 | - if (p == NULL) {
|
|
| 119 | + ("ptr" p) = ccall allocateArrBytes(MyCapability() "ptr", n, CCCS);
|
|
| 120 | + if (p == NULL) (likely: False) {
|
|
| 124 | 121 | jump stg_raisezh(ghczminternal_GHCziInternalziIOziException_heapOverflow_closure);
|
| 125 | 122 | }
|
| 126 | - TICK_ALLOC_PRIM(SIZEOF_StgArrBytes,WDS(payload_words),0);
|
|
| 127 | - SET_HDR(p, stg_ARR_WORDS_info, CCCS);
|
|
| 128 | - StgArrBytes_bytes(p) = n;
|
|
| 129 | 123 | return (p);
|
| 130 | 124 | }
|
| 131 | 125 | |
| ... | ... | @@ -134,64 +128,29 @@ stg_newByteArrayzh ( W_ n ) |
| 134 | 128 | |
| 135 | 129 | stg_newPinnedByteArrayzh ( W_ n )
|
| 136 | 130 | {
|
| 137 | - W_ words, bytes, payload_words;
|
|
| 138 | 131 | gcptr p;
|
| 139 | 132 | |
| 140 | 133 | MAYBE_GC_N(stg_newPinnedByteArrayzh, n);
|
| 141 | 134 | |
| 142 | - bytes = n;
|
|
| 143 | - /* payload_words is what we will tell the profiler we had to allocate */
|
|
| 144 | - payload_words = ROUNDUP_BYTES_TO_WDS(bytes);
|
|
| 145 | - /* When we actually allocate memory, we need to allow space for the
|
|
| 146 | - header: */
|
|
| 147 | - bytes = bytes + SIZEOF_StgArrBytes;
|
|
| 148 | - /* Now we convert to a number of words: */
|
|
| 149 | - words = ROUNDUP_BYTES_TO_WDS(bytes);
|
|
| 150 | - |
|
| 151 | - ("ptr" p) = ccall allocatePinned(MyCapability() "ptr", words, BA_ALIGN, SIZEOF_StgArrBytes);
|
|
| 152 | - if (p == NULL) {
|
|
| 135 | + ("ptr" p) = ccall allocateArrBytesPinned(MyCapability() "ptr", n,
|
|
| 136 | + BA_ALIGN, CCCS);
|
|
| 137 | + if (p == NULL) (likely: False) {
|
|
| 153 | 138 | jump stg_raisezh(ghczminternal_GHCziInternalziIOziException_heapOverflow_closure);
|
| 154 | 139 | }
|
| 155 | - TICK_ALLOC_PRIM(SIZEOF_StgArrBytes,WDS(payload_words),0);
|
|
| 156 | - |
|
| 157 | - /* No write barrier needed since this is a new allocation. */
|
|
| 158 | - SET_HDR(p, stg_ARR_WORDS_info, CCCS);
|
|
| 159 | - StgArrBytes_bytes(p) = n;
|
|
| 160 | 140 | return (p);
|
| 161 | 141 | }
|
| 162 | 142 | |
| 163 | 143 | stg_newAlignedPinnedByteArrayzh ( W_ n, W_ alignment )
|
| 164 | 144 | {
|
| 165 | - W_ words, bytes, payload_words;
|
|
| 166 | 145 | gcptr p;
|
| 167 | 146 | |
| 168 | 147 | again: MAYBE_GC(again);
|
| 169 | 148 | |
| 170 | - /* we always supply at least word-aligned memory, so there's no
|
|
| 171 | - need to allow extra space for alignment if the requirement is less
|
|
| 172 | - than a word. This also prevents mischief with alignment == 0. */
|
|
| 173 | - if (alignment <= SIZEOF_W) { alignment = SIZEOF_W; }
|
|
| 174 | - |
|
| 175 | - bytes = n;
|
|
| 176 | - |
|
| 177 | - /* payload_words is what we will tell the profiler we had to allocate */
|
|
| 178 | - payload_words = ROUNDUP_BYTES_TO_WDS(bytes);
|
|
| 179 | - |
|
| 180 | - /* When we actually allocate memory, we need to allow space for the
|
|
| 181 | - header: */
|
|
| 182 | - bytes = bytes + SIZEOF_StgArrBytes;
|
|
| 183 | - /* Now we convert to a number of words: */
|
|
| 184 | - words = ROUNDUP_BYTES_TO_WDS(bytes);
|
|
| 185 | - |
|
| 186 | - ("ptr" p) = ccall allocatePinned(MyCapability() "ptr", words, alignment, SIZEOF_StgArrBytes);
|
|
| 187 | - if (p == NULL) {
|
|
| 149 | + ("ptr" p) = ccall allocateArrBytesPinned(MyCapability() "ptr", n,
|
|
| 150 | + alignment, CCCS);
|
|
| 151 | + if (p == NULL) (likely: False) {
|
|
| 188 | 152 | jump stg_raisezh(ghczminternal_GHCziInternalziIOziException_heapOverflow_closure);
|
| 189 | 153 | }
|
| 190 | - TICK_ALLOC_PRIM(SIZEOF_StgArrBytes,WDS(payload_words),0);
|
|
| 191 | - |
|
| 192 | - /* No write barrier needed since this is a new allocation. */
|
|
| 193 | - SET_HDR(p, stg_ARR_WORDS_info, CCCS);
|
|
| 194 | - StgArrBytes_bytes(p) = n;
|
|
| 195 | 154 | return (p);
|
| 196 | 155 | }
|
| 197 | 156 | |
| ... | ... | @@ -399,36 +358,23 @@ stg_casInt64Arrayzh( gcptr arr, W_ ind, I64 old, I64 new ) |
| 399 | 358 | |
| 400 | 359 | stg_newArrayzh ( W_ n /* words */, gcptr init )
|
| 401 | 360 | {
|
| 402 | - W_ words, size, p;
|
|
| 403 | 361 | gcptr arr;
|
| 404 | 362 | |
| 405 | 363 | again: MAYBE_GC(again);
|
| 406 | 364 | |
| 407 | - // the mark area contains one byte for each 2^MUT_ARR_PTRS_CARD_BITS words
|
|
| 408 | - // in the array, making sure we round up, and then rounding up to a whole
|
|
| 409 | - // number of words.
|
|
| 410 | - size = n + mutArrPtrsCardWords(n);
|
|
| 411 | - words = BYTES_TO_WDS(SIZEOF_StgMutArrPtrs) + size;
|
|
| 412 | - ("ptr" arr) = ccall allocateMightFail(MyCapability() "ptr",words);
|
|
| 413 | - if (arr == NULL) {
|
|
| 365 | + ("ptr" arr) = ccall allocateMutArrPtrs(MyCapability() "ptr", n, CCCS);
|
|
| 366 | + if (arr == NULL) (likely: False) {
|
|
| 414 | 367 | jump stg_raisezh(ghczminternal_GHCziInternalziIOziException_heapOverflow_closure);
|
| 415 | 368 | }
|
| 416 | - TICK_ALLOC_PRIM(SIZEOF_StgMutArrPtrs, WDS(size), 0);
|
|
| 417 | - |
|
| 418 | - /* No write barrier needed since this is a new allocation. */
|
|
| 419 | - SET_HDR(arr, stg_MUT_ARR_PTRS_DIRTY_info, CCCS);
|
|
| 420 | - StgMutArrPtrs_ptrs(arr) = n;
|
|
| 421 | - StgMutArrPtrs_size(arr) = size;
|
|
| 422 | - |
|
| 423 | - /* Ensure that the card array is initialized */
|
|
| 424 | - if (n != 0) {
|
|
| 425 | - setCardsValue(arr, 0, n, 0);
|
|
| 426 | - }
|
|
| 427 | 369 | |
| 428 | - // Initialise all elements of the array with the value in R2
|
|
| 370 | + // Initialise all elements of the array with the value init
|
|
| 371 | + W_ p;
|
|
| 429 | 372 | p = arr + SIZEOF_StgMutArrPtrs;
|
| 373 | + // Avoid the shift for `WDS(n)` in the inner loop
|
|
| 374 | + W_ limit;
|
|
| 375 | + limit = arr + SIZEOF_StgMutArrPtrs + WDS(n);
|
|
| 430 | 376 | for:
|
| 431 | - if (p < arr + SIZEOF_StgMutArrPtrs + WDS(n)) (likely: True) {
|
|
| 377 | + if (p < limit) (likely: True) {
|
|
| 432 | 378 | W_[p] = init;
|
| 433 | 379 | p = p + WDS(1);
|
| 434 | 380 | goto for;
|
| ... | ... | @@ -522,23 +468,17 @@ stg_casArrayzh ( gcptr arr, W_ ind, gcptr old, gcptr new ) |
| 522 | 468 | |
| 523 | 469 | stg_newSmallArrayzh ( W_ n /* words */, gcptr init )
|
| 524 | 470 | {
|
| 525 | - W_ words, size, p;
|
|
| 526 | 471 | gcptr arr;
|
| 527 | 472 | |
| 528 | 473 | again: MAYBE_GC(again);
|
| 529 | 474 | |
| 530 | - words = BYTES_TO_WDS(SIZEOF_StgSmallMutArrPtrs) + n;
|
|
| 531 | - ("ptr" arr) = ccall allocateMightFail(MyCapability() "ptr",words);
|
|
| 475 | + ("ptr" arr) = ccall allocateSmallMutArrPtrs(MyCapability() "ptr", n, CCCS);
|
|
| 532 | 476 | if (arr == NULL) (likely: False) {
|
| 533 | 477 | jump stg_raisezh(ghczminternal_GHCziInternalziIOziException_heapOverflow_closure);
|
| 534 | 478 | }
|
| 535 | - TICK_ALLOC_PRIM(SIZEOF_StgSmallMutArrPtrs, WDS(n), 0);
|
|
| 536 | - |
|
| 537 | - /* No write barrier needed since this is a new allocation. */
|
|
| 538 | - SET_HDR(arr, stg_SMALL_MUT_ARR_PTRS_DIRTY_info, CCCS);
|
|
| 539 | - StgSmallMutArrPtrs_ptrs(arr) = n;
|
|
| 540 | 479 | |
| 541 | - // Initialise all elements of the array with the value in R2
|
|
| 480 | + // Initialise all elements of the array with the value init
|
|
| 481 | + W_ p;
|
|
| 542 | 482 | p = arr + SIZEOF_StgSmallMutArrPtrs;
|
| 543 | 483 | // Avoid the shift for `WDS(n)` in the inner loop
|
| 544 | 484 | W_ limit;
|
| ... | ... | @@ -1148,6 +1088,11 @@ stg_listThreadszh () |
| 1148 | 1088 | P_ arr;
|
| 1149 | 1089 | |
| 1150 | 1090 | ("ptr" arr) = ccall listThreads(MyCapability() "ptr");
|
| 1091 | + |
|
| 1092 | + if (arr == NULL) (likely: False) {
|
|
| 1093 | + jump stg_raisezh(ghczminternal_GHCziInternalziIOziException_heapOverflow_closure);
|
|
| 1094 | + }
|
|
| 1095 | + |
|
| 1151 | 1096 | return (arr);
|
| 1152 | 1097 | }
|
| 1153 | 1098 | |
| ... | ... | @@ -1414,7 +1359,7 @@ stg_atomicallyzh (P_ stm) |
| 1414 | 1359 | old_trec = StgTSO_trec(CurrentTSO);
|
| 1415 | 1360 | |
| 1416 | 1361 | /* Nested transactions are not allowed; raise an exception */
|
| 1417 | - if (old_trec != NO_TREC) {
|
|
| 1362 | + if (old_trec != NO_TREC) (likely: False) {
|
|
| 1418 | 1363 | jump stg_raisezh(ghczminternal_GHCziInternalziControlziExceptionziBase_nestedAtomically_closure);
|
| 1419 | 1364 | }
|
| 1420 | 1365 | |
| ... | ... | @@ -2537,6 +2482,10 @@ for: |
| 2537 | 2482 | // Collect pointers.
|
| 2538 | 2483 | ("ptr" ptrArray) = foreign "C" heap_view_closurePtrs(MyCapability() "ptr", clos "ptr");
|
| 2539 | 2484 | |
| 2485 | + if (ptrArray == NULL) (likely: False) {
|
|
| 2486 | + jump stg_raisezh(ghczminternal_GHCziInternalziIOziException_heapOverflow_closure);
|
|
| 2487 | + }
|
|
| 2488 | + |
|
| 2540 | 2489 | return (info, dat_arr, ptrArray);
|
| 2541 | 2490 | }
|
| 2542 | 2491 |
| ... | ... | @@ -198,6 +198,13 @@ reportHeapOverflow(void) |
| 198 | 198 | (W_)RtsFlags.GcFlags.maxHeapSize * BLOCK_SIZE);
|
| 199 | 199 | }
|
| 200 | 200 | |
| 201 | +void
|
|
| 202 | +exitHeapOverflow(void)
|
|
| 203 | +{
|
|
| 204 | + reportHeapOverflow(); // reportHeapOverflow() doesn't exit (see #2592)
|
|
| 205 | + stg_exit(EXIT_HEAPOVERFLOW);
|
|
| 206 | +}
|
|
| 207 | + |
|
| 201 | 208 | /* -----------------------------------------------------------------------------
|
| 202 | 209 | Sleep for the given period of time.
|
| 203 | 210 | -------------------------------------------------------------------------- */
|
| ... | ... | @@ -15,6 +15,7 @@ |
| 15 | 15 | #include "RtsFlags.h"
|
| 16 | 16 | #include "Hash.h"
|
| 17 | 17 | #include "Trace.h"
|
| 18 | +#include "AllocArray.h"
|
|
| 18 | 19 | |
| 19 | 20 | #include <stdlib.h>
|
| 20 | 21 | #include <string.h>
|
| ... | ... | @@ -31,25 +32,16 @@ |
| 31 | 32 | * determined by the ByteArray# length.
|
| 32 | 33 | */
|
| 33 | 34 | |
| 34 | -static StgArrBytes *
|
|
| 35 | -allocateArrBytes(Capability *cap, size_t size_in_bytes)
|
|
| 36 | -{
|
|
| 37 | - /* round up to a whole number of words */
|
|
| 38 | - uint32_t data_size_in_words = ROUNDUP_BYTES_TO_WDS(size_in_bytes);
|
|
| 39 | - uint32_t total_size_in_words = sizeofW(StgArrBytes) + data_size_in_words;
|
|
| 40 | - |
|
| 41 | - StgArrBytes *arr = (StgArrBytes *) allocate(cap, total_size_in_words);
|
|
| 42 | - SET_ARR_HDR(arr, &stg_ARR_WORDS_info, cap->r.rCCCS, size_in_bytes);
|
|
| 43 | - return arr;
|
|
| 44 | -}
|
|
| 45 | - |
|
| 46 | 35 | void
|
| 47 | 36 | setThreadLabel(Capability *cap,
|
| 48 | 37 | StgTSO *tso,
|
| 49 | 38 | char *label)
|
| 50 | 39 | {
|
| 51 | 40 | int len = strlen(label);
|
| 52 | - StgArrBytes *arr = allocateArrBytes(cap, len);
|
|
| 41 | + StgArrBytes *arr = allocateArrBytes(cap, len, cap->r.rCCCS);
|
|
| 42 | + // On allocation failure don't perform the effect. It's not convenient to
|
|
| 43 | + // propagate failure from here since there are multiple callers in the RTS.
|
|
| 44 | + if (RTS_UNLIKELY(arr == NULL)) return;
|
|
| 53 | 45 | memcpy(&arr->payload, label, len);
|
| 54 | 46 | labelThread(cap, tso, arr);
|
| 55 | 47 | }
|
| ... | ... | @@ -25,6 +25,7 @@ |
| 25 | 25 | #include "Printer.h"
|
| 26 | 26 | #include "sm/Sanity.h"
|
| 27 | 27 | #include "sm/Storage.h"
|
| 28 | +#include "AllocArray.h"
|
|
| 28 | 29 | |
| 29 | 30 | #include <string.h>
|
| 30 | 31 | |
| ... | ... | @@ -879,6 +880,7 @@ loop: |
| 879 | 880 | return true;
|
| 880 | 881 | }
|
| 881 | 882 | |
| 883 | +/* Return NULL on allocation failure */
|
|
| 882 | 884 | StgMutArrPtrs *listThreads(Capability *cap)
|
| 883 | 885 | {
|
| 884 | 886 | ACQUIRE_LOCK(&sched_mutex);
|
| ... | ... | @@ -892,13 +894,8 @@ StgMutArrPtrs *listThreads(Capability *cap) |
| 892 | 894 | }
|
| 893 | 895 | |
| 894 | 896 | // Allocate a suitably-sized array...
|
| 895 | - const StgWord size = n_threads + mutArrPtrsCardTableSize(n_threads);
|
|
| 896 | - StgMutArrPtrs *arr =
|
|
| 897 | - (StgMutArrPtrs *)allocate(cap, sizeofW(StgMutArrPtrs) + size);
|
|
| 898 | - SET_HDR(arr, &stg_MUT_ARR_PTRS_DIRTY_info, CCS_SYSTEM);
|
|
| 899 | - TICK_ALLOC_PRIM(sizeofW(StgMutArrPtrs), size, 0);
|
|
| 900 | - arr->ptrs = n_threads;
|
|
| 901 | - arr->size = size;
|
|
| 897 | + StgMutArrPtrs *arr = allocateMutArrPtrs(cap, n_threads, cap->r.rCCCS);
|
|
| 898 | + if (RTS_UNLIKELY(arr == NULL)) goto end;
|
|
| 902 | 899 | |
| 903 | 900 | // Populate it...
|
| 904 | 901 | StgWord i = 0;
|
| ... | ... | @@ -913,6 +910,7 @@ StgMutArrPtrs *listThreads(Capability *cap) |
| 913 | 910 | }
|
| 914 | 911 | }
|
| 915 | 912 | CHECKM(i == n_threads, "listThreads: Found too few threads");
|
| 913 | +end:
|
|
| 916 | 914 | RELEASE_LOCK(&sched_mutex);
|
| 917 | 915 | return arr;
|
| 918 | 916 | }
|
| ... | ... | @@ -17,6 +17,7 @@ |
| 17 | 17 | #include "Prelude.h"
|
| 18 | 18 | #include "ThreadLabels.h"
|
| 19 | 19 | #include "Trace.h"
|
| 20 | +#include "AllocArray.h"
|
|
| 20 | 21 | |
| 21 | 22 | // List of dead weak pointers collected by the last GC
|
| 22 | 23 | static StgWeak *finalizer_list = NULL;
|
| ... | ... | @@ -89,8 +90,6 @@ scheduleFinalizers(Capability *cap, StgWeak *list) |
| 89 | 90 | {
|
| 90 | 91 | StgWeak *w;
|
| 91 | 92 | StgTSO *t;
|
| 92 | - StgMutArrPtrs *arr;
|
|
| 93 | - StgWord size;
|
|
| 94 | 93 | uint32_t n, i;
|
| 95 | 94 | |
| 96 | 95 | // n_finalizers is not necessarily zero under non-moving collection
|
| ... | ... | @@ -147,13 +146,10 @@ scheduleFinalizers(Capability *cap, StgWeak *list) |
| 147 | 146 | |
| 148 | 147 | debugTrace(DEBUG_weak, "weak: batching %d finalizers", n);
|
| 149 | 148 | |
| 150 | - size = n + mutArrPtrsCardTableSize(n);
|
|
| 151 | - arr = (StgMutArrPtrs *)allocate(cap, sizeofW(StgMutArrPtrs) + size);
|
|
| 152 | - TICK_ALLOC_PRIM(sizeofW(StgMutArrPtrs), n, 0);
|
|
| 149 | + StgMutArrPtrs *arr = allocateMutArrPtrs(cap, n, CCS_SYSTEM_OR_NULL);
|
|
| 150 | + if (RTS_UNLIKELY(arr == NULL)) exitHeapOverflow();
|
|
| 153 | 151 | // No write barrier needed here; this array is only going to referred to by this core.
|
| 154 | - SET_HDR(arr, &stg_MUT_ARR_PTRS_FROZEN_CLEAN_info, CCS_SYSTEM);
|
|
| 155 | - arr->ptrs = n;
|
|
| 156 | - arr->size = size;
|
|
| 152 | + SET_INFO((StgClosure *) arr, &stg_MUT_ARR_PTRS_FROZEN_CLEAN_info);
|
|
| 157 | 153 | |
| 158 | 154 | n = 0;
|
| 159 | 155 | for (w = list; w; w = w->link) {
|
| ... | ... | @@ -163,6 +159,10 @@ scheduleFinalizers(Capability *cap, StgWeak *list) |
| 163 | 159 | }
|
| 164 | 160 | }
|
| 165 | 161 | // set all the cards to 1
|
| 162 | + StgWord size = n + mutArrPtrsCardTableSize(n);
|
|
| 163 | + // TODO: does this need to be a StgMutArrPtrs with a card table?
|
|
| 164 | + // If the cards are all 1 and the array is clean, couldn't it
|
|
| 165 | + // be a StgSmallMutArrPtrs instead?
|
|
| 166 | 166 | for (i = n; i < size; i++) {
|
| 167 | 167 | arr->payload[i] = (StgClosure *)(W_)(-1);
|
| 168 | 168 | }
|
| ... | ... | @@ -291,6 +291,7 @@ DLL_IMPORT_RTS extern char *prog_name; |
| 291 | 291 | |
| 292 | 292 | void reportStackOverflow(StgTSO* tso);
|
| 293 | 293 | void reportHeapOverflow(void);
|
| 294 | +void exitHeapOverflow(void) STG_NORETURN;;
|
|
| 294 | 295 | |
| 295 | 296 | void stg_exit(int n) STG_NORETURN;
|
| 296 | 297 |
| ... | ... | @@ -220,9 +220,14 @@ extern CostCentre * RTS_VAR(CC_LIST); // registered CC list |
| 220 | 220 | #define CCS_ALLOC(ccs, size) (ccs)->mem_alloc += ((size)-sizeofW(StgProfHeader))
|
| 221 | 221 | #define ENTER_CCS_THUNK(cap,p) cap->r.rCCCS = p->header.prof.ccs
|
| 222 | 222 | |
| 223 | +/* Allow using CCS_SYSTEM somewhat consistently with/without profiling mode */
|
|
| 224 | +#define CCS_SYSTEM_OR_NULL CCS_SYSTEM
|
|
| 225 | + |
|
| 223 | 226 | #else /* !PROFILING */
|
| 224 | 227 | |
| 225 | 228 | #define CCS_ALLOC(ccs, amount) doNothing()
|
| 226 | 229 | #define ENTER_CCS_THUNK(cap,p) doNothing()
|
| 227 | 230 | |
| 231 | +#define CCS_SYSTEM_OR_NULL NULL
|
|
| 232 | + |
|
| 228 | 233 | #endif /* PROFILING */ |
| ... | ... | @@ -170,36 +170,106 @@ void listAllBlocks(ListBlocksCb cb, void *user); |
| 170 | 170 | /* -----------------------------------------------------------------------------
|
| 171 | 171 | Generic allocation
|
| 172 | 172 | |
| 173 | - StgPtr allocate(Capability *cap, W_ n)
|
|
| 174 | - Allocates memory from the nursery in
|
|
| 175 | - the current Capability.
|
|
| 176 | - |
|
| 177 | - StgPtr allocatePinned(Capability *cap, W_ n, W_ alignment, W_ align_off)
|
|
| 178 | - Allocates a chunk of contiguous store
|
|
| 179 | - n words long, which is at a fixed
|
|
| 180 | - address (won't be moved by GC). The
|
|
| 181 | - word at the byte offset 'align_off'
|
|
| 182 | - will be aligned to 'alignment', which
|
|
| 183 | - must be a power of two.
|
|
| 184 | - Returns a pointer to the first word.
|
|
| 185 | - Always succeeds.
|
|
| 186 | - |
|
| 187 | - NOTE: the GC can't in general handle
|
|
| 188 | - pinned objects, so allocatePinned()
|
|
| 189 | - can only be used for ByteArrays at the
|
|
| 190 | - moment.
|
|
| 191 | - |
|
| 192 | - Don't forget to TICK_ALLOC_XXX(...)
|
|
| 193 | - after calling allocate or
|
|
| 194 | - allocatePinned, for the
|
|
| 195 | - benefit of the ticky-ticky profiler.
|
|
| 196 | - |
|
| 173 | + See: Note [allocate and allocateMightFail]
|
|
| 174 | + Note [allocatePinned]
|
|
| 175 | + Note [allocate failure]
|
|
| 197 | 176 | -------------------------------------------------------------------------- */
|
| 198 | 177 | |
| 199 | 178 | StgPtr allocate ( Capability *cap, W_ n );
|
| 200 | 179 | StgPtr allocateMightFail ( Capability *cap, W_ n );
|
| 201 | 180 | StgPtr allocatePinned ( Capability *cap, W_ n, W_ alignment, W_ align_off);
|
| 202 | 181 | |
| 182 | +/* Note [allocate and allocateMightFail]
|
|
| 183 | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
| 184 | + |
|
| 185 | + allocate() and allocateMightFail() allocate an area of memory n
|
|
| 186 | + *words* large, from the nursery of the supplied Capability, or from
|
|
| 187 | + the global block pool if the area requested is larger than
|
|
| 188 | + LARGE_OBJECT_THRESHOLD. Memory is not allocated from the current
|
|
| 189 | + nursery block, so as not to interfere with Hp/HpLim.
|
|
| 190 | + |
|
| 191 | + The address of the allocated memory is returned.
|
|
| 192 | + |
|
| 193 | + After allocating, fill in the heap closure header, e.g.
|
|
| 194 | + SET_HDR(arr, stg_MUT_ARR_PTRS_DIRTY_info, CCCS);
|
|
| 195 | + and call TICK_ALLOC_XXX(...) for the benefit of the ticky-ticky
|
|
| 196 | + profiler.
|
|
| 197 | + |
|
| 198 | + On allocation failure, allocateMightFail() returns NULL whereas
|
|
| 199 | + allocate() terminates the RTS. See Note [allocate failure]. You
|
|
| 200 | + should prefer allocateMightFail() in cases where you can propagate
|
|
| 201 | + the failure up to a context in which you can raise exceptions, e.g.
|
|
| 202 | + in primops.
|
|
| 203 | + */
|
|
| 204 | + |
|
| 205 | +/* Note [allocatePinned]
|
|
| 206 | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
| 207 | + allocatePinned() allocates a chunk of contiguous store n *words*
|
|
| 208 | + long, which is at a fixed address (i.e. won't be moved by GC). The
|
|
| 209 | + word at the byte offset 'align_off' will be aligned to 'alignment',
|
|
| 210 | + which must be a power of two.
|
|
| 211 | + |
|
| 212 | + The address of the allocated memory is returned.
|
|
| 213 | + |
|
| 214 | + The GC can't in general handle pinned objects, so allocatePinned()
|
|
| 215 | + can only be used for ByteArrays / stg_ARR_WORDS at the moment.
|
|
| 216 | + |
|
| 217 | + On allocation failure, allocatePinned() returns NULL.
|
|
| 218 | + See Note [allocate failure].
|
|
| 219 | + */
|
|
| 220 | + |
|
| 221 | +/* Note [allocate failure]
|
|
| 222 | + ~~~~~~~~~~~~~~~~~~~~~~~
|
|
| 223 | + |
|
| 224 | + The allocation functions differ in how they handle failure to
|
|
| 225 | + allocate:
|
|
| 226 | + |
|
| 227 | + * on failure allocateMightFail() returns NULL
|
|
| 228 | + * on failure allocatePinned() returns NULL
|
|
| 229 | + * on failure allocate() terminates the RTS (and thus typically
|
|
| 230 | + the whole process)
|
|
| 231 | + |
|
| 232 | + Each of these functions tries _quite_ hard to avoid allocation
|
|
| 233 | + failure however. If the nursery is already full, then another block
|
|
| 234 | + is allocated from the global block pool. If we need to get memory
|
|
| 235 | + from the OS and that operation fails, or if we would exceed
|
|
| 236 | + maxHeapSize then we fail.
|
|
| 237 | + |
|
| 238 | + There are two main existing conventions within the RTS for handling
|
|
| 239 | + allocation failure.
|
|
| 240 | + |
|
| 241 | + 1. Start from a primop that uses one of the MAYBE_GC_* macros to
|
|
| 242 | + provide an opportunity to GC. Then buried deeply within C code
|
|
| 243 | + called from the primop, use allocate().
|
|
| 244 | + |
|
| 245 | + 2. Start from a primop that uses one of the MAYBE_GC_* macros to
|
|
| 246 | + provide an opportunity to GC. Use allocateMightFail() within the
|
|
| 247 | + C code called from the primop. If that fails, propagate the
|
|
| 248 | + failure up to the primop where it can throw a HeapOverflow
|
|
| 249 | + exception.
|
|
| 250 | + |
|
| 251 | + Being able to throw an exception is preferable, since it's more
|
|
| 252 | + polite, provides better reporting and potentially it can be
|
|
| 253 | + caught and handled by the user program.
|
|
| 254 | + |
|
| 255 | + An advantage of the first approach is that its simpler to implement.
|
|
| 256 | + It does not require any mechanism to propagate failure (and undoing
|
|
| 257 | + any effects along the way so the operation can be safely retried
|
|
| 258 | + after GC).
|
|
| 259 | + |
|
| 260 | + Arguably neither existing convention is ideal. One might imagine
|
|
| 261 | + that when failure from allocateMightFail() propagates to the top
|
|
| 262 | + level primop, the primop would not throw a HeapOverflow exception
|
|
| 263 | + but invoke the GC with a request to make available at least the
|
|
| 264 | + required number of words. The GC may be able to succeed, in which
|
|
| 265 | + case the original operation can be retried. Or if the GC is unable
|
|
| 266 | + to free enough memory then it can throw the HeapOverflow exception.
|
|
| 267 | + In practice however, though there is a mechanism (via HpAlloc) to
|
|
| 268 | + tell the GC how much memory was needed, this is not used to decide
|
|
| 269 | + if we have to fail the allocation, it is just used for error
|
|
| 270 | + reporting.
|
|
| 271 | + */
|
|
| 272 | + |
|
| 203 | 273 | /* memory allocator for executable memory */
|
| 204 | 274 | typedef void* AdjustorWritable;
|
| 205 | 275 | typedef void* AdjustorExecutable;
|
| ... | ... | @@ -10,6 +10,7 @@ |
| 10 | 10 | |
| 11 | 11 | #include "rts/storage/Closures.h"
|
| 12 | 12 | |
| 13 | +/* Returns NULL on allocation failure */
|
|
| 13 | 14 | StgMutArrPtrs *heap_view_closurePtrs(Capability *cap, StgClosure *closure);
|
| 14 | 15 | |
| 15 | 16 | void heap_view_closure_ptrs_in_pap_payload(StgClosure *ptrs[], StgWord *nptrs
|
| ... | ... | @@ -400,6 +400,7 @@ library |
| 400 | 400 | asm-sources: StgCRunAsm.S
|
| 401 | 401 | |
| 402 | 402 | c-sources: Adjustor.c
|
| 403 | + AllocArray.c
|
|
| 403 | 404 | adjustor/AdjustorPool.c
|
| 404 | 405 | ExecPage.c
|
| 405 | 406 | Arena.c
|
| ... | ... | @@ -1065,46 +1065,31 @@ accountAllocation(Capability *cap, W_ n) |
| 1065 | 1065 | * overwriting closures].
|
| 1066 | 1066 | */
|
| 1067 | 1067 | |
| 1068 | -/* -----------------------------------------------------------------------------
|
|
| 1069 | - StgPtr allocate (Capability *cap, W_ n)
|
|
| 1070 | - |
|
| 1071 | - Allocates an area of memory n *words* large, from the nursery of
|
|
| 1072 | - the supplied Capability, or from the global block pool if the area
|
|
| 1073 | - requested is larger than LARGE_OBJECT_THRESHOLD. Memory is not
|
|
| 1074 | - allocated from the current nursery block, so as not to interfere
|
|
| 1075 | - with Hp/HpLim.
|
|
| 1076 | - |
|
| 1077 | - The address of the allocated memory is returned. allocate() never
|
|
| 1078 | - fails; if it returns, the returned value is a valid address. If
|
|
| 1079 | - the nursery is already full, then another block is allocated from
|
|
| 1080 | - the global block pool. If we need to get memory from the OS and
|
|
| 1081 | - that operation fails, then the whole process will be killed.
|
|
| 1082 | - -------------------------------------------------------------------------- */
|
|
| 1083 | - |
|
| 1084 | 1068 | /*
|
| 1085 | - * Allocate some n words of heap memory; terminating
|
|
| 1086 | - * on heap overflow
|
|
| 1069 | + * Allocate some n words of heap memory; terminating on heap overflow.
|
|
| 1070 | + *
|
|
| 1071 | + * See Note [allocate and allocateMightFail].
|
|
| 1087 | 1072 | */
|
| 1088 | 1073 | StgPtr
|
| 1089 | 1074 | allocate (Capability *cap, W_ n)
|
| 1090 | 1075 | {
|
| 1091 | 1076 | StgPtr p = allocateMightFail(cap, n);
|
| 1092 | - if (p == NULL) {
|
|
| 1093 | - reportHeapOverflow();
|
|
| 1094 | - // heapOverflow() doesn't exit (see #2592), but we aren't
|
|
| 1077 | + if (RTS_UNLIKELY(p == NULL)) {
|
|
| 1078 | + // reportHeapOverflow() doesn't exit (see #2592), but we aren't
|
|
| 1095 | 1079 | // in a position to do a clean shutdown here: we
|
| 1096 | 1080 | // either have to allocate the memory or exit now.
|
| 1097 | 1081 | // Allocating the memory would be bad, because the user
|
| 1098 | 1082 | // has requested that we not exceed maxHeapSize, so we
|
| 1099 | 1083 | // just exit.
|
| 1100 | - stg_exit(EXIT_HEAPOVERFLOW);
|
|
| 1084 | + exitHeapOverflow();
|
|
| 1101 | 1085 | }
|
| 1102 | 1086 | return p;
|
| 1103 | 1087 | }
|
| 1104 | 1088 | |
| 1105 | 1089 | /*
|
| 1106 | - * Allocate some n words of heap memory; returning NULL
|
|
| 1107 | - * on heap overflow
|
|
| 1090 | + * Allocate some n words of heap memory; returning NULL on heap overflow.
|
|
| 1091 | + *
|
|
| 1092 | + * See Note [allocate and allocateMightFail].
|
|
| 1108 | 1093 | */
|
| 1109 | 1094 | StgPtr
|
| 1110 | 1095 | allocateMightFail (Capability *cap, W_ n)
|
| ... | ... | @@ -1303,6 +1288,9 @@ start_new_pinned_block(Capability *cap) |
| 1303 | 1288 | /* ---------------------------------------------------------------------------
|
| 1304 | 1289 | Allocate a fixed/pinned object.
|
| 1305 | 1290 | |
| 1291 | + See Note [allocatePinned] for the interface. This describes the
|
|
| 1292 | + implementation.
|
|
| 1293 | + |
|
| 1306 | 1294 | We allocate small pinned objects into a single block, allocating a
|
| 1307 | 1295 | new block when the current one overflows. The block is chained
|
| 1308 | 1296 | onto the large_object_list of generation 0.
|