Skip to content

Commit 7d92d3d

Browse files
Merge pull request #25034 from JuliaLang/jn/require_ji_better2
loading: refactor .ji loading, to handle errors better
2 parents dcddbca + e58b7c7 commit 7d92d3d

File tree

5 files changed

+110
-107
lines changed

5 files changed

+110
-107
lines changed

base/loading.jl

Lines changed: 62 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -118,22 +118,58 @@ end
118118

119119
# these return either the array of modules loaded from the path / content given
120120
# or an Exception that describes why it couldn't be loaded
121-
function _include_from_serialized(content::Vector{UInt8})
122-
return ccall(:jl_restore_incremental_from_buf, Any, (Ptr{UInt8}, Int), content, sizeof(content))
121+
function _include_from_serialized(content::Vector{UInt8}, depmods::Vector{Module})
122+
return ccall(:jl_restore_incremental_from_buf, Any, (Ptr{UInt8}, Int, Any), content, sizeof(content), depmods)
123123
end
124-
function _include_from_serialized(path::String)
125-
return ccall(:jl_restore_incremental, Any, (Cstring,), path)
124+
function _include_from_serialized(path::String, depmods::Vector{Module})
125+
return ccall(:jl_restore_incremental, Any, (Cstring, Any), path, depmods)
126126
end
127127

128128
# returns an array of modules loaded, or an Exception that describes why it failed
129129
# and it reconnects the Base.Docs.META
130130
function _require_from_serialized(mod::Symbol, path_to_try::String)
131-
restored = _include_from_serialized(path_to_try)
131+
return _require_from_serialized(mod, path_to_try, parse_cache_header(path_to_try)[3])
132+
end
133+
function _require_from_serialized(mod::Symbol, path_to_try::String, depmodnames::Vector{Pair{Symbol, UInt64}})
134+
# load all of the dependent modules
135+
ndeps = length(depmodnames)
136+
depmods = Vector{Module}(uninitialized, ndeps)
137+
for i in 1:ndeps
138+
modname, uuid = depmodnames[i]
139+
if root_module_exists(modname)
140+
M = root_module(modname)
141+
if module_name(M) === modname && module_uuid(M) === uuid
142+
depmods[i] = M
143+
end
144+
else
145+
modpath = find_package(string(modname))
146+
modpath === nothing && return ErrorException("Required dependency $modname not found in current path.")
147+
mod = _require_search_from_serialized(modname, String(modpath))
148+
if !isa(mod, Bool)
149+
for M in mod::Vector{Any}
150+
if module_name(M) === modname && module_uuid(M) === uuid
151+
depmods[i] = M
152+
break
153+
end
154+
end
155+
for callback in package_callbacks
156+
invokelatest(callback, modname)
157+
end
158+
end
159+
end
160+
isassigned(depmods, i) || return ErrorException("Required dependency $modname failed to load from a cache file.")
161+
end
162+
# now load the path_to_try.ji file
163+
restored = _include_from_serialized(path_to_try, depmods)
132164
if !isa(restored, Exception)
133165
for M in restored::Vector{Any}
166+
M = M::Module
134167
if isdefined(M, Base.Docs.META)
135168
push!(Base.Docs.modules, M)
136169
end
170+
if module_parent(M) === M
171+
register_root_module(module_name(M), M)
172+
end
137173
end
138174
end
139175
return restored
@@ -145,12 +181,13 @@ end
145181
function _require_search_from_serialized(mod::Symbol, sourcepath::String)
146182
paths = find_all_in_cache_path(mod)
147183
for path_to_try in paths::Vector{String}
148-
if stale_cachefile(sourcepath, path_to_try)
184+
deps = stale_cachefile(sourcepath, path_to_try)
185+
if deps === true
149186
continue
150187
end
151-
restored = _require_from_serialized(mod, path_to_try)
188+
restored = _require_from_serialized(mod, path_to_try, deps)
152189
if isa(restored, Exception)
153-
if isa(restored, ErrorException) && endswith(restored.msg, " uuid did not match cache file.")
190+
if isa(restored, ErrorException)
154191
# can't use this cache due to a module uuid mismatch,
155192
# defer reporting error until after trying all of the possible matches
156193
@debug "Failed to load $path_to_try because $(restored.msg)"
@@ -178,7 +215,7 @@ const package_callbacks = Any[]
178215
const include_callbacks = Any[]
179216

