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.
|