Skip to content

Commit 87b8896

Browse files
authored
remove precompile mutation step from staticdata (#48309)
Make sure things are properly ordered here, so that when serializing, nothing is mutating the system at the same time. Fix #48047
1 parent 1c5fa2b commit 87b8896

File tree

4 files changed

+90
-104
lines changed

4 files changed

+90
-104
lines changed

src/gf.c

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -281,11 +281,9 @@ jl_code_info_t *jl_type_infer(jl_method_instance_t *mi, size_t world, int force)
281281
return NULL;
282282
jl_task_t *ct = jl_current_task;
283283
if (ct->reentrant_inference == (uint16_t)-1) {
284-
// TODO: We should avoid attempting to re-inter inference here at all
285-
// and turn on this warning, but that requires further refactoring
286-
// of the precompile code, so for now just catch that case here.
287-
//jl_printf(JL_STDERR, "ERROR: Attempted to enter inference while writing out image.");
288-
return NULL;
284+
// We must avoid attempting to re-enter inference here
285+
assert(0 && "attempted to enter inference while writing out image");
286+
abort();
289287
}
290288
if (ct->reentrant_inference > 2)
291289
return NULL;
@@ -487,6 +485,7 @@ int foreach_mtable_in_module(
487485
// this is the original/primary binding for the type (name/wrapper)
488486
jl_methtable_t *mt = tn->mt;
489487
if (mt != NULL && (jl_value_t*)mt != jl_nothing && mt != jl_type_type_mt && mt != jl_nonfunction_mt) {
488+
assert(mt->module == m);
490489
if (!visit(mt, env))
491490
return 0;
492491
}
@@ -500,6 +499,15 @@ int foreach_mtable_in_module(
500499
return 0;
501500
}
502501
}
502+
else if (jl_is_mtable(v)) {
503+
jl_methtable_t *mt = (jl_methtable_t*)v;
504+
if (mt->module == m && mt->name == name) {
505+
// this is probably an external method table here, so let's
506+
// assume so as there is no way to precisely distinguish them
507+
if (!visit(mt, env))
508+
return 0;
509+
}
510+
}
503511
}
504512
}
505513
table = jl_atomic_load_relaxed(&m->bindings);