180217
# used to optionally track dependencies when requiring a module:
181-
const _concrete_dependencies = Any[] # these dependency versions are "set in stone", and the process should try to avoid invalidating them
218+
const _concrete_dependencies = Pair{Symbol, UInt64}[] # these dependency versions are "set in stone", and the process should try to avoid invalidating them
182219
const _require_dependencies = Any[] # a list of (mod, path, mtime) tuples that are the file dependencies of the module currently being precompiled
183220
const _track_dependencies = Ref(false) # set this to true to track the list of file dependencies
184221
function _include_dependency(modstring::AbstractString, _path::AbstractString)
@@ -358,14 +395,6 @@ function unreference_module(key)
358395
end
359396
end
360397

361-
function register_all(a)
362-
for m in a
363-
if module_parent(m) === m
364-
register_root_module(module_name(m), m)
365-
end
366-
end
367-
end
368-
369398
function _require(mod::Symbol)
370399
# dependency-tracking is only used for one top-level include(path),
371400
# and is not applied recursively to imported modules:
@@ -390,13 +419,13 @@ function _require(mod::Symbol)
390419
if path === nothing
391420
throw(ArgumentError("Module $name not found in current path.\nRun `Pkg.add(\"$name\")` to install the $name package."))
392421
end
422+
path = String(path)
393423

394424
# attempt to load the module file via the precompile cache locations
395425
doneprecompile = false
396426
if JLOptions().use_compiled_modules != 0
397427
doneprecompile = _require_search_from_serialized(mod, path)
398428
if !isa(doneprecompile, Bool)
399-
register_all(doneprecompile)
400429
return
401430
end
402431
end
@@ -423,7 +452,6 @@ function _require(mod::Symbol)
423452
@warn "The call to compilecache failed to create a usable precompiled cache file for module $name" exception=m
424453
# fall-through, TODO: disable __precompile__(true) error so that the normal include will succeed
425454
else
426-
register_all(m)
427455
return
428456
end
429457
end
@@ -446,7 +474,6 @@ function _require(mod::Symbol)
446474
# TODO: disable __precompile__(true) error and do normal include instead of error
447475
error("Module $mod declares __precompile__(true) but require failed to create a usable precompiled cache file.")
448476
end
449-
register_all(m)
450477
end
451478
finally
452479
toplevel_load[] = last
@@ -539,7 +566,7 @@ function evalfile(path::AbstractString, args::Vector{String}=String[])
539566
end
540567
evalfile(path::AbstractString, args::Vector) = evalfile(path, String[args...])
541568

