Skip to content

Commit 26b6e81

Browse files
committed
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 (cherry picked from commit 87b8896)
1 parent a7f04b8 commit 26b6e81

File tree

4 files changed

+90
-103
lines changed

4 files changed

+90
-103
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 == b->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
}

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
@@ -2229,50 +2229,52 @@ JL_DLLEXPORT jl_value_t *jl_as_global_root(jl_value_t *val JL_MAYBE_UNROOTED)
22292229
}
22302230

22312231
static void jl_prepare_serialization_data(jl_array_t *mod_array, jl_array_t *newly_inferred, uint64_t worklist_key,
2232-
/* outputs */ jl_array_t **extext_methods,
2233-
jl_array_t **new_specializations, jl_array_t **method_roots_list,
2234-
jl_array_t **ext_targets, jl_array_t **edges)
2232+
/* outputs */ jl_array_t **extext_methods, jl_array_t **new_specializations,
2233+
jl_array_t **method_roots_list, jl_array_t **ext_targets, jl_array_t **edges)
22352234
{
22362235
// extext_methods: [method1, ...], worklist-owned "extending external" methods added to functions owned by modules outside the worklist
22372236
// ext_targets: [invokesig1, callee1, matches1, ...] non-worklist callees of worklist-owned methods
22382237
// ordinary dispatch: invokesig=NULL, callee is MethodInstance
22392238
// `invoke` dispatch: invokesig is signature, callee is MethodInstance
22402239
// abstract call: callee is signature
22412240
// edges: [caller1, ext_targets_indexes1, ...] for worklist-owned methods calling external methods
2242-
22432241
assert(edges_map == NULL);
2244-
JL_GC_PUSH1(&edges_map);
22452242

2246-
// Save the inferred code from newly inferred, external methods
22472243
htable_new(&external_mis, 0); // we need external_mis until after `jl_collect_edges` finishes
2244+
// Save the inferred code from newly inferred, external methods
22482245
*new_specializations = queue_external_cis(newly_inferred);
2249-
// Collect the new method roots
2250-
htable_t methods_with_newspecs;
2251-
htable_new(&methods_with_newspecs, 0);
2252-
jl_collect_methods(&methods_with_newspecs, *new_specializations);
2253-
*method_roots_list = jl_alloc_vec_any(0);
2254-
jl_collect_new_roots(*method_roots_list, &methods_with_newspecs, worklist_key);
2255-
htable_free(&methods_with_newspecs);
22562246

22572247
// Collect method extensions and edges data
2258-
edges_map = jl_alloc_vec_any(0);
2248+
JL_GC_PUSH1(&edges_map);
2249+
if (edges)
2250+
edges_map = jl_alloc_vec_any(0);
22592251
*extext_methods = jl_alloc_vec_any(0);
2252+
jl_collect_methtable_from_mod(jl_type_type_mt, *extext_methods);
2253+
jl_collect_methtable_from_mod(jl_nonfunction_mt, *extext_methods);
22602254
size_t i, len = jl_array_len(mod_array);
22612255
for (i = 0; i < len; i++) {
22622256
jl_module_t *m = (jl_module_t*)jl_array_ptr_ref(mod_array, i);
22632257
assert(jl_is_module(m));
22642258
if (m->parent == m) // some toplevel modules (really just Base) aren't actually
22652259
jl_collect_extext_methods_from_mod(*extext_methods, m);
22662260
}
2267-
jl_collect_methtable_from_mod(*extext_methods, jl_type_type_mt);
2268-
jl_collect_missing_backedges(jl_type_type_mt);
2269-
jl_collect_methtable_from_mod(*extext_methods, jl_nonfunction_mt);
2270-
jl_collect_missing_backedges(jl_nonfunction_mt);
2271-
// jl_collect_extext_methods_from_mod and jl_collect_missing_backedges also accumulate data in callers_with_edges.
2272-
// Process this to extract `edges` and `ext_targets`.
2273-
*ext_targets = jl_alloc_vec_any(0);
2274-
*edges = jl_alloc_vec_any(0);
2275-
jl_collect_edges(*edges, *ext_targets);
2261+
2262+
if (edges) {
2263+
jl_collect_missing_backedges(jl_type_type_mt);
2264+
jl_collect_missing_backedges(jl_nonfunction_mt);
2265+
// jl_collect_extext_methods_from_mod and jl_collect_missing_backedges also accumulate data in callers_with_edges.
2266+
// Process this to extract `edges` and `ext_targets`.
2267+
*ext_targets = jl_alloc_vec_any(0);
2268+
*edges = jl_alloc_vec_any(0);
2269+
*method_roots_list = jl_alloc_vec_any(0);
2270+
// Collect the new method roots
2271+
htable_t methods_with_newspecs;
2272+
htable_new(&methods_with_newspecs, 0);
2273+
jl_collect_methods(&methods_with_newspecs, *new_specializations);
2274+
jl_collect_new_roots(*method_roots_list, &methods_with_newspecs, worklist_key);
2275+
htable_free(&methods_with_newspecs);
2276+
jl_collect_edges(*edges, *ext_targets);
2277+
}
22762278
htable_free(&external_mis);
22772279
assert(edges_map == NULL); // jl_collect_edges clears this when done
22782280

@@ -2562,9 +2564,8 @@ static void jl_save_system_image_to_stream(ios_t *f,
25622564
jl_gc_enable(en);
25632565
}
25642566

2565-
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)
2567+
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)
25662568
{
2567-
*mod_array = jl_get_loaded_modules(); // __toplevel__ modules loaded in this session (from Base.loaded_modules_array)
25682569
assert(jl_precompile_toplevel_module == NULL);
25692570
jl_precompile_toplevel_module = (jl_module_t*)jl_array_ptr_ref(worklist, jl_array_len(worklist)-1);
25702571

@@ -2580,7 +2581,7 @@ static void jl_write_header_for_incremental(ios_t *f, jl_array_t *worklist, jl_a
25802581
// write description of requirements for loading (modules that must be pre-loaded if initialization is to succeed)
25812582
// this can return errors during deserialize,
25822583
// best to keep it early (before any actual initialization)
2583-
write_mod_list(f, *mod_array);
2584+
write_mod_list(f, mod_array);
25842585
}
25852586

25862587
JL_DLLEXPORT void jl_create_system_image(void **_native_data, jl_array_t *worklist, bool_t emit_split,
@@ -2611,49 +2612,58 @@ JL_DLLEXPORT void jl_create_system_image(void **_native_data, jl_array_t *workli
26112612
int64_t checksumpos_ff = 0;
26122613
int64_t datastartpos = 0;
26132614
JL_GC_PUSH6(&mod_array, &extext_methods, &new_specializations, &method_roots_list, &ext_targets, &edges);
2614-
if (worklist) {
2615-
jl_write_header_for_incremental(f, worklist, &mod_array, udeps, srctextpos, &checksumpos);
2616-
if (emit_split) {
2617-
checksumpos_ff = write_header(ff, 1);
2618-
write_uint8(ff, jl_cache_flags());
2619-
write_mod_list(ff, mod_array);
2620-
} else {
2621-
checksumpos_ff = checksumpos;
2622-
}
2623-
{
2624-
// make sure we don't run any Julia code concurrently after this point
2625-
jl_gc_enable_finalizers(ct, 0);
2626-
assert(ct->reentrant_inference == 0);
2627-
ct->reentrant_inference = (uint16_t)-1;
2628-
}
2629-
jl_prepare_serialization_data(mod_array, newly_inferred, jl_worklist_key(worklist), &extext_methods, &new_specializations, &method_roots_list, &ext_targets, &edges);
26302615

2616+
if (worklist) {
2617+
mod_array = jl_get_loaded_modules(); // __toplevel__ modules loaded in this session (from Base.loaded_modules_array)
26312618
// Generate _native_data`
26322619
if (jl_options.outputo || jl_options.outputbc || jl_options.outputunoptbc || jl_options.outputasm) {
2620+
jl_prepare_serialization_data(mod_array, newly_inferred, jl_worklist_key(worklist),
2621+
&extext_methods, &new_specializations, NULL, NULL, NULL);
26332622
jl_precompile_toplevel_module = (jl_module_t*)jl_array_ptr_ref(worklist, jl_array_len(worklist)-1);
26342623
*_native_data = jl_precompile_worklist(worklist, extext_methods, new_specializations);
26352624
jl_precompile_toplevel_module = NULL;
2625+
extext_methods = NULL;
2626+
new_specializations = NULL;
26362627
}
2628+
jl_write_header_for_incremental(f, worklist, mod_array, udeps, srctextpos, &checksumpos);
2629+
if (emit_split) {
2630+
checksumpos_ff = write_header(ff, 1);
2631+
write_uint8(ff, jl_cache_flags());
2632+
write_mod_list(ff, mod_array);
2633+
}
2634+
else {
2635+
checksumpos_ff = checksumpos;
2636+
}
2637+
}
2638+
else {
2639+
*_native_data = jl_precompile(jl_options.compile_enabled == JL_OPTIONS_COMPILE_ALL);
2640+
}
26372641

2642+
// Make sure we don't run any Julia code concurrently after this point
2643+
// since it will invalidate our serialization preparations
2644+
jl_gc_enable_finalizers(ct, 0);
2645+
assert(ct->reentrant_inference == 0);
2646+
ct->reentrant_inference = (uint16_t)-1;
2647+
if (worklist) {
2648+
jl_prepare_serialization_data(mod_array, newly_inferred, jl_worklist_key(worklist),
2649+
&extext_methods, &new_specializations, &method_roots_list, &ext_targets, &edges);
26382650
if (!emit_split) {
26392651
write_int32(f, 0); // No clone_targets
26402652
write_padding(f, LLT_ALIGN(ios_pos(f), JL_CACHE_BYTE_ALIGNMENT) - ios_pos(f));
2641-
} else {
2653+
}
2654+
else {
26422655
write_padding(ff, LLT_ALIGN(ios_pos(ff), JL_CACHE_BYTE_ALIGNMENT) - ios_pos(ff));
26432656
}
26442657
datastartpos = ios_pos(ff);
2645-
} else {
2646-
*_native_data = jl_precompile(jl_options.compile_enabled == JL_OPTIONS_COMPILE_ALL);
26472658
}
26482659
native_functions = *_native_data;
26492660
jl_save_system_image_to_stream(ff, worklist, extext_methods, new_specializations, method_roots_list, ext_targets, edges);
26502661
native_functions = NULL;
2651-
if (worklist) {
2652-
// Re-enable running julia code for postoutput hooks, atexit, etc.
2653-
jl_gc_enable_finalizers(ct, 1);
2654-
ct->reentrant_inference = 0;
2655-
jl_precompile_toplevel_module = NULL;
2656-
}
2662+
// make sure we don't run any Julia code concurrently before this point
2663+
// Re-enable running julia code for postoutput hooks, atexit, etc.
2664+
jl_gc_enable_finalizers(ct, 1);
2665+
ct->reentrant_inference = 0;
2666+
jl_precompile_toplevel_module = NULL;
26572667

26582668
if (worklist) {
26592669
// Go back and update the checksum in the header

src/staticdata_utils.c

Lines changed: 12 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -294,12 +294,12 @@ static void jl_collect_methods(htable_t *mset, jl_array_t *new_specializations)
294294
}
295295
}
296296

297-
static void jl_collect_new_roots(jl_array_t *roots, htable_t *mset, uint64_t key)
297+
static void jl_collect_new_roots(jl_array_t *roots, const htable_t *mset, uint64_t key)
298298
{
299299
size_t i, sz = mset->size;
300300
int nwithkey;
301301
jl_method_t *m;
302-
void **table = mset->table;
302+
void *const *table = mset->table;
303303
jl_array_t *newroots = NULL;
304304
JL_GC_PUSH1(&newroots);
305305
for (i = 0; i < sz; i += 2) {
@@ -390,6 +390,8 @@ static int jl_collect_methcache_from_mod(jl_typemap_entry_t *ml, void *closure)
390390
if (s && !jl_object_in_image((jl_value_t*)m->module)) {
391391
jl_array_ptr_1d_push(s, (jl_value_t*)m);
392392
}
393+
if (edges_map == NULL)
394+
return 1;
393395
jl_svec_t *specializations = m->specializations;
394396
size_t i, l = jl_svec_len(specializations);
395397
for (i = 0; i < l; i++) {
@@ -400,59 +402,22 @@ static int jl_collect_methcache_from_mod(jl_typemap_entry_t *ml, void *closure)
400402
return 1;
401403
}
402404

403-
static void jl_collect_methtable_from_mod(jl_array_t *s, jl_methtable_t *mt)
405+
static int jl_collect_methtable_from_mod(jl_methtable_t *mt, void *env)
404406
{
405-
jl_typemap_visitor(mt->defs, jl_collect_methcache_from_mod, (void*)s);
407+
if (!jl_object_in_image((jl_value_t*)mt))
408+
env = NULL; // do not collect any methods from here
409+
jl_typemap_visitor(jl_atomic_load_relaxed(&mt->defs), jl_collect_methcache_from_mod, env);
410+
if (env && edges_map)
411+
jl_collect_missing_backedges(mt);
412+
return 1;
406413
}
407414

408415
// Collect methods of external functions defined by modules in the worklist
409416
// "extext" = "extending external"
410417
// Also collect relevant backedges
411418
static void jl_collect_extext_methods_from_mod(jl_array_t *s, jl_module_t *m)
412419
{
413-
if (s && !jl_object_in_image((jl_value_t*)m))
414-
s = NULL; // do not collect any methods
415-
size_t i;
416-
void **table = m->bindings.table;
417-
for (i = 1; i < m->bindings.size; i += 2) {
418-
if (table[i] != HT_NOTFOUND) {
419-
jl_binding_t *b = (jl_binding_t*)table[i];
420-
if (b->owner == m && b->value && b->constp) {
421-
jl_value_t *bv = jl_unwrap_unionall(b->value);
422-
if (jl_is_datatype(bv)) {
423-
jl_typename_t *tn = ((jl_datatype_t*)bv)->name;
424-
if (tn->module == m && tn->name == b->name && tn->wrapper == b->value) {
425-
jl_methtable_t *mt = tn->mt;
426-
if (mt != NULL &&
427-
(jl_value_t*)mt != jl_nothing &&
428-
(mt != jl_type_type_mt && mt != jl_nonfunction_mt)) {
429-
assert(mt->module == tn->module);
430-
jl_collect_methtable_from_mod(s, mt);
431-
if (s)
432-
jl_collect_missing_backedges(mt);
433-
}
434-
}
435-
}
436-
else if (jl_is_module(b->value)) {
437-
jl_module_t *child = (jl_module_t*)b->value;
438-
if (child != m && child->parent == m && child->name == b->name) {
439-
// this is the original/primary binding for the submodule
440-
jl_collect_extext_methods_from_mod(s, (jl_module_t*)b->value);
441-
}
442-
}
443-
else if (jl_is_mtable(b->value)) {
444-
jl_methtable_t *mt = (jl_methtable_t*)b->value;
445-
if (mt->module == m && mt->name == b->name) {
446-
// this is probably an external method table, so let's assume so
447-
// as there is no way to precisely distinguish them,
448-
// and the rest of this serializer does not bother
449-
// to handle any method tables specially
450-
jl_collect_methtable_from_mod(s, (jl_methtable_t*)bv);
451-
}
452-
}
453-
}
454-
}
455-
}
420+
foreach_mtable_in_module(m, jl_collect_methtable_from_mod, s);
456421
}
457422

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

0 commit comments

Comments
 (0)