Skip to content

Commit fe0db7d

Browse files
heap snapshot: add gc roots and gc finalist roots to fix unrooted nodes (#52618)
1 parent 2091058 commit fe0db7d

File tree

4 files changed

+157
-35
lines changed

4 files changed

+157
-35
lines changed

src/gc-heap-snapshot.cpp

Lines changed: 101 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "gc-heap-snapshot.h"
44

55
#include "julia_internal.h"
6+
#include "julia_assert.h"
67
#include "gc.h"
78

89
#include "llvm/ADT/SmallVector.h"
@@ -12,8 +13,11 @@
1213
#include <vector>
1314
#include <string>
1415
#include <sstream>
16+
#include <iostream>
17+
#include <set>
1518

1619
using std::string;
20+
using std::set;
1721
using std::ostringstream;
1822
using std::pair;
1923
using std::make_pair;
@@ -116,6 +120,8 @@ struct HeapSnapshot {
116120
DenseMap<void *, size_t> node_ptr_to_index_map;
117121

118122
size_t num_edges = 0; // For metadata, updated as you add each edge. Needed because edges owned by nodes.
123+
size_t _gc_root_idx = 1; // node index of the GC roots node
124+
size_t _gc_finlist_root_idx = 2; // node index of the GC finlist roots node
119125
};
120126

121127
// global heap snapshot, mutated by garbage collector
@@ -128,13 +134,13 @@ void serialize_heap_snapshot(ios_t *stream, HeapSnapshot &snapshot, char all_one
128134
static inline void _record_gc_edge(const char *edge_type,
129135
jl_value_t *a, jl_value_t *b, size_t name_or_index) JL_NOTSAFEPOINT;
130136
void _record_gc_just_edge(const char *edge_type, Node &from_node, size_t to_idx, size_t name_or_idx) JL_NOTSAFEPOINT;
131-
void _add_internal_root(HeapSnapshot *snapshot);
137+
void _add_synthetic_root_entries(HeapSnapshot *snapshot);
132138

133139

134140
JL_DLLEXPORT void jl_gc_take_heap_snapshot(ios_t *stream, char all_one)
135141
{
136142
HeapSnapshot snapshot;
137-
_add_internal_root(&snapshot);
143+
_add_synthetic_root_entries(&snapshot);
138144

139145
jl_mutex_lock(&heapsnapshot_lock);
140146

@@ -156,10 +162,12 @@ JL_DLLEXPORT void jl_gc_take_heap_snapshot(ios_t *stream, char all_one)
156162
serialize_heap_snapshot((ios_t*)stream, snapshot, all_one);
157163
}
158164

159-
// adds a node at id 0 which is the "uber root":
160-
// a synthetic node which points to all the GC roots.
161-
void _add_internal_root(HeapSnapshot *snapshot)
165+
// mimicking https://github.com/nodejs/node/blob/5fd7a72e1c4fbaf37d3723c4c81dce35c149dc84/deps/v8/src/profiler/heap-snapshot-generator.cc#L212
166+
// add synthetic nodes for the uber root, the GC roots, and the GC finalizer list roots
167+
void _add_synthetic_root_entries(HeapSnapshot *snapshot)
162168
{
169+
// adds a node at id 0 which is the "uber root":
170+
// a synthetic node which points to all the GC roots.
163171
Node internal_root{
164172
snapshot->node_types.find_or_create_string_id("synthetic"),
165173
snapshot->names.find_or_create_string_id(""), // name
@@ -170,6 +178,44 @@ void _add_internal_root(HeapSnapshot *snapshot)
170178
SmallVector<Edge, 0>() // outgoing edges
171179
};
172180
snapshot->nodes.push_back(internal_root);
181+
182+
// Add a node for the GC roots
183+
snapshot->_gc_root_idx = snapshot->nodes.size();
184+
Node gc_roots{
185+
snapshot->node_types.find_or_create_string_id("synthetic"),
186+
snapshot->names.find_or_create_string_id("GC roots"), // name
187+
snapshot->_gc_root_idx, // id
188+
0, // size
189+
0, // size_t trace_node_id (unused)
190+
0, // int detachedness; // 0 - unknown, 1 - attached; 2 - detached
191+
SmallVector<Edge, 0>() // outgoing edges
192+
};
193+
snapshot->nodes.push_back(gc_roots);
194+
snapshot->nodes.front().edges.push_back(Edge{
195+
snapshot->edge_types.find_or_create_string_id("internal"),
196+
snapshot->names.find_or_create_string_id("GC roots"), // edge label
197+
snapshot->_gc_root_idx // to
198+
});
199+
snapshot->num_edges += 1;
200+
201+
// add a node for the gc finalizer list roots
202+
snapshot->_gc_finlist_root_idx = snapshot->nodes.size();
203+
Node gc_finlist_roots{
204+
snapshot->node_types.find_or_create_string_id("synthetic"),
205+
snapshot->names.find_or_create_string_id("GC finalizer list roots"), // name
206+
snapshot->_gc_finlist_root_idx, // id
207+
0, // size
208+
0, // size_t trace_node_id (unused)
209+
0, // int detachedness; // 0 - unknown, 1 - attached; 2 - detached
210+
SmallVector<Edge, 0>() // outgoing edges
211+
};
212+
snapshot->nodes.push_back(gc_finlist_roots);
213+
snapshot->nodes.front().edges.push_back(Edge{
214+
snapshot->edge_types.find_or_create_string_id("internal"),
215+
snapshot->names.find_or_create_string_id("GC finlist roots"), // edge label
216+
snapshot->_gc_finlist_root_idx // to
217+
});
218+
snapshot->num_edges += 1;
173219
}
174220

175221
// mimicking https://github.com/nodejs/node/blob/5fd7a72e1c4fbaf37d3723c4c81dce35c149dc84/deps/v8/src/profiler/heap-snapshot-generator.cc#L597-L597
@@ -327,6 +373,26 @@ void _gc_heap_snapshot_record_root(jl_value_t *root, char *name) JL_NOTSAFEPOINT
327373
_record_gc_just_edge("internal", internal_root, to_node_idx, edge_label);
328374
}
329375

376+
void _gc_heap_snapshot_record_gc_roots(jl_value_t *root, char *name) JL_NOTSAFEPOINT
377+
{
378+
record_node_to_gc_snapshot(root);
379+
380+
auto from_node_idx = g_snapshot->_gc_root_idx;
381+
auto to_node_idx = record_node_to_gc_snapshot(root);
382+
auto edge_label = g_snapshot->names.find_or_create_string_id(name);
383+
_record_gc_just_edge("internal", g_snapshot->nodes[from_node_idx], to_node_idx, edge_label);
384+
}
385+
386+
void _gc_heap_snapshot_record_finlist(jl_value_t *obj, size_t index) JL_NOTSAFEPOINT
387+
{
388+
auto from_node_idx = g_snapshot->_gc_finlist_root_idx;
389+
auto to_node_idx = record_node_to_gc_snapshot(obj);
390+
ostringstream ss;
391+
ss << "finlist-" << index;
392+
auto edge_label = g_snapshot->names.find_or_create_string_id(ss.str());
393+
_record_gc_just_edge("internal", g_snapshot->nodes[from_node_idx], to_node_idx, edge_label);
394+
}
395+
330396
// Add a node to the heap snapshot representing a Julia stack frame.
331397
// Each task points at a stack frame, which points at the stack frame of
332398
// the function it's currently calling, forming a linked list.
@@ -393,27 +459,19 @@ void _gc_heap_snapshot_record_object_edge(jl_value_t *from, jl_value_t *to, void
393459
g_snapshot->names.find_or_create_string_id(path));
394460
}
395461