src/precompile_utils.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,8 @@ static int compile_all_collect__(jl_typemap_entry_t *ml, void *env)
132132
{
133133
jl_array_t *allmeths = (jl_array_t*)env;
134134
jl_method_t *m = ml->func.method;
135+
if (m->external_mt)
136+
return 1;
135137
if (m->source) {
136138
// method has a non-generated definition; can be compiled generically
137139
jl_array_ptr_1d_push(allmeths, (jl_value_t*)m);
@@ -204,6 +206,8 @@ static int precompile_enq_specialization_(jl_method_instance_t *mi, void *closur
204206
static int precompile_enq_all_specializations__(jl_typemap_entry_t *def, void *closure)
205207
{
206208
jl_method_t *m = def->func.method;
209+
if (m->external_mt)
210+
return 1;
207211
if ((m->name == jl_symbol("__init__") || m->ccallable) && jl_is_dispatch_tupletype(m->sig)) {
208212
// ensure `__init__()` and @ccallables get strongly-hinted, specialized, and compiled
209213
jl_method_instance_t *mi = jl_specializations_get_linfo(m, m->sig, jl_emptysvec);

src/staticdata.c

Lines changed: 61 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -2170,50 +2170,52 @@ JL_DLLEXPORT jl_value_t *jl_as_global_root(jl_value_t *val JL_MAYBE_UNROOTED)
21702170
}
21712171

21722172
static void jl_prepare_serialization_data(jl_array_t *mod_array, jl_array_t *newly_inferred, uint64_t worklist_key,
2173-
/* outputs */ jl_array_t **extext_methods,
2174-
jl_array_t **new_specializations, jl_array_t **method_roots_list,
2175-
jl_array_t **ext_targets, jl_array_t **edges)
2173+
/* outputs */ jl_array_t **extext_methods, jl_array_t **new_specializations,
2174+
jl_array_t **method_roots_list, jl_array_t **ext_targets, jl_array_t **edges)
21762175
{
21772176
// extext_methods: [method1, ...], worklist-owned "extending external" methods added to functions owned by modules outside the worklist
21782177
// ext_targets: [invokesig1, callee1, matches1, ...] non-worklist callees of worklist-owned methods
21792178
// ordinary dispatch: invokesig=NULL, callee is MethodInstance
21802179
// `invoke` dispatch: invokesig is signature, callee is MethodInstance
21812180
// abstract call: callee is signature
21822181
// edges: [caller1, ext_targets_indexes1, ...] for worklist-owned methods calling external methods
2183-
21842182
assert(edges_map == NULL);
2185-
JL_GC_PUSH1(&edges_map);
21862183

2187-
// Save the inferred code from newly inferred, external methods
21882184
htable_new(&external_mis, 0); // we need external_mis until after `jl_collect_edges` finishes
2185+
// Save the inferred code from newly inferred, external methods
21892186
*new_specializations = queue_external_cis(newly_inferred);
2190-
// Collect the new method roots
2191-
htable_t methods_with_newspecs;
2192-
htable_new(&methods_with_newspecs, 0);
2193-
jl_collect_methods(&methods_with_newspecs, *new_specializations);
2194-
*method_roots_list = jl_alloc_vec_any(0);
2195-
jl_collect_new_roots(*method_roots_list, &methods_with_newspecs, worklist_key);
2196-
htable_free(&methods_with_newspecs);
21972187

21982188
// Collect method extensions and edges data
2199-
edges_map = jl_alloc_vec_any(0);
2189+
JL_GC_PUSH1(&edges_map);
2190+
if (edges)
2191+
edges_map = jl_alloc_vec_any(0);
22002192
*extext_methods = jl_alloc_vec_any(0);
2193+
jl_collect_methtable_from_mod(jl_type_type_mt, *extext_methods);
2194+
jl_collect_methtable_from_mod(jl_nonfunction_mt, *extext_methods);
22012195
size_t i, len = jl_array_len(mod_array);
22022196
for (i = 0; i < len; i++) {
22032197
jl_module_t *m = (jl_module_t*)jl_array_ptr_ref(mod_array, i);
22042198
assert(jl_is_module(m));
22052199
if (m->parent == m) // some toplevel modules (really just Base) aren't actually
22062200
jl_collect_extext_methods_from_mod(*extext_methods, m);
22072201
}
2208-
jl_collect_methtable_from_mod(*extext_methods, jl_type_type_mt);
2209-
jl_collect_missing_backedges(jl_type_type_mt);
2210-
jl_collect_methtable_from_mod(*extext_methods, jl_nonfunction_mt);
2211-
jl_collect_missing_backedges(jl_nonfunction_mt);
2212-
// jl_collect_extext_methods_from_mod and jl_collect_missing_backedges also accumulate data in callers_with_edges.
2213-
// Process this to extract `edges` and `ext_targets`.
2214-
*ext_targets = jl_alloc_vec_any(0);
2215-
*edges = jl_alloc_vec_any(0);
2216-
jl_collect_edges(*edges, *ext_targets);
2202+
2203+
if (edges) {
2204+
jl_collect_missing_backedges(jl_type_type_mt);
2205+
jl_collect_missing_backedges(jl_nonfunction_mt);
2206+
// jl_collect_extext_methods_from_mod and jl_collect_missing_backedges also accumulate data in callers_with_edges.
2207+
// Process this to extract `edges` and `ext_targets`.
2208+
*ext_targets = jl_alloc_vec_any(0);
2209+
*edges = jl_alloc_vec_any(0);
2210+
*method_roots_list = jl_alloc_vec_any(0);
2211+
// Collect the new method roots
2212+
htable_t methods_with_newspecs;
2213+
htable_new(&methods_with_newspecs, 0);
2214+
jl_collect_methods(&methods_with_newspecs, *new_specializations);
2215+
jl_collect_new_roots(*method_roots_list, &methods_with_newspecs, worklist_key);
2216+
htable_free(&methods_with_newspecs);
2217+
jl_collect_edges(*edges, *ext_targets);
2218+
}
22172219
htable_free(&external_mis);
22182220
assert(edges_map == NULL); // jl_collect_edges clears this when done
22192221

@@ -2501,9 +2503,8 @@ static void jl_save_system_image_to_stream(ios_t *f,
25012503
jl_gc_enable(en);
25022504
}
25032505

2504-
static void jl_write_header_for_incremental(ios_t *f, jl_array_t *worklist, jl_array_t **mod_array, jl_array_t **udeps, int64_t *srctextpos, int64_t *checksumpos)
2506+
static void jl_write_header_for_incremental(ios_t *f, jl_array_t *worklist, jl_array_t *mod_array, jl_array_t **udeps, int64_t *srctextpos, int64_t *checksumpos)
25052507
{
2506-
*mod_array = jl_get_loaded_modules(); // __toplevel__ modules loaded in this session (from Base.loaded_modules_array)
25072508
assert(jl_precompile_toplevel_module == NULL);
25082509
jl_precompile_toplevel_module = (jl_module_t*)jl_array_ptr_ref(worklist, jl_array_len(worklist)-1);
25092510

@@ -2519,7 +2520,7 @@ static void jl_write_header_for_incremental(ios_t *f, jl_array_t *worklist, jl_a
25192520
// write description of requirements for loading (modules that must be pre-loaded if initialization is to succeed)
25202521
// this can return errors during deserialize,
25212522
// best to keep it early (before any actual initialization)
2522-
write_mod_list(f, *mod_array);
2523+
write_mod_list(f, mod_array);
25232524
}
25242525

25252526
JL_DLLEXPORT void jl_create_system_image(void **_native_data, jl_array_t *worklist, bool_t emit_split,
@@ -2550,49 +2551,58 @@ JL_DLLEXPORT void jl_create_system_image(void **_native_data, jl_array_t *workli
25502551
int64_t checksumpos_ff = 0;
25512552
int64_t datastartpos = 0;
25522553
JL_GC_PUSH6(&mod_array, &extext_methods, &new_specializations, &method_roots_list, &ext_targets, &edges);
2553-
if (worklist) {
2554-
jl_write_header_for_incremental(f, worklist, &mod_array, udeps, srctextpos, &checksumpos);
2555-
if (emit_split) {
2556-
checksumpos_ff = write_header(ff, 1);
2557-
write_uint8(ff, jl_cache_flags());
2558-
write_mod_list(ff, mod_array);
2559-
} else {
2560-
checksumpos_ff = checksumpos;
2561-
}
2562-
{
2563-
// make sure we don't run any Julia code concurrently after this point
2564-
jl_gc_enable_finalizers(ct, 0);
2565-
assert(ct->reentrant_inference == 0);
2566-
ct->reentrant_inference = (uint16_t)-1;
2567-
}
2568-
jl_prepare_serialization_data(mod_array, newly_inferred, jl_worklist_key(worklist), &extext_methods, &new_specializations, &method_roots_list, &ext_targets, &edges);
25692554

2555+
if (worklist) {
2556+
mod_array = jl_get_loaded_modules(); // __toplevel__ modules loaded in this session (from Base.loaded_modules_array)
25702557
// Generate _native_data`
25712558
if (jl_options.outputo || jl_options.outputbc || jl_options.outputunoptbc || jl_options.outputasm) {
2559+
jl_prepare_serialization_data(mod_array, newly_inferred, jl_worklist_key(worklist),
2560+
&extext_methods, &new_specializations, NULL, NULL, NULL);
25722561
jl_precompile_toplevel_module = (jl_module_t*)jl_array_ptr_ref(worklist, jl_array_len(worklist)-1);
25732562
*_native_data = jl_precompile_worklist(worklist, extext_methods, new_specializations);
25742563
jl_precompile_toplevel_module = NULL;
2564+
extext_methods = NULL;
2565+
new_specializations = NULL;
25752566
}
2567+
jl_write_header_for_incremental(f, worklist, mod_array, udeps, srctextpos, &checksumpos);
2568+
if (emit_split) {
2569+
checksumpos_ff = write_header(ff, 1);
2570+
write_uint8(ff, jl_cache_flags());
2571+
write_mod_list(ff, mod_array);
2572+
}
2573+
else {
2574+
checksumpos_ff = checksumpos;
2575+
}
2576+
}
2577+
else {
2578+
*_native_data = jl_precompile(jl_options.compile_enabled == JL_OPTIONS_COMPILE_ALL);
2579+
}
25762580

2581+
// Make sure we don't run any Julia code concurrently after this point
2582+
// since it will invalidate our serialization preparations
2583+
jl_gc_enable_finalizers(ct, 0);
2584+
assert(ct->reentrant_inference == 0);
2585+
ct->reentrant_inference = (uint16_t)-1;
2586+
if (worklist) {
2587+
jl_prepare_serialization_data(mod_array, newly_inferred, jl_worklist_key(worklist),
2588+
&extext_methods, &new_specializations, &method_roots_list, &ext_targets, &edges);
25772589
if (!emit_split) {
25782590
write_int32(f, 0); // No clone_targets
25792591
write_padding(f, LLT_ALIGN(ios_pos(f), JL_CACHE_BYTE_ALIGNMENT) - ios_pos(f));
2580-
} else {
2592+
}
2593+
else {
25812594
write_padding(ff, LLT_ALIGN(ios_pos(ff), JL_CACHE_BYTE_ALIGNMENT) - ios_pos(ff));
25822595
}
25832596
datastartpos = ios_pos(ff);
2584-
} else {
2585-
*_native_data = jl_precompile(jl_options.compile_enabled == JL_OPTIONS_COMPILE_ALL);
25862597
}
25872598
native_functions = *_native_data;
25882599
jl_save_system_image_to_stream(ff, worklist, extext_methods, new_specializations, method_roots_list, ext_targets, edges);
25892600
native_functions = NULL;
2590-
if (worklist) {
2591-
// Re-enable running julia code for postoutput hooks, atexit, etc.
2592-
jl_gc_enable_finalizers(ct, 1);
2593-
ct->reentrant_inference = 0;
2594-
jl_precompile_toplevel_module = NULL;
2595-
}
2601+
// make sure we don't run any Julia code concurrently before this point
2602+
// Re-enable running julia code for postoutput hooks, atexit, etc.
2603+
jl_gc_enable_finalizers(ct, 1);
2604+
ct->reentrant_inference = 0;
2605+
jl_precompile_toplevel_module = NULL;
25962606

25972607
if (worklist) {
25982608
// Go back and update the checksum in the header

src/staticdata_utils.c

Lines changed: 12 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -273,12 +273,12 @@ static void jl_collect_methods(htable_t *mset, jl_array_t *new_specializations)
273273
}
274274
}
275275

276-
static void jl_collect_new_roots(jl_array_t *roots, htable_t *mset, uint64_t key)
276+
static void jl_collect_new_roots(jl_array_t *roots, const htable_t *mset, uint64_t key)
277277
{
278278
size_t i, sz = mset->size;
279279
int nwithkey;
280280
jl_method_t *m;
281-
void **table = mset->table;
281+
void *const *table = mset->table;
282282
jl_array_t *newroots = NULL;
283283
JL_GC_PUSH1(&newroots);
284284
for (i = 0; i < sz; i += 2) {
@@ -369,6 +369,8 @@ static int jl_collect_methcache_from_mod(jl_typemap_entry_t *ml, void *closure)
369369
if (s && !jl_object_in_image((jl_value_t*)m->module)) {
370370
jl_array_ptr_1d_push(s, (jl_value_t*)m);
371371
}
372+
if (edges_map == NULL)
373+
return 1;
372374
jl_svec_t *specializations = m->specializations;
373375
size_t i, l = jl_svec_len(specializations);
374376
for (i = 0; i < l; i++) {
@@ -379,60 +381,22 @@ static int jl_collect_methcache_from_mod(jl_typemap_entry_t *ml, void *closure)
379381
return 1;
380382
}
381383

382-
static void jl_collect_methtable_from_mod(jl_array_t *s, jl_methtable_t *mt)
384+
static int jl_collect_methtable_from_mod(jl_methtable_t *mt, void *env)
383385
{
384-
jl_typemap_visitor(mt->defs, jl_collect_methcache_from_mod, (void*)s);
386+
if (!jl_object_in_image((jl_value_t*)mt))
387+
env = NULL; // do not collect any methods from here
388+
jl_typemap_visitor(jl_atomic_load_relaxed(&mt->defs), jl_collect_methcache_from_mod, env);
389+
if (env && edges_map)
390+
jl_collect_missing_backedges(mt);
391+
return 1;
385392
}
386393

387394
// Collect methods of external functions defined by modules in the worklist
388395
// "extext" = "extending external"
389396
// Also collect relevant backedges
390397
static void jl_collect_extext_methods_from_mod(jl_array_t *s, jl_module_t *m)
391398
{
392-
if (s && !jl_object_in_image((jl_value_t*)m))
393-
s = NULL; // do not collect any methods
394-
jl_svec_t *table = jl_atomic_load_relaxed(&m->bindings);
395-
for (size_t i = 0; i < jl_svec_len(table); i++) {
396-
jl_binding_t *b = (jl_binding_t*)jl_svec_ref(table, i);
397-
if ((void*)b == jl_nothing)
398-
break;
399-
jl_sym_t *name = b->globalref->name;
400-
if (b->owner == b && b->value && b->constp) {
401-
jl_value_t *bv = jl_unwrap_unionall(b->value);
402-
if (jl_is_datatype(bv)) {
403-
jl_typename_t *tn = ((jl_datatype_t*)bv)->name;
404-
if (tn->module == m && tn->name == name && tn->wrapper == b->value) {
405-
jl_methtable_t *mt = tn->mt;
406-
if (mt != NULL &&
407-
(jl_value_t*)mt != jl_nothing &&
408-
(mt != jl_type_type_mt && mt != jl_nonfunction_mt)) {
409-
assert(mt->module == tn->module);
410-
jl_collect_methtable_from_mod(s, mt);
411-
if (s)
412-
jl_collect_missing_backedges(mt);
413-
}
414-
}
415-
}
416-
else if (jl_is_module(b->value)) {
417-
jl_module_t *child = (jl_module_t*)b->value;
418-
if (child != m && child->parent == m && child->name == name) {
419-
// this is the original/primary binding for the submodule
420-
jl_collect_extext_methods_from_mod(s, (jl_module_t*)b->value);
421-
}
422-
}
423-
else if (jl_is_mtable(b->value)) {
424-
jl_methtable_t *mt = (jl_methtable_t*)b->value;
425-
if (mt->module == m && mt->name == name) {
426-
// this is probably an external method table, so let's assume so
427-
// as there is no way to precisely distinguish them,
428-
// and the rest of this serializer does not bother
429-
// to handle any method tables specially
430-
jl_collect_methtable_from_mod(s, (jl_methtable_t*)bv);
431-
}
432-
}
433-
}
434-
table = jl_atomic_load_relaxed(&m->bindings);
435-
}
399+
foreach_mtable_in_module(m, jl_collect_methtable_from_mod, s);
436400
}
437401

438402
static void jl_record_edges(jl_method_instance_t *caller, arraylist_t *wq, jl_array_t *edges)

0 commit comments

Comments
 (0)