Skip to content

Commit 5319160

Browse files
committed
remove precompile mutation step from staticdata
Make sure things are properly ordered here, so that when serializing, nothing is mutating the system at the same time. Fix #48047
1 parent 388864a commit 5319160

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
}
@@ -501,6 +500,15 @@ int foreach_mtable_in_module(
501500
}
502501
}
503502
}
503+
else if (jl_is_mtable(v)) {
504+
jl_methtable_t *mt = (jl_methtable_t*)v;
505+
if (mt->module == m && mt->name == name) {
506+
// this is probably an external method table here, so let's
507+
// assume so as there is no way to precisely distinguish them
508+
if (!visit(mt, env))
509+
return 0;
510+
}
511+
}
504512
}
505513
table = jl_atomic_load_relaxed(&m->bindings);
506514
}

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)