396-
void _gc_heap_snapshot_record_module_to_binding(jl_module_t *module, jl_binding_t *binding) JL_NOTSAFEPOINT
462+
void _gc_heap_snapshot_record_module_to_binding(jl_module_t *module, jl_value_t *bindings, jl_value_t *bindingkeyset) JL_NOTSAFEPOINT
397463
{
398-
jl_globalref_t *globalref = binding->globalref;
399-
jl_sym_t *name = globalref->name;
400464
auto from_node_idx = record_node_to_gc_snapshot((jl_value_t*)module);
401-
auto to_node_idx = record_pointer_to_gc_snapshot(binding, sizeof(jl_binding_t), jl_symbol_name(name));
402-
403-
jl_value_t *value = jl_atomic_load_relaxed(&binding->value);
404-
auto value_idx = value ? record_node_to_gc_snapshot(value) : 0;
405-
jl_value_t *ty = jl_atomic_load_relaxed(&binding->ty);
406-
auto ty_idx = ty ? record_node_to_gc_snapshot(ty) : 0;
407-
auto globalref_idx = record_node_to_gc_snapshot((jl_value_t*)globalref);
408-
465+
auto to_bindings_idx = record_node_to_gc_snapshot(bindings);
466+
auto to_bindingkeyset_idx = record_node_to_gc_snapshot(bindingkeyset);
409467
auto &from_node = g_snapshot->nodes[from_node_idx];
410-
auto &to_node = g_snapshot->nodes[to_node_idx];
411-
412-
_record_gc_just_edge("property", from_node, to_node_idx, g_snapshot->names.find_or_create_string_id("<native>"));
413-
if (value_idx) _record_gc_just_edge("internal", to_node, value_idx, g_snapshot->names.find_or_create_string_id("value"));
414-
if (ty_idx) _record_gc_just_edge("internal", to_node, ty_idx, g_snapshot->names.find_or_create_string_id("ty"));
415-
if (globalref_idx) _record_gc_just_edge("internal", to_node, globalref_idx, g_snapshot->names.find_or_create_string_id("globalref"));
416-
}
468+
if (to_bindings_idx > 0) {
469+
_record_gc_just_edge("internal", from_node, to_bindings_idx, g_snapshot->names.find_or_create_string_id("bindings"));
470+
}
471+
if (to_bindingkeyset_idx > 0) {
472+
_record_gc_just_edge("internal", from_node, to_bindingkeyset_idx, g_snapshot->names.find_or_create_string_id("bindingkeyset"));
473+
}
474+
}
417475

