@@ -1218,8 +1218,8 @@ function _include_from_serialized(pkg::PkgId, path::String, ocachepath::Union{No
12181218 dep = depmods[i]
12191219 dep isa Module && continue
12201220 _, depkey, depbuild_id = dep:: Tuple{String, PkgId, UInt128}
1221- @assert root_module_exists ( depkey)
1222- dep = root_module ( depkey)
1221+ dep = loaded_precompiles[ depkey => depbuild_id]
1222+ @assert PkgId ( dep) == depkey && module_build_id (dep) === depbuild_id
12231223 depmods[i] = dep
12241224 end
12251225
@@ -1276,7 +1276,8 @@ function register_restored_modules(sv::SimpleVector, pkg::PkgId, path::String)
12761276 push! (Base. Docs. modules, M)
12771277 end
12781278 if parentmodule (M) === M
1279- register_root_module (M)
1279+ push! (loaded_modules_order, M)
1280+ loaded_precompiles[pkg => module_build_id (M)] = M
12801281 end
12811282 end
12821283
@@ -1703,7 +1704,7 @@ function compilecache_path(pkg::PkgId;
17031704 if staledeps === true
17041705 continue
17051706 end
1706- staledeps, _ = staledeps:: Tuple{Vector{Any}, Union{Nothing, String}}
1707+ staledeps, _, _ = staledeps:: Tuple{Vector{Any}, Union{Nothing, String}, UInt128 }
17071708 # finish checking staledeps module graph
17081709 for i in 1 : length (staledeps)
17091710 dep = staledeps[i]
@@ -1791,23 +1792,23 @@ end
17911792# search for a precompile cache file to load, after some various checks
17921793function _tryrequire_from_serialized (modkey:: PkgId , build_id:: UInt128 )
17931794 assert_havelock (require_lock)
1794- if root_module_exists (modkey)
1795- loaded = root_module (modkey)
1796- else
1795+ loaded = maybe_root_module (modkey)
1796+ if loaded === nothing
17971797 loaded = start_loading (modkey)
1798- if loaded === nothing
1799- try
1800- modpath = locate_package (modkey)
1801- modpath === nothing && return nothing
1802- set_pkgorigin_version_path (modkey, String (modpath))
1803- loaded = _require_search_from_serialized (modkey, String (modpath), build_id, true )
1804- finally
1805- end_loading (modkey, loaded)
1806- end
1807- if loaded isa Module
1808- insert_extension_triggers (modkey)
1809- run_package_callbacks (modkey)
1810- end
1798+ end
1799+ if loaded === nothing
1800+ try
1801+ modpath = locate_package (modkey)
1802+ isnothing (modpath) && error (" Cannot locate source for $(repr (" text/plain" , modkey)) " )
1803+ modpath = String (modpath):: String
1804+ set_pkgorigin_version_path (modkey, modpath)
1805+ loaded = _require_search_from_serialized (modkey, modpath, build_id, true )
1806+ finally
1807+ end_loading (modkey, loaded)
1808+ end
1809+ if loaded isa Module
1810+ insert_extension_triggers (modkey)
1811+ run_package_callbacks (modkey)
18111812 end
18121813 end
18131814 if loaded isa Module && PkgId (loaded) == modkey && module_build_id (loaded) === build_id
@@ -1880,10 +1881,12 @@ function _tryrequire_from_serialized(pkg::PkgId, path::String, ocachepath::Union
18801881 depmods[i] = dep
18811882 end
18821883 # then load the file
1883- return _include_from_serialized (pkg, path, ocachepath, depmods, ignore_native)
1884+ loaded = _include_from_serialized (pkg, path, ocachepath, depmods, ignore_native)
1885+ loaded isa Module && register_root_module (loaded)
1886+ return loaded
18841887end
18851888
1886- # returns `nothing` if require found a precompile cache for this sourcepath, but couldn't load it
1889+ # returns `nothing` if require found a precompile cache for this sourcepath, but couldn't load it or it was stale
18871890# returns the set of modules restored if the cache load succeeded
18881891@constprop :none function _require_search_from_serialized (pkg:: PkgId , sourcepath:: String , build_id:: UInt128 , stalecheck:: Bool ; reasons= nothing , DEPOT_PATH :: typeof (DEPOT_PATH )= DEPOT_PATH )
18891892 assert_havelock (require_lock)
@@ -1895,7 +1898,7 @@ end
18951898 continue
18961899 end
18971900 try
1898- staledeps, ocachefile = staledeps:: Tuple{Vector{Any}, Union{Nothing, String}}
1901+ staledeps, ocachefile, build_id = staledeps:: Tuple{Vector{Any}, Union{Nothing, String}, UInt128 }
18991902 # finish checking staledeps module graph
19001903 for i in 1 : length (staledeps)
19011904 dep = staledeps[i]
@@ -1907,14 +1910,19 @@ end
19071910 if modstaledeps === true
19081911 continue
19091912 end
1910- modstaledeps, modocachepath = modstaledeps:: Tuple{Vector{Any}, Union{Nothing, String}}
1913+ modstaledeps, modocachepath, _ = modstaledeps:: Tuple{Vector{Any}, Union{Nothing, String}, UInt128 }
19111914 staledeps[i] = (modpath, modkey, modbuild_id, modpath_to_try, modstaledeps, modocachepath)
19121915 @goto check_next_dep
19131916 end
19141917 @debug " Rejecting cache file $path_to_try because required dependency $modkey with build ID $(UUID (modbuild_id)) is missing from the cache."
19151918 @goto check_next_path
19161919 @label check_next_dep
19171920 end
1921+ M = get (loaded_precompiles, pkg => build_id, nothing )
1922+ if isa (M, Module)
1923+ stalecheck && register_root_module (M)
1924+ return M
1925+ end
19181926 if stalecheck
19191927 try
19201928 touch (path_to_try) # update timestamp of precompilation file
@@ -1927,26 +1935,25 @@ end
19271935 dep = staledeps[i]
19281936 dep isa Module && continue
19291937 modpath, modkey, modbuild_id, modcachepath, modstaledeps, modocachepath = dep:: Tuple{String, PkgId, UInt128, String, Vector{Any}, Union{Nothing, String}}
1930- dep = nothing
1931- if root_module_exists (modkey)
1932- dep = root_module (modkey)
1938+ dep = get (loaded_precompiles, modkey => modbuild_id, nothing )
1939+ if dep === nothing
1940+ dep = maybe_root_module (modkey)
19331941 end
19341942 while true
19351943 if dep isa Module
19361944 if PkgId (dep) == modkey && module_build_id (dep) === modbuild_id
19371945 break
19381946 else
1939- if stalecheck
1940- @debug " Rejecting cache file $path_to_try because module $modkey is already loaded and incompatible."
1941- @goto check_next_path
1942- end
1947+ @debug " Rejecting cache file $path_to_try because module $modkey got loaded at a different version than expected."
1948+ @goto check_next_path
19431949 end
19441950 end
19451951 dep = start_loading (modkey)
19461952 if dep === nothing
19471953 try
19481954 set_pkgorigin_version_path (modkey, modpath)
19491955 dep = _include_from_serialized (modkey, modcachepath, modocachepath, modstaledeps)
1956+ dep isa Module && stalecheck && register_root_module (dep)
19501957 finally
19511958 end_loading (modkey, dep)
19521959 end
@@ -1960,7 +1967,11 @@ end
19601967 end
19611968 staledeps[i] = dep
19621969 end
1963- restored = _include_from_serialized (pkg, path_to_try, ocachefile, staledeps)
1970+ restored = get (loaded_precompiles, pkg => build_id, nothing )
1971+ if ! isa (restored, Module)
1972+ restored = _include_from_serialized (pkg, path_to_try, ocachefile, staledeps)
1973+ end
1974+ isa (restored, Module) && stalecheck && register_root_module (restored)
19641975 isa (restored, Module) && return restored
19651976 @debug " Deserialization checks failed while attempting to load cache from $path_to_try " exception= restored
19661977 @label check_next_path
@@ -2046,7 +2057,7 @@ const package_callbacks = Any[]
20462057const include_callbacks = Any[]
20472058
20482059# used to optionally track dependencies when requiring a module:
2049- const _concrete_dependencies = Pair{PkgId,UInt128}[] # these dependency versions are "set in stone", and the process should try to avoid invalidating them
2060+ const _concrete_dependencies = Pair{PkgId,UInt128}[] # these dependency versions are "set in stone", because they are explicitly loaded, and the process should try to avoid invalidating them
20502061const _require_dependencies = Any[] # a list of (mod, abspath, fsize, hash, mtime) tuples that are the file dependencies of the module currently being precompiled
20512062const _track_dependencies = Ref (false ) # set this to true to track the list of file dependencies
20522063function _include_dependency (mod:: Module , _path:: AbstractString ; track_content= true ,
@@ -2296,14 +2307,19 @@ end
22962307PkgOrigin () = PkgOrigin (nothing , nothing , nothing )
22972308const pkgorigins = Dict {PkgId,PkgOrigin} ()
22982309
2299- const loaded_modules = Dict {PkgId,Module} ()
2300- # Emptied on Julia start
2301- const explicit_loaded_modules = Dict {PkgId,Module} ()
2310+ const explicit_loaded_modules = Dict {PkgId,Module} () # Emptied on Julia start
2311+ const loaded_modules = Dict {PkgId,Module} () # available to be explicitly loaded
2312+ const loaded_precompiles = Dict {Pair{ PkgId,UInt128}, Module} () # extended (complete) list of modules, available to be loaded
23022313const loaded_modules_order = Vector {Module} ()
2303- const module_keys = IdDict {Module,PkgId} () # the reverse
2314+ const module_keys = IdDict {Module,PkgId} () # the reverse of loaded_modules
23042315
23052316root_module_key (m:: Module ) = @lock require_lock module_keys[m]
23062317
2318+ function module_build_id (m:: Module )
2319+ hi, lo = ccall (:jl_module_build_id , NTuple{2 ,UInt64}, (Any,), m)
2320+ return (UInt128 (hi) << 64 ) | lo
2321+ end
2322+
23072323@constprop :none function register_root_module (m:: Module )
23082324 # n.b. This is called from C after creating a new module in `Base.__toplevel__`,
23092325 # instead of adding them to the binding table there.
@@ -2319,7 +2335,7 @@ root_module_key(m::Module) = @lock require_lock module_keys[m]
23192335 end
23202336 end
23212337 end
2322- push! (loaded_modules_order, m)
2338+ haskey (loaded_precompiles, key => module_build_id (m)) || push! (loaded_modules_order, m)
23232339 loaded_modules[key] = m
23242340 explicit_loaded_modules[key] = m
23252341 module_keys[m] = key
@@ -2351,6 +2367,9 @@ root_module_exists(key::PkgId) = @lock require_lock haskey(loaded_modules, key)
23512367loaded_modules_array () = @lock require_lock copy (loaded_modules_order)
23522368
23532369function unreference_module (key:: PkgId )
2370+ if haskey (explicit_loaded_modules, key)
2371+ m = pop! (explicit_loaded_modules, key)
2372+ end
23542373 if haskey (loaded_modules, key)
23552374 m = pop! (loaded_modules, key)
23562375 # need to ensure all modules are GC rooted; will still be referenced
@@ -2495,7 +2514,7 @@ function _require(pkg::PkgId, env=nothing)
24952514 return loaded
24962515end
24972516
2498- # load a serialized file directly
2517+ # load a serialized file directly, including dependencies (without checking staleness except for immediate conflicts)
24992518function _require_from_serialized (uuidkey:: PkgId , path:: String , ocachepath:: Union{String, Nothing} , sourcepath:: String )
25002519 @lock require_lock begin
25012520 set_pkgorigin_version_path (uuidkey, sourcepath)
@@ -2929,13 +2948,15 @@ function compilecache(pkg::PkgId, path::String, internal_stderr::IO = stderr, in
29292948 cachepath = compilecache_dir (pkg)
29302949
29312950 # build up the list of modules that we want the precompile process to preserve
2932- concrete_deps = copy (_concrete_dependencies)
29332951 if keep_loaded_modules
2934- for mod in loaded_modules_array ()
2935- if ! (mod === Main || mod === Core || mod === Base)
2936- push! (concrete_deps, PkgId (mod) => module_build_id (mod))
2952+ concrete_deps = copy (_concrete_dependencies)
2953+ for (pkgreq, modreq) in loaded_modules # TODO : convert all relevant staleness heuristics to use explicit_loaded_modules instead
2954+ if ! (pkgreq === Main || pkgreq === Core || pkgreq === Base)
2955+ push! (concrete_deps, pkgreq => module_build_id (modreq))
29372956 end
29382957 end
2958+ else
2959+ concrete_deps = empty (_concrete_dependencies)
29392960 end
29402961 # run the expression and cache the result
29412962 verbosity = isinteractive () ? CoreLogging. Info : CoreLogging. Debug
@@ -3063,11 +3084,6 @@ function rename_unique_ocachefile(tmppath_so::String, ocachefile_orig::String, o
30633084 return ocachefile
30643085end
30653086
3066- function module_build_id (m:: Module )
3067- hi, lo = ccall (:jl_module_build_id , NTuple{2 ,UInt64}, (Any,), m)
3068- return (UInt128 (hi) << 64 ) | lo
3069- end
3070-
30713087function object_build_id (obj)
30723088 mod = ccall (:jl_object_top_module , Any, (Any,), obj)
30733089 if mod === nothing
@@ -3646,7 +3662,7 @@ end
36463662@constprop :none function stale_cachefile (modkey:: PkgId , build_id:: UInt128 , modpath:: String , cachefile:: String ;
36473663 ignore_loaded:: Bool = false , requested_flags:: CacheFlags = CacheFlags (),
36483664 reasons:: Union{Dict{String,Int},Nothing} = nothing , stalecheck:: Bool = true )
3649- # XXX : this function appears to dl all of the file validation, not just those checks related to stale
3665+ # n.b. : this function does nearly all of the file validation, not just those checks related to stale, so the name is potentially unclear
36503666 io = open (cachefile, " r" )
36513667 try
36523668 checksum = isvalid_cache_header (io)
@@ -3700,8 +3716,8 @@ end
37003716 record_reason (reasons, " for different pkgid" )
37013717 return true
37023718 end
3719+ id_build = (UInt128 (checksum) << 64 ) | id. second
37033720 if build_id != UInt128 (0 )
3704- id_build = (UInt128 (checksum) << 64 ) | id. second
37053721 if id_build != build_id
37063722 @debug " Ignoring cache file $cachefile for $modkey ($((UUID (id_build))) ) since it does not provide desired build_id ($((UUID (build_id))) )"
37073723 record_reason (reasons, " for different buildid" )
@@ -3716,8 +3732,12 @@ end
37163732 depmods = Vector {Any} (undef, ndeps)
37173733 for i in 1 : ndeps
37183734 req_key, req_build_id = required_modules[i]
3719- # Module is already loaded
3720- if root_module_exists (req_key)
3735+ # Check if module is already loaded
3736+ if ! stalecheck && haskey (loaded_precompiles, req_key => req_build_id)
3737+ M = loaded_precompiles[req_key => req_build_id]
3738+ @assert PkgId (M) == req_key && module_build_id (M) === req_build_id
3739+ depmods[i] = M
3740+ elseif root_module_exists (req_key)
37213741 M = root_module (req_key)
37223742 if PkgId (M) == req_key && module_build_id (M) === req_build_id
37233743 depmods[i] = M
@@ -3748,17 +3768,19 @@ end
37483768 # check if this file is going to provide one of our concrete dependencies
37493769 # or if it provides a version that conflicts with our concrete dependencies
37503770 # or neither
3751- for (req_key, req_build_id) in _concrete_dependencies
3752- build_id = get (modules, req_key, UInt64 (0 ))
3753- if build_id != = UInt64 (0 )
3754- build_id |= UInt128 (checksum) << 64
3755- if build_id === req_build_id
3756- stalecheck = false
3757- break
3771+ if stalecheck
3772+ for (req_key, req_build_id) in _concrete_dependencies
3773+ build_id = get (modules, req_key, UInt64 (0 ))
3774+ if build_id != = UInt64 (0 )
3775+ build_id |= UInt128 (checksum) << 64
3776+ if build_id === req_build_id
3777+ stalecheck = false
3778+ break
3779+ end
3780+ @debug " Rejecting cache file $cachefile because it provides the wrong build_id (got $((UUID (build_id))) ) for $req_key (want $(UUID (req_build_id)) )"
3781+ record_reason (reasons, " wrong dep buildid" )
3782+ return true # cachefile doesn't provide the required version of the dependency
37583783 end
3759- @debug " Rejecting cache file $cachefile because it provides the wrong build_id (got $((UUID (build_id))) ) for $req_key (want $(UUID (req_build_id)) )"
3760- record_reason (reasons, " wrong dep buildid" )
3761- return true # cachefile doesn't provide the required version of the dependency
37623784 end
37633785 end
37643786
@@ -3846,7 +3868,7 @@ end
38463868 return true
38473869 end
38483870
3849- return depmods, ocachefile # fresh cachefile
3871+ return depmods, ocachefile, id_build # fresh cachefile
38503872 finally
38513873 close (io)
38523874 end
0 commit comments