542-
function create_expr_cache(input::String, output::String, concrete_deps::Vector{Any})
569+
function create_expr_cache(input::String, output::String, concrete_deps::typeof(_concrete_dependencies))
543570
rm(output, force=true) # Remove file if it exists
544571
code_object = """
545572
while !eof(STDIN)
@@ -606,12 +633,12 @@ function compilecache(name::String)
606633
if !isdir(cachepath)
607634
mkpath(cachepath)
608635
end
609-
cachefile::String = abspath(cachepath, name*".ji")
636+
cachefile::String = abspath(cachepath, "$name.ji")
610637
# build up the list of modules that we want the precompile process to preserve
611638
concrete_deps = copy(_concrete_dependencies)
612-
for (key,mod) in loaded_modules
639+
for (key, mod) in loaded_modules
613640
if !(mod === Main || mod === Core || mod === Base)
614-
push!(concrete_deps, (key, module_uuid(mod)))
641+
push!(concrete_deps, key => module_uuid(mod))
615642
end
616643
end
617644
# run the expression and cache the result
@@ -637,13 +664,13 @@ module_uuid(m::Module) = ccall(:jl_module_uuid, UInt64, (Any,), m)
637664
isvalid_cache_header(f::IOStream) = 0 != ccall(:jl_read_verify_header, Cint, (Ptr{Void},), f.ios)
638665

639666
function parse_cache_header(f::IO)
640-
modules = Dict{Symbol,UInt64}()
667+
modules = Vector{Pair{Symbol, UInt64}}()
641668
while true
642669
n = ntoh(read(f, Int32))
643670
n == 0 && break
644671
sym = Symbol(read(f, n)) # module symbol
645672
uuid = ntoh(read(f, UInt64)) # module UUID (mostly just a timestamp)
646-
modules[sym] = uuid
673+
push!(modules, sym => uuid)
647674
end
648675
totbytes = ntoh(read(f, Int64)) # total bytes for file dependencies
649676
# read the list of files
@@ -662,13 +689,13 @@ function parse_cache_header(f::IO)
662689
@assert totbytes == 12 "header of cache file appears to be corrupt"
663690
srctextpos = ntoh(read(f, Int64))
664691
# read the list of modules that are required to be present during loading
665-
required_modules = Dict{Symbol,UInt64}()
692+
required_modules = Vector{Pair{Symbol, UInt64}}()
666693
while true
667694
n = ntoh(read(f, Int32))
668695
n == 0 && break
669696
sym = Symbol(read(f, n)) # module symbol
670697
uuid = ntoh(read(f, UInt64)) # module UUID
671-
required_modules[sym] = uuid
698+
push!(required_modules, sym => uuid)
672699
end
673700
return modules, files, required_modules, srctextpos
674701
end
@@ -729,6 +756,8 @@ function read_dependency_src(cachefile::String, filename::AbstractString)
729756
end
730757
end
731758

759+
# returns true if it "cachefile.ji" is stale relative to "modpath.jl"
760+
# otherwise returns the list of dependencies to also check
732761
function stale_cachefile(modpath::String, cachefile::String)
733762
io = open(cachefile, "r")
734763
try
@@ -737,13 +766,12 @@ function stale_cachefile(modpath::String, cachefile::String)
737766
return true # invalid cache file
738767
end
739768
modules, files, required_modules = parse_cache_header(io)
769+
modules = Dict{Symbol, UInt64}(modules)
740770

741771
# Check if transitive dependencies can be fullfilled
742-
for mod in keys(required_modules)
743-
if mod == :Main || mod == :Core || mod == :Base
744-
continue
772+
for (mod, uuid_req) in required_modules
745773
# Module is already loaded
746-
elseif root_module_exists(mod)
774+
if root_module_exists(mod)
747775
continue
748776
end
749777
name = string(mod)
@@ -761,7 +789,7 @@ function stale_cachefile(modpath::String, cachefile::String)
761789
uuid = get(modules, mod, UInt64(0))
762790
if uuid !== UInt64(0)
763791
if uuid === uuid_req
764-
return false # this is the file we want
792+
return required_modules # this is the file we want
765793
end
766794
@debug "Rejecting cache file $cachefile because it provides the wrong uuid (got $uuid) for $mod (want $uuid_req)"
767795
return true # cachefile doesn't provide the required version of the dependency
@@ -790,7 +818,7 @@ function stale_cachefile(modpath::String, cachefile::String)
790818
return true
791819
end
792820

793-
return false # fresh cachefile
821+
return required_modules # fresh cachefile
794822
finally
795823
close(io)
796824
end

base/precompile.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1417,7 +1417,7 @@ precompile(Tuple{typeof(Base.collect_to!), Array{Int32, 1}, Base.Generator{Array
14171417
precompile(Tuple{typeof(Base.collect_to!), Array{Symbol, 1}, Base.Generator{Array{Any, 1}, typeof(Base.FastMath.make_fastmath)}, Int64, Int64})
14181418
precompile(Tuple{typeof(Base.compilecache), String})
14191419
precompile(Tuple{typeof(Base.copyto!), Array{Tuple{String, Float64}, 1}, Int64, Array{Tuple{String, Float64}, 1}, Int64, Int64})
1420-
precompile(Tuple{typeof(Base.create_expr_cache), String, String, Array{Any, 1}})
1420+
precompile(Tuple{typeof(Base.create_expr_cache), String, String, typeof(Base._concrete_dependencies)})
14211421
precompile(Tuple{typeof(Base._delete!), Base.Dict{Symbol, Base.Condition}, Int64})
14221422
precompile(Tuple{typeof(Base.Docs.docm), LineNumberNode, Module, String, Expr})
14231423
precompile(Tuple{typeof(Base.Docs.docm), LineNumberNode, Module, String, Expr, Bool})
@@ -1444,7 +1444,7 @@ precompile(Tuple{typeof(Base.ht_keyindex), Base.Dict{Symbol, UInt64}, Symbol})
14441444
precompile(Tuple{typeof(Base.ht_keyindex), Base.Dict{Tuple{String, Float64}, Void}, Tuple{String, Float64}})
14451445
precompile(Tuple{typeof(Base.ident_cmp), Tuple{String, String, Int64}, Tuple{String, Int64}})
14461446
precompile(Tuple{typeof(Base.include_relative), String})
1447-
precompile(Tuple{typeof(Base._include_from_serialized), String})
1447+
precompile(Tuple{typeof(Base._include_from_serialized), String, Vector{Module}})
14481448
precompile(Tuple{typeof(Base.indexed_next), Tuple{Symbol, UInt64}, Int64, Int64})
14491449
precompile(Tuple{typeof(Base.indexed_next), Tuple{Void, Void}, Int64, Int64})
14501450
precompile(Tuple{typeof(Base.isassigned), Array{String, 1}, Int64})
@@ -1485,7 +1485,7 @@ precompile(Tuple{typeof(Base.rehash!), Base.Dict{Symbol, Base.Condition}, Int64}
14851485
precompile(Tuple{typeof(Base.rehash!), Base.Dict{Symbol, UInt64}, Int64})
14861486
precompile(Tuple{typeof(Base.rehash!), Base.Dict{Tuple{String, Float64}, Void}, Int64})
14871487
precompile(Tuple{typeof(Base.remove_linenums!), Module})
1488-
precompile(Tuple{typeof(Base._require_from_serialized), Symbol, String})
1488+
precompile(Tuple{typeof(Base._require_from_serialized), Symbol, String, Vector{Pair{Symbol, UInt64}}})
14891489
precompile(Tuple{typeof(Base._require_search_from_serialized), Symbol, String})
14901490
precompile(Tuple{typeof(Base.require), Symbol})
14911491
precompile(Tuple{typeof(Base.resize!), Array{Base.Condition, 1}, Int64})

src/dump.c

Lines changed: 20 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1924,53 +1924,29 @@ static int size_isgreater(const void *a, const void *b)
19241924
return *(size_t*)b - *(size_t*)a;
19251925
}
19261926

1927-
static jl_value_t *read_verify_mod_list(ios_t *s, arraylist_t *dependent_worlds)
1927+
static jl_value_t *read_verify_mod_list(ios_t *s, arraylist_t *dependent_worlds, jl_array_t *mod_list)
19281928
{
19291929
if (!jl_main_module->uuid) {
19301930
return jl_get_exceptionf(jl_errorexception_type,
19311931
"Main module uuid state is invalid for module deserialization.");
19321932
}
1933-
jl_array_t *mod_array = jl_alloc_vec_any(0);
1934-
JL_GC_PUSH1(&mod_array);
1935-
while (1) {
1933+
size_t i, l = jl_array_len(mod_list);
1934+
for (i = 0; ; i++) {
19361935
size_t len = read_int32(s);
1937-
if (len == 0) {
1938-
JL_GC_POP();
1939-
return (jl_value_t*)mod_array;
1940-
}
1941-
char *name = (char*)alloca(len+1);
1936+
if (len == 0 && i == l)
1937+
return NULL; // success
1938+
if (len == 0 || i == l)
1939+
return jl_get_exceptionf(jl_errorexception_type, "Wrong number of entries in module list.");
1940+
char *name = (char*)alloca(len + 1);
19421941
ios_read(s, name, len);
19431942
name[len] = '\0';
19441943
uint64_t uuid = read_uint64(s);
1945-
jl_sym_t *sym = jl_symbol(name);
1946-
jl_module_t *m = NULL;
1947-
static jl_value_t *require_func = NULL;
1948-
if (!require_func)
1949-
require_func = jl_get_global(jl_base_module, jl_symbol("require"));
1950-
jl_value_t *reqargs[2] = {require_func, (jl_value_t*)sym};
1951-
JL_TRY {
1952-
m = (jl_module_t*)jl_apply(reqargs, 2);
1953-
}
1954-
JL_CATCH {
1955-
ios_close(s);
1956-
jl_rethrow();
1957-
}
1958-
if (!m) {
1959-
JL_GC_POP();
1960-
return jl_get_exceptionf(jl_errorexception_type,
1961-
"Requiring \"%s\" did not define a corresponding module.", name);
1962-
}
1963-
if (!jl_is_module(m)) {
1964-
JL_GC_POP();
1965-
return jl_get_exceptionf(jl_errorexception_type,
1966-
"Invalid module path (%s does not name a module).", name);
1967-
}
1968-
if (m->uuid != uuid) {
1969-
JL_GC_POP();
1944+
jl_sym_t *sym = jl_symbol_n(name, len);
1945+
jl_module_t *m = (jl_module_t*)jl_array_ptr_ref(mod_list, i);
1946+
if (!m || !jl_is_module(m) || m->name != sym || m->uuid != uuid) {
19701947
return jl_get_exceptionf(jl_errorexception_type,
1971-
"Module %s uuid did not match cache file.", name);
1948+
"Invalid input in module list: expected %s.", name);
19721949
}
1973-
jl_array_ptr_1d_push(mod_array, (jl_value_t*)m);
19741950
if (m->primary_world > jl_main_module->primary_world)
19751951
arraylist_push(dependent_worlds, (void*)m->primary_world);
19761952
}
@@ -2659,7 +2635,7 @@ static int trace_method(jl_typemap_entry_t *entry, void *closure)
26592635
return 1;
26602636
}
26612637

2662-
static jl_value_t *_jl_restore_incremental(ios_t *f)
2638+
static jl_value_t *_jl_restore_incremental(ios_t *f, jl_array_t *mod_array)
26632639
{
26642640
jl_ptls_t ptls = jl_get_ptls_states();
26652641
if (ios_eof(f) || !jl_read_verify_header(f)) {
@@ -2682,13 +2658,12 @@ static jl_value_t *_jl_restore_incremental(ios_t *f)
26822658
arraylist_new(&dependent_worlds, 0);
26832659

26842660
// verify that the system state is valid
2685-
jl_value_t *verify_result = read_verify_mod_list(f, &dependent_worlds);
2686-
if (!jl_is_array(verify_result)) {
2661+
jl_value_t *verify_fail = read_verify_mod_list(f, &dependent_worlds, mod_array);
2662+
if (verify_fail) {
26872663
arraylist_free(&dependent_worlds);
26882664
ios_close(f);
2689-
return verify_result;
2665+
return verify_fail;
26902666
}
2691-
jl_array_t *mod_array = (jl_array_t*)verify_result;
26922667

26932668
// prepare to deserialize
26942669
int en = jl_gc_enable(0);
@@ -2751,21 +2726,21 @@ static jl_value_t *_jl_restore_incremental(ios_t *f)
27512726
return (jl_value_t*)restored;
27522727
}
27532728

2754-
JL_DLLEXPORT jl_value_t *jl_restore_incremental_from_buf(const char *buf, size_t sz)
2729+
JL_DLLEXPORT jl_value_t *jl_restore_incremental_from_buf(const char *buf, size_t sz, jl_array_t *mod_array)
27552730
{
27562731
ios_t f;
27572732
ios_static_buffer(&f, (char*)buf, sz);
2758-
return _jl_restore_incremental(&f);
2733+
return _jl_restore_incremental(&f, mod_array);
27592734
}
27602735

2761-
JL_DLLEXPORT jl_value_t *jl_restore_incremental(const char *fname)
2736+
JL_DLLEXPORT jl_value_t *jl_restore_incremental(const char *fname, jl_array_t *mod_array)
27622737
{
27632738
ios_t f;
27642739
if (ios_file(&f, fname, 1, 0, 0, 0) == NULL) {
27652740
return jl_get_exceptionf(jl_errorexception_type,
27662741
"Cache file \"%s\" not found.\n", fname);
27672742
}
2768-
return _jl_restore_incremental(&f);
2743+
return _jl_restore_incremental(&f, mod_array);
27692744
}
27702745

27712746
// --- init ---

src/julia.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1381,8 +1381,8 @@ JL_DLLEXPORT void jl_save_system_image(const char *fname);
13811381
JL_DLLEXPORT void jl_restore_system_image(const char *fname);
13821382
JL_DLLEXPORT void jl_restore_system_image_data(const char *buf, size_t len);
13831383
JL_DLLEXPORT int jl_save_incremental(const char *fname, jl_array_t *worklist);
1384-
JL_DLLEXPORT jl_value_t *jl_restore_incremental(const char *fname);
1385-
JL_DLLEXPORT jl_value_t *jl_restore_incremental_from_buf(const char *buf, size_t sz);
1384+
JL_DLLEXPORT jl_value_t *jl_restore_incremental(const char *fname, jl_array_t *depmods);
1385+
JL_DLLEXPORT jl_value_t *jl_restore_incremental_from_buf(const char *buf, size_t sz, jl_array_t *depmods);
13861386

13871387
// front end interface
13881388
JL_DLLEXPORT jl_value_t *jl_parse_input_line(const char *str, size_t len,

0 commit comments

Comments
 (0)