418476
void _gc_heap_snapshot_record_internal_array_edge(jl_value_t *from, jl_value_t *to) JL_NOTSAFEPOINT
419477
{
@@ -492,6 +550,8 @@ void serialize_heap_snapshot(ios_t *stream, HeapSnapshot &snapshot, char all_one
492550

493551
ios_printf(stream, "\"nodes\":[");
494552
bool first_node = true;
553+
// use a set to track the nodes that do not have parents
554+
set<size_t> orphans;
495555
for (const auto &from_node : snapshot.nodes) {
496556
if (first_node) {
497557
first_node = false;
@@ -508,6 +568,14 @@ void serialize_heap_snapshot(ios_t *stream, HeapSnapshot &snapshot, char all_one
508568
from_node.edges.size(),
509569
from_node.trace_node_id,
510570
from_node.detachedness);
571+
if (from_node.id != snapshot._gc_root_idx && from_node.id != snapshot._gc_finlist_root_idx) {
572+
// find the node index from the node object pointer
573+
void * ptr = (void*)from_node.id;
574+
size_t n_id = snapshot.node_ptr_to_index_map[ptr];
575+
orphans.insert(n_id);
576+
} else {
577+
orphans.insert(from_node.id);
578+
}
511579
}
512580
ios_printf(stream, "],\n");
513581

@@ -525,6 +593,12 @@ void serialize_heap_snapshot(ios_t *stream, HeapSnapshot &snapshot, char all_one
525593
edge.type,
526594
edge.name_or_index,
527595
edge.to_node * k_node_number_of_fields);
596+
auto n_id = edge.to_node;
597+
auto it = orphans.find(n_id);
598+
if (it != orphans.end()) {
599+
// remove the node from the orphans if it has at least one incoming edge
600+
orphans.erase(it);
601+
}
528602
}
529603
}
530604
ios_printf(stream, "],\n"); // end "edges"
@@ -534,4 +608,8 @@ void serialize_heap_snapshot(ios_t *stream, HeapSnapshot &snapshot, char all_one
534608
snapshot.names.print_json_array(stream, true);
535609

536610
ios_printf(stream, "}");
611+
612+
// remove the uber node from the orphans
613+
orphans.erase(0);
614+
assert(orphans.size() == 0 && "all nodes except the uber node should have at least one incoming edge");
537615
}

src/gc-heap-snapshot.h

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,18 @@ void _gc_heap_snapshot_record_task_to_frame_edge(jl_task_t *from, void *to) JL_N
2020
void _gc_heap_snapshot_record_frame_to_frame_edge(jl_gcframe_t *from, jl_gcframe_t *to) JL_NOTSAFEPOINT;
2121
void _gc_heap_snapshot_record_array_edge(jl_value_t *from, jl_value_t *to, size_t index) JL_NOTSAFEPOINT;
2222
void _gc_heap_snapshot_record_object_edge(jl_value_t *from, jl_value_t *to, void* slot) JL_NOTSAFEPOINT;
23-
void _gc_heap_snapshot_record_module_to_binding(jl_module_t* module, jl_binding_t* binding) JL_NOTSAFEPOINT;
23+
void _gc_heap_snapshot_record_module_to_binding(jl_module_t* module, jl_value_t *bindings, jl_value_t *bindingkeyset) JL_NOTSAFEPOINT;
2424
// Used for objects managed by GC, but which aren't exposed in the julia object, so have no
2525
// field or index. i.e. they're not reachable from julia code, but we _will_ hit them in
2626
// the GC mark phase (so we can check their type tag to get the size).
2727
void _gc_heap_snapshot_record_internal_array_edge(jl_value_t *from, jl_value_t *to) JL_NOTSAFEPOINT;
2828
// Used for objects manually allocated in C (outside julia GC), to still tell the heap snapshot about the
2929
// size of the object, even though we're never going to mark that object.
3030
void _gc_heap_snapshot_record_hidden_edge(jl_value_t *from, void* to, size_t bytes, uint16_t alloc_type) JL_NOTSAFEPOINT;
31-
31+
// Used for objects that are reachable from the GC roots
32+
void _gc_heap_snapshot_record_gc_roots(jl_value_t *root, char *name) JL_NOTSAFEPOINT;
33+
// Used for objects that are reachable from the finalizer list
34+
void _gc_heap_snapshot_record_finlist(jl_value_t *finlist, size_t index) JL_NOTSAFEPOINT;
3235

3336
extern int gc_heap_snapshot_enabled;
3437
extern int prev_sweep_full;
@@ -60,6 +63,12 @@ static inline void gc_heap_snapshot_record_root(jl_value_t *root, char *name) JL
6063
_gc_heap_snapshot_record_root(root, name);
6164
}
6265
}
66+
static inline void gc_heap_snapshot_record_array_edge_index(jl_value_t *from, jl_value_t *to, size_t index) JL_NOTSAFEPOINT
67+
{
68+
if (__unlikely(gc_heap_snapshot_enabled && prev_sweep_full && from != NULL && to != NULL)) {
69+
_gc_heap_snapshot_record_array_edge(from, to, index);
70+
}
71+
}
6372
static inline void gc_heap_snapshot_record_array_edge(jl_value_t *from, jl_value_t **to) JL_NOTSAFEPOINT
6473
{
6574
if (__unlikely(gc_heap_snapshot_enabled && prev_sweep_full)) {
@@ -73,10 +82,10 @@ static inline void gc_heap_snapshot_record_object_edge(jl_value_t *from, jl_valu
7382
}
7483
}
7584

76-
static inline void gc_heap_snapshot_record_module_to_binding(jl_module_t* module, jl_binding_t* binding) JL_NOTSAFEPOINT
85+
static inline void gc_heap_snapshot_record_module_to_binding(jl_module_t* module, jl_value_t *bindings, jl_value_t *bindingkeyset) JL_NOTSAFEPOINT
7786
{
78-
if (__unlikely(gc_heap_snapshot_enabled && prev_sweep_full)) {
79-
_gc_heap_snapshot_record_module_to_binding(module, binding);
87+
if (__unlikely(gc_heap_snapshot_enabled && prev_sweep_full) && bindings != NULL && bindingkeyset != NULL) {
88+
_gc_heap_snapshot_record_module_to_binding(module, bindings, bindingkeyset);
8089
}
8190
}
8291

@@ -94,6 +103,20 @@ static inline void gc_heap_snapshot_record_hidden_edge(jl_value_t *from, void* t
94103
}
95104
}
96105

106+
static inline void gc_heap_snapshot_record_gc_roots(jl_value_t *root, char *name) JL_NOTSAFEPOINT
107+
{
108+
if (__unlikely(gc_heap_snapshot_enabled && prev_sweep_full && root != NULL)) {
109+
_gc_heap_snapshot_record_gc_roots(root, name);
110+
}
111+
}
112+
113+
static inline void gc_heap_snapshot_record_finlist(jl_value_t *finlist, size_t index) JL_NOTSAFEPOINT
114+
{
115+
if (__unlikely(gc_heap_snapshot_enabled && prev_sweep_full && finlist != NULL)) {
116+
_gc_heap_snapshot_record_finlist(finlist, index);
117+
}
118+
}
119+
97120
// ---------------------------------------------------------------------
98121
// Functions to call from Julia to take heap snapshot
99122
// ---------------------------------------------------------------------

src/gc.c

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2358,9 +2358,10 @@ STATIC_INLINE void gc_mark_chunk(jl_ptls_t ptls, jl_gc_markqueue_t *mq, jl_gc_ch
23582358
break;
23592359
}
23602360
case GC_finlist_chunk: {
2361+
jl_value_t *fl_parent = c->parent;
23612362
jl_value_t **fl_begin = c->begin;
23622363
jl_value_t **fl_end = c->end;
2363-
gc_mark_finlist_(mq, fl_begin, fl_end);
2364+
gc_mark_finlist_(mq, fl_parent, fl_begin, fl_end);
23642365
break;
23652366
}
23662367
default: {
@@ -2458,6 +2459,7 @@ STATIC_INLINE void gc_mark_module_binding(jl_ptls_t ptls, jl_module_t *mb_parent
24582459
jl_value_t *bindingkeyset = (jl_value_t *)jl_atomic_load_relaxed(&mb_parent->bindingkeyset);
24592460
gc_assert_parent_validity((jl_value_t *)mb_parent, bindingkeyset);
24602461
gc_try_claim_and_push(mq, bindingkeyset, &nptr);
2462+
gc_heap_snapshot_record_module_to_binding(mb_parent, bindings, bindingkeyset);
24612463
gc_assert_parent_validity((jl_value_t *)mb_parent, (jl_value_t *)mb_parent->parent);
24622464
gc_try_claim_and_push(mq, (jl_value_t *)mb_parent->parent, &nptr);
24632465
size_t nusings = mb_parent->usings.len;
@@ -2476,7 +2478,7 @@ STATIC_INLINE void gc_mark_module_binding(jl_ptls_t ptls, jl_module_t *mb_parent
24762478
}
24772479
}
24782480

2479-
void gc_mark_finlist_(jl_gc_markqueue_t *mq, jl_value_t **fl_begin, jl_value_t **fl_end)
2481+
void gc_mark_finlist_(jl_gc_markqueue_t *mq, jl_value_t *fl_parent, jl_value_t **fl_begin, jl_value_t **fl_end)
24802482
{
24812483
jl_value_t *new_obj;
24822484
// Decide whether need to chunk finlist
@@ -2486,8 +2488,10 @@ void gc_mark_finlist_(jl_gc_markqueue_t *mq, jl_value_t **fl_begin, jl_value_t *
24862488
gc_chunkqueue_push(mq, &c);
24872489
fl_end = fl_begin + GC_CHUNK_BATCH_SIZE;
24882490
}
2491+
size_t i = 0;
24892492
for (; fl_begin < fl_end; fl_begin++) {
2490-
new_obj = *fl_begin;
2493+
jl_value_t **slot = fl_begin;
2494+
new_obj = *slot;
24912495
if (__unlikely(new_obj == NULL))
24922496
continue;
24932497
if (gc_ptr_tag(new_obj, 1)) {
@@ -2498,6 +2502,13 @@ void gc_mark_finlist_(jl_gc_markqueue_t *mq, jl_value_t **fl_begin, jl_value_t *
24982502
if (gc_ptr_tag(new_obj, 2))
24992503
continue;
25002504
gc_try_claim_and_push(mq, new_obj, NULL);
2505+
if (fl_parent != NULL) {
2506+
gc_heap_snapshot_record_array_edge(fl_parent, slot);
2507+
} else {
2508+
// This is a list of objects following the same format as a finlist
2509+
// if `fl_parent` is NULL
2510+
gc_heap_snapshot_record_finlist(new_obj, ++i);
2511+
}
25012512
}
25022513
}
25032514

@@ -2509,7 +2520,7 @@ void gc_mark_finlist(jl_gc_markqueue_t *mq, arraylist_t *list, size_t start)
25092520
return;
25102521
jl_value_t **fl_begin = (jl_value_t **)list->items + start;
25112522
jl_value_t **fl_end = (jl_value_t **)list->items + len;
2512-
gc_mark_finlist_(mq, fl_begin, fl_end);
2523+
gc_mark_finlist_(mq, NULL, fl_begin, fl_end);
25132524
}
25142525

25152526
JL_DLLEXPORT int jl_gc_mark_queue_obj(jl_ptls_t ptls, jl_value_t *obj)
@@ -3158,28 +3169,38 @@ static void gc_mark_roots(jl_gc_markqueue_t *mq)
31583169
{
31593170
// modules
31603171
gc_try_claim_and_push(mq, jl_main_module, NULL);
3161-
gc_heap_snapshot_record_root((jl_value_t*)jl_main_module, "main_module");
3172+
gc_heap_snapshot_record_gc_roots((jl_value_t*)jl_main_module, "main_module");
31623173
// invisible builtin values
31633174
gc_try_claim_and_push(mq, jl_an_empty_vec_any, NULL);
3175+
gc_heap_snapshot_record_gc_roots((jl_value_t*)jl_an_empty_vec_any, "an_empty_vec_any");
31643176
gc_try_claim_and_push(mq, jl_module_init_order, NULL);
3177+
gc_heap_snapshot_record_gc_roots((jl_value_t*)jl_module_init_order, "module_init_order");
31653178
for (size_t i = 0; i < jl_current_modules.size; i += 2) {
31663179
if (jl_current_modules.table[i + 1] != HT_NOTFOUND) {
31673180
gc_try_claim_and_push(mq, jl_current_modules.table[i], NULL);
3168-
gc_heap_snapshot_record_root((jl_value_t*)jl_current_modules.table[i], "top level module");
3181+
gc_heap_snapshot_record_gc_roots((jl_value_t*)jl_current_modules.table[i], "top level module");
31693182
}
31703183
}
31713184
gc_try_claim_and_push(mq, jl_anytuple_type_type, NULL);
3185+
gc_heap_snapshot_record_gc_roots((jl_value_t*)jl_anytuple_type_type, "anytuple_type_type");
31723186
for (size_t i = 0; i < N_CALL_CACHE; i++) {
31733187
jl_typemap_entry_t *v = jl_atomic_load_relaxed(&call_cache[i]);
31743188
gc_try_claim_and_push(mq, v, NULL);
3189+
gc_heap_snapshot_record_array_edge_index((jl_value_t*)jl_anytuple_type_type, (jl_value_t*)v, i);
31753190
}
31763191
gc_try_claim_and_push(mq, jl_all_methods, NULL);
3192+
gc_heap_snapshot_record_gc_roots((jl_value_t*)jl_all_methods, "all_methods");
31773193
gc_try_claim_and_push(mq, _jl_debug_method_invalidation, NULL);
3194+
gc_heap_snapshot_record_gc_roots((jl_value_t*)_jl_debug_method_invalidation, "debug_method_invalidation");
31783195
// constants
31793196
gc_try_claim_and_push(mq, jl_emptytuple_type, NULL);
3197+
gc_heap_snapshot_record_gc_roots((jl_value_t*)jl_emptytuple_type, "emptytuple_type");
31803198
gc_try_claim_and_push(mq, cmpswap_names, NULL);
3199+
gc_heap_snapshot_record_gc_roots((jl_value_t*)cmpswap_names, "cmpswap_names");
31813200
gc_try_claim_and_push(mq, jl_global_roots_list, NULL);
3201+
gc_heap_snapshot_record_gc_roots((jl_value_t*)jl_global_roots_list, "global_roots_list");
31823202
gc_try_claim_and_push(mq, jl_global_roots_keyset, NULL);
3203+
gc_heap_snapshot_record_gc_roots((jl_value_t*)jl_global_roots_keyset, "global_roots_keyset");
31833204
}
31843205

31853206
// find unmarked objects that need to be finalized from the finalizer list "list".

src/gc.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -468,7 +468,7 @@ extern uv_sem_t gc_sweep_assists_needed;
468468
extern _Atomic(int) gc_n_threads_marking;
469469
extern _Atomic(int) gc_n_threads_sweeping;
470470
void gc_mark_queue_all_roots(jl_ptls_t ptls, jl_gc_markqueue_t *mq);
471-
void gc_mark_finlist_(jl_gc_markqueue_t *mq, jl_value_t **fl_begin, jl_value_t **fl_end) JL_NOTSAFEPOINT;
471+
void gc_mark_finlist_(jl_gc_markqueue_t *mq, jl_value_t *fl_parent, jl_value_t **fl_begin, jl_value_t **fl_end) JL_NOTSAFEPOINT;
472472
void gc_mark_finlist(jl_gc_markqueue_t *mq, arraylist_t *list, size_t start) JL_NOTSAFEPOINT;
473473
void gc_mark_loop_serial_(jl_ptls_t ptls, jl_gc_markqueue_t *mq);
474474
void gc_mark_loop_serial(jl_ptls_t ptls);

0 commit comments

Comments
 (0)