@@ -1218,8 +1218,8 @@ function _include_from_serialized(pkg::PkgId, path::String, ocachepath::Union{No
1218
1218
dep = depmods[i]
1219
1219
dep isa Module && continue
1220
1220
_, 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
1223
1223
depmods[i] = dep
1224
1224
end
1225
1225
@@ -1276,7 +1276,8 @@ function register_restored_modules(sv::SimpleVector, pkg::PkgId, path::String)
1276
1276
push! (Base. Docs. modules, M)
1277
1277
end
1278
1278
if parentmodule (M) === M
1279
- register_root_module (M)
1279
+ push! (loaded_modules_order, M)
1280
+ loaded_precompiles[pkg => module_build_id (M)] = M
1280
1281
end
1281
1282
end
1282
1283
@@ -1703,7 +1704,7 @@ function compilecache_path(pkg::PkgId;
1703
1704
if staledeps === true
1704
1705
continue
1705
1706
end
1706
- staledeps, _ = staledeps:: Tuple{Vector{Any}, Union{Nothing, String}}
1707
+ staledeps, _, _ = staledeps:: Tuple{Vector{Any}, Union{Nothing, String}, UInt128 }
1707
1708
# finish checking staledeps module graph
1708
1709
for i in 1 : length (staledeps)
1709
1710
dep = staledeps[i]
@@ -1791,23 +1792,23 @@ end
1791
1792
# search for a precompile cache file to load, after some various checks
1792
1793
function _tryrequire_from_serialized (modkey:: PkgId , build_id:: UInt128 )
1793
1794
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
1797
1797
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)
1811
1812
end
1812
1813
end
1813
1814
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
1880
1881
depmods[i] = dep
1881
1882
end
1882
1883
# 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
1884
1887
end
1885
1888
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
1887
1890
# returns the set of modules restored if the cache load succeeded
1888
1891
@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 )
1889
1892
assert_havelock (require_lock)
@@ -1895,7 +1898,7 @@ end
1895
1898
continue
1896
1899
end
1897
1900
try
1898
- staledeps, ocachefile = staledeps:: Tuple{Vector{Any}, Union{Nothing, String}}
1901
+ staledeps, ocachefile, build_id = staledeps:: Tuple{Vector{Any}, Union{Nothing, String}, UInt128 }
1899
1902
# finish checking staledeps module graph
1900
1903
for i in 1 : length (staledeps)
1901
1904
dep = staledeps[i]
@@ -1907,14 +1910,19 @@ end
1907
1910
if modstaledeps === true
1908
1911
continue
1909
1912
end
1910
- modstaledeps, modocachepath = modstaledeps:: Tuple{Vector{Any}, Union{Nothing, String}}
1913
+ modstaledeps, modocachepath, _ = modstaledeps:: Tuple{Vector{Any}, Union{Nothing, String}, UInt128 }
1911
1914
staledeps[i] = (modpath, modkey, modbuild_id, modpath_to_try, modstaledeps, modocachepath)
1912
1915
@goto check_next_dep
1913
1916
end
1914
1917
@debug " Rejecting cache file $path_to_try because required dependency $modkey with build ID $(UUID (modbuild_id)) is missing from the cache."
1915
1918
@goto check_next_path
1916
1919
@label check_next_dep
1917
1920
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
1918
1926
if stalecheck
1919
1927
try
1920
1928
touch (path_to_try) # update timestamp of precompilation file
@@ -1927,26 +1935,25 @@ end
1927
1935
dep = staledeps[i]
1928
1936
dep isa Module && continue
1929
1937
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)
1933
1941
end
1934
1942
while true
1935
1943
if dep isa Module
1936
1944
if PkgId (dep) == modkey && module_build_id (dep) === modbuild_id
1937
1945
break
1938
1946
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
1943
1949
end
1944
1950
end
1945
1951
dep = start_loading (modkey)
1946
1952
if dep === nothing
1947
1953
try
1948
1954
set_pkgorigin_version_path (modkey, modpath)
1949
1955
dep = _include_from_serialized (modkey, modcachepath, modocachepath, modstaledeps)
1956
+ dep isa Module && stalecheck && register_root_module (dep)
1950
1957
finally
1951
1958
end_loading (modkey, dep)
1952
1959
end
@@ -1960,7 +1967,11 @@ end
1960
1967
end
1961
1968
staledeps[i] = dep
1962
1969
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)
1964
1975
isa (restored, Module) && return restored
1965
1976
@debug " Deserialization checks failed while attempting to load cache from $path_to_try " exception= restored
1966
1977
@label check_next_path
@@ -2046,7 +2057,7 @@ const package_callbacks = Any[]
2046
2057
const include_callbacks = Any[]
2047
2058
2048
2059
# 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
2050
2061
const _require_dependencies = Any[] # a list of (mod, abspath, fsize, hash, mtime) tuples that are the file dependencies of the module currently being precompiled
2051
2062
const _track_dependencies = Ref (false ) # set this to true to track the list of file dependencies
2052
2063
function _include_dependency (mod:: Module , _path:: AbstractString ; track_content= true ,
@@ -2296,14 +2307,19 @@ end
2296
2307
PkgOrigin () = PkgOrigin (nothing , nothing , nothing )
2297
2308
const pkgorigins = Dict {PkgId,PkgOrigin} ()
2298
2309
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
2302
2313
const 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
2304
2315
2305
2316
root_module_key (m:: Module ) = @lock require_lock module_keys[m]
2306
2317
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
+
2307
2323
@constprop :none function register_root_module (m:: Module )
2308
2324
# n.b. This is called from C after creating a new module in `Base.__toplevel__`,
2309
2325
# instead of adding them to the binding table there.
@@ -2319,7 +2335,7 @@ root_module_key(m::Module) = @lock require_lock module_keys[m]
2319
2335
end
2320
2336
end
2321
2337
end
2322
- push! (loaded_modules_order, m)
2338
+ haskey (loaded_precompiles, key => module_build_id (m)) || push! (loaded_modules_order, m)
2323
2339
loaded_modules[key] = m
2324
2340
explicit_loaded_modules[key] = m
2325
2341
module_keys[m] = key
@@ -2351,6 +2367,9 @@ root_module_exists(key::PkgId) = @lock require_lock haskey(loaded_modules, key)
2351
2367
loaded_modules_array () = @lock require_lock copy (loaded_modules_order)
2352
2368
2353
2369
function unreference_module (key:: PkgId )
2370
+ if haskey (explicit_loaded_modules, key)
2371
+ m = pop! (explicit_loaded_modules, key)
2372
+ end
2354
2373
if haskey (loaded_modules, key)
2355
2374
m = pop! (loaded_modules, key)
2356
2375
# need to ensure all modules are GC rooted; will still be referenced
@@ -2495,7 +2514,7 @@ function _require(pkg::PkgId, env=nothing)
2495
2514
return loaded
2496
2515
end
2497
2516
2498
- # load a serialized file directly
2517
+ # load a serialized file directly, including dependencies (without checking staleness except for immediate conflicts)
2499
2518
function _require_from_serialized (uuidkey:: PkgId , path:: String , ocachepath:: Union{String, Nothing} , sourcepath:: String )
2500
2519
@lock require_lock begin
2501
2520
set_pkgorigin_version_path (uuidkey, sourcepath)
@@ -2929,13 +2948,15 @@ function compilecache(pkg::PkgId, path::String, internal_stderr::IO = stderr, in
2929
2948
cachepath = compilecache_dir (pkg)
2930
2949
2931
2950
# build up the list of modules that we want the precompile process to preserve
2932
- concrete_deps = copy (_concrete_dependencies)
2933
2951
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))
2937
2956
end
2938
2957
end
2958
+ else
2959
+ concrete_deps = empty (_concrete_dependencies)
2939
2960
end
2940
2961
# run the expression and cache the result
2941
2962
verbosity = isinteractive () ? CoreLogging. Info : CoreLogging. Debug
@@ -3063,11 +3084,6 @@ function rename_unique_ocachefile(tmppath_so::String, ocachefile_orig::String, o
3063
3084
return ocachefile
3064
3085
end
3065
3086
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
-
3071
3087
function object_build_id (obj)
3072
3088
mod = ccall (:jl_object_top_module , Any, (Any,), obj)
3073
3089
if mod === nothing
@@ -3646,7 +3662,7 @@ end
3646
3662
@constprop :none function stale_cachefile (modkey:: PkgId , build_id:: UInt128 , modpath:: String , cachefile:: String ;
3647
3663
ignore_loaded:: Bool = false , requested_flags:: CacheFlags = CacheFlags (),
3648
3664
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
3650
3666
io = open (cachefile, " r" )
3651
3667
try
3652
3668
checksum = isvalid_cache_header (io)
@@ -3700,8 +3716,8 @@ end
3700
3716
record_reason (reasons, " for different pkgid" )
3701
3717
return true
3702
3718
end
3719
+ id_build = (UInt128 (checksum) << 64 ) | id. second
3703
3720
if build_id != UInt128 (0 )
3704
- id_build = (UInt128 (checksum) << 64 ) | id. second
3705
3721
if id_build != build_id
3706
3722
@debug " Ignoring cache file $cachefile for $modkey ($((UUID (id_build))) ) since it does not provide desired build_id ($((UUID (build_id))) )"
3707
3723
record_reason (reasons, " for different buildid" )
@@ -3716,8 +3732,12 @@ end
3716
3732
depmods = Vector {Any} (undef, ndeps)
3717
3733
for i in 1 : ndeps
3718
3734
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)
3721
3741
M = root_module (req_key)
3722
3742
if PkgId (M) == req_key && module_build_id (M) === req_build_id
3723
3743
depmods[i] = M
@@ -3748,17 +3768,19 @@ end
3748
3768
# check if this file is going to provide one of our concrete dependencies
3749
3769
# or if it provides a version that conflicts with our concrete dependencies
3750
3770
# 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
3758
3783
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
3762
3784
end
3763
3785
end
3764
3786
@@ -3846,7 +3868,7 @@ end
3846
3868
return true
3847
3869
end
3848
3870
3849
- return depmods, ocachefile # fresh cachefile
3871
+ return depmods, ocachefile, id_build # fresh cachefile
3850
3872
finally
3851
3873
close (io)
3852
3874
end
0 commit comments