@@ -1176,8 +1176,8 @@ function _include_from_serialized(pkg::PkgId, path::String, ocachepath::Union{No
1176
1176
dep = depmods[i]
1177
1177
dep isa Module && continue
1178
1178
_, depkey, depbuild_id = dep:: Tuple{String, PkgId, UInt128}
1179
- @assert root_module_exists ( depkey)
1180
- dep = root_module ( depkey)
1179
+ dep = loaded_precompiles[ depkey => depbuild_id]
1180
+ @assert PkgId ( dep) == depkey && module_build_id (dep) === depbuild_id
1181
1181
depmods[i] = dep
1182
1182
end
1183
1183
@@ -1234,7 +1234,8 @@ function register_restored_modules(sv::SimpleVector, pkg::PkgId, path::String)
1234
1234
push! (Base. Docs. modules, M)
1235
1235
end
1236
1236
if parentmodule (M) === M
1237
- register_root_module (M)
1237
+ push! (loaded_modules_order, M)
1238
+ loaded_precompiles[pkg => module_build_id (M)] = M
1238
1239
end
1239
1240
end
1240
1241
@@ -1654,7 +1655,7 @@ function compilecache_path(pkg::PkgId;
1654
1655
if staledeps === true
1655
1656
continue
1656
1657
end
1657
- staledeps, _ = staledeps:: Tuple{Vector{Any}, Union{Nothing, String}}
1658
+ staledeps, _, _ = staledeps:: Tuple{Vector{Any}, Union{Nothing, String}, UInt128 }
1658
1659
# finish checking staledeps module graph
1659
1660
for i in 1 : length (staledeps)
1660
1661
dep = staledeps[i]
@@ -1742,23 +1743,23 @@ end
1742
1743
# search for a precompile cache file to load, after some various checks
1743
1744
function _tryrequire_from_serialized (modkey:: PkgId , build_id:: UInt128 )
1744
1745
assert_havelock (require_lock)
1745
- if root_module_exists (modkey)
1746
- loaded = root_module (modkey)
1747
- else
1746
+ loaded = maybe_root_module (modkey)
1747
+ if loaded === nothing
1748
1748
loaded = start_loading (modkey)
1749
- if loaded === nothing
1750
- try
1751
- modpath = locate_package (modkey)
1752
- modpath === nothing && return nothing
1753
- set_pkgorigin_version_path (modkey, String (modpath))
1754
- loaded = _require_search_from_serialized (modkey, String (modpath), build_id, true )
1755
- finally
1756
- end_loading (modkey, loaded)
1757
- end
1758
- if loaded isa Module
1759
- insert_extension_triggers (modkey)
1760
- run_package_callbacks (modkey)
1761
- end
1749
+ end
1750
+ if loaded === nothing
1751
+ try
1752
+ modpath = locate_package (modkey)
1753
+ isnothing (modpath) && error (" Cannot locate source for $(repr (" text/plain" , modkey)) " )
1754
+ modpath = String (modpath):: String
1755
+ set_pkgorigin_version_path (modkey, modpath)
1756
+ loaded = _require_search_from_serialized (modkey, modpath, build_id, true )
1757
+ finally
1758
+ end_loading (modkey, loaded)
1759
+ end
1760
+ if loaded isa Module
1761
+ insert_extension_triggers (modkey)
1762
+ run_package_callbacks (modkey)
1762
1763
end
1763
1764
end
1764
1765
if loaded isa Module && PkgId (loaded) == modkey && module_build_id (loaded) === build_id
@@ -1831,10 +1832,12 @@ function _tryrequire_from_serialized(pkg::PkgId, path::String, ocachepath::Union
1831
1832
depmods[i] = dep
1832
1833
end
1833
1834
# then load the file
1834
- return _include_from_serialized (pkg, path, ocachepath, depmods, ignore_native)
1835
+ loaded = _include_from_serialized (pkg, path, ocachepath, depmods, ignore_native)
1836
+ loaded isa Module && register_root_module (loaded)
1837
+ return loaded
1835
1838
end
1836
1839
1837
- # returns `nothing` if require found a precompile cache for this sourcepath, but couldn't load it
1840
+ # returns `nothing` if require found a precompile cache for this sourcepath, but couldn't load it or it was stale
1838
1841
# returns the set of modules restored if the cache load succeeded
1839
1842
@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 )
1840
1843
assert_havelock (require_lock)
@@ -1846,7 +1849,7 @@ end
1846
1849
continue
1847
1850
end
1848
1851
try
1849
- staledeps, ocachefile = staledeps:: Tuple{Vector{Any}, Union{Nothing, String}}
1852
+ staledeps, ocachefile, build_id = staledeps:: Tuple{Vector{Any}, Union{Nothing, String}, UInt128 }
1850
1853
# finish checking staledeps module graph
1851
1854
for i in 1 : length (staledeps)
1852
1855
dep = staledeps[i]
@@ -1858,14 +1861,19 @@ end
1858
1861
if modstaledeps === true
1859
1862
continue
1860
1863
end
1861
- modstaledeps, modocachepath = modstaledeps:: Tuple{Vector{Any}, Union{Nothing, String}}
1864
+ modstaledeps, modocachepath, _ = modstaledeps:: Tuple{Vector{Any}, Union{Nothing, String}, UInt128 }
1862
1865
staledeps[i] = (modpath, modkey, modbuild_id, modpath_to_try, modstaledeps, modocachepath)
1863
1866
@goto check_next_dep
1864
1867
end
1865
1868
@debug " Rejecting cache file $path_to_try because required dependency $modkey with build ID $(UUID (modbuild_id)) is missing from the cache."
1866
1869
@goto check_next_path
1867
1870
@label check_next_dep
1868
1871
end
1872
+ M = get (loaded_precompiles, pkg => build_id, nothing )
1873
+ if isa (M, Module)
1874
+ stalecheck && register_root_module (M)
1875
+ return M
1876
+ end
1869
1877
if stalecheck
1870
1878
try
1871
1879
touch (path_to_try) # update timestamp of precompilation file
@@ -1878,26 +1886,25 @@ end
1878
1886
dep = staledeps[i]
1879
1887
dep isa Module && continue
1880
1888
modpath, modkey, modbuild_id, modcachepath, modstaledeps, modocachepath = dep:: Tuple{String, PkgId, UInt128, String, Vector{Any}, Union{Nothing, String}}
1881
- dep = nothing
1882
- if root_module_exists (modkey)
1883
- dep = root_module (modkey)
1889
+ dep = get (loaded_precompiles, modkey => modbuild_id, nothing )
1890
+ if dep === nothing
1891
+ dep = maybe_root_module (modkey)
1884
1892
end
1885
1893
while true
1886
1894
if dep isa Module
1887
1895
if PkgId (dep) == modkey && module_build_id (dep) === modbuild_id
1888
1896
break
1889
1897
else
1890
- if stalecheck
1891
- @debug " Rejecting cache file $path_to_try because module $modkey is already loaded and incompatible."
1892
- @goto check_next_path
1893
- end
1898
+ @debug " Rejecting cache file $path_to_try because module $modkey got loaded at a different version than expected."
1899
+ @goto check_next_path
1894
1900
end
1895
1901
end
1896
1902
dep = start_loading (modkey)
1897
1903
if dep === nothing
1898
1904
try
1899
1905
set_pkgorigin_version_path (modkey, modpath)
1900
1906
dep = _include_from_serialized (modkey, modcachepath, modocachepath, modstaledeps)
1907
+ dep isa Module && stalecheck && register_root_module (dep)
1901
1908
finally
1902
1909
end_loading (modkey, dep)
1903
1910
end
@@ -1911,7 +1918,11 @@ end
1911
1918
end
1912
1919
staledeps[i] = dep
1913
1920
end
1914
- restored = _include_from_serialized (pkg, path_to_try, ocachefile, staledeps)
1921
+ restored = get (loaded_precompiles, pkg => build_id, nothing )
1922
+ if ! isa (restored, Module)
1923
+ restored = _include_from_serialized (pkg, path_to_try, ocachefile, staledeps)
1924
+ end
1925
+ isa (restored, Module) && stalecheck && register_root_module (restored)
1915
1926
isa (restored, Module) && return restored
1916
1927
@debug " Deserialization checks failed while attempting to load cache from $path_to_try " exception= restored
1917
1928
@label check_next_path
@@ -2000,7 +2011,7 @@ const package_callbacks = Any[]
2000
2011
const include_callbacks = Any[]
2001
2012
2002
2013
# used to optionally track dependencies when requiring a module:
2003
- const _concrete_dependencies = Pair{PkgId,UInt128}[] # these dependency versions are "set in stone", and the process should try to avoid invalidating them
2014
+ 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
2004
2015
const _require_dependencies = Any[] # a list of (mod, abspath, fsize, hash, mtime) tuples that are the file dependencies of the module currently being precompiled
2005
2016
const _track_dependencies = Ref (false ) # set this to true to track the list of file dependencies
2006
2017
function _include_dependency (mod:: Module , _path:: AbstractString ; track_content= true ,
@@ -2251,15 +2262,20 @@ end
2251
2262
PkgOrigin () = PkgOrigin (nothing , nothing , nothing )
2252
2263
const pkgorigins = Dict {PkgId,PkgOrigin} ()
2253
2264
2254
- const loaded_modules = Dict {PkgId,Module} ()
2255
- # Emptied on Julia start
2256
- const explicit_loaded_modules = Dict {PkgId,Module} ()
2265
+ const explicit_loaded_modules = Dict {PkgId,Module} () # Emptied on Julia start
2266
+ const loaded_modules = Dict {PkgId,Module} () # available to be explicitly loaded
2267
+ const loaded_precompiles = Dict {Pair{ PkgId,UInt128}, Module} () # extended (complete) list of modules, available to be loaded
2257
2268
const loaded_modules_order = Vector {Module} ()
2258
- const module_keys = IdDict {Module,PkgId} () # the reverse
2269
+ const module_keys = IdDict {Module,PkgId} () # the reverse of loaded_modules
2259
2270
2260
2271
is_root_module (m:: Module ) = @lock require_lock haskey (module_keys, m)
2261
2272
root_module_key (m:: Module ) = @lock require_lock module_keys[m]
2262
2273
2274
+ function module_build_id (m:: Module )
2275
+ hi, lo = ccall (:jl_module_build_id , NTuple{2 ,UInt64}, (Any,), m)
2276
+ return (UInt128 (hi) << 64 ) | lo
2277
+ end
2278
+
2263
2279
@constprop :none function register_root_module (m:: Module )
2264
2280
# n.b. This is called from C after creating a new module in `Base.__toplevel__`,
2265
2281
# instead of adding them to the binding table there.
@@ -2275,7 +2291,7 @@ root_module_key(m::Module) = @lock require_lock module_keys[m]
2275
2291
end
2276
2292
end
2277
2293
end
2278
- push! (loaded_modules_order, m)
2294
+ haskey (loaded_precompiles, key => module_build_id (m)) || push! (loaded_modules_order, m)
2279
2295
loaded_modules[key] = m
2280
2296
explicit_loaded_modules[key] = m
2281
2297
module_keys[m] = key
@@ -2307,6 +2323,9 @@ root_module_exists(key::PkgId) = @lock require_lock haskey(loaded_modules, key)
2307
2323
loaded_modules_array () = @lock require_lock copy (loaded_modules_order)
2308
2324
2309
2325
function unreference_module (key:: PkgId )
2326
+ if haskey (explicit_loaded_modules, key)
2327
+ m = pop! (explicit_loaded_modules, key)
2328
+ end
2310
2329
if haskey (loaded_modules, key)
2311
2330
m = pop! (loaded_modules, key)
2312
2331
# need to ensure all modules are GC rooted; will still be referenced
@@ -2450,7 +2469,7 @@ function _require(pkg::PkgId, env=nothing)
2450
2469
return loaded
2451
2470
end
2452
2471
2453
- # load a serialized file directly
2472
+ # load a serialized file directly, including dependencies (without checking staleness except for immediate conflicts)
2454
2473
function _require_from_serialized (uuidkey:: PkgId , path:: String , ocachepath:: Union{String, Nothing} , sourcepath:: String )
2455
2474
@lock require_lock begin
2456
2475
set_pkgorigin_version_path (uuidkey, sourcepath)
@@ -2884,13 +2903,15 @@ function compilecache(pkg::PkgId, path::String, internal_stderr::IO = stderr, in
2884
2903
cachepath = compilecache_dir (pkg)
2885
2904
2886
2905
# build up the list of modules that we want the precompile process to preserve
2887
- concrete_deps = copy (_concrete_dependencies)
2888
2906
if keep_loaded_modules
2889
- for mod in loaded_modules_array ()
2890
- if ! (mod === Main || mod === Core || mod === Base)
2891
- push! (concrete_deps, PkgId (mod) => module_build_id (mod))
2907
+ concrete_deps = copy (_concrete_dependencies)
2908
+ for (pkgreq, modreq) in loaded_modules # TODO : convert all relevant staleness heuristics to use explicit_loaded_modules instead
2909
+ if ! (pkgreq === Main || pkgreq === Core || pkgreq === Base)
2910
+ push! (concrete_deps, pkgreq => module_build_id (modreq))
2892
2911
end
2893
2912
end
2913
+ else
2914
+ concrete_deps = empty (_concrete_dependencies)
2894
2915
end
2895
2916
# run the expression and cache the result
2896
2917
verbosity = isinteractive () ? CoreLogging. Info : CoreLogging. Debug
@@ -3013,11 +3034,6 @@ function compilecache(pkg::PkgId, path::String, internal_stderr::IO = stderr, in
3013
3034
end
3014
3035
end
3015
3036
3016
- function module_build_id (m:: Module )
3017
- hi, lo = ccall (:jl_module_build_id , NTuple{2 ,UInt64}, (Any,), m)
3018
- return (UInt128 (hi) << 64 ) | lo
3019
- end
3020
-
3021
3037
function isvalid_cache_header (f:: IOStream )
3022
3038
pkgimage = Ref {UInt8} ()
3023
3039
checksum = ccall (:jl_read_verify_header , UInt64, (Ptr{Cvoid}, Ptr{UInt8}, Ptr{Int64}, Ptr{Int64}), f. ios, pkgimage, Ref {Int64} (), Ref {Int64} ()) # returns checksum id or zero
@@ -3570,7 +3586,7 @@ end
3570
3586
@constprop :none function stale_cachefile (modkey:: PkgId , build_id:: UInt128 , modpath:: String , cachefile:: String ;
3571
3587
ignore_loaded:: Bool = false , requested_flags:: CacheFlags = CacheFlags (),
3572
3588
reasons:: Union{Dict{String,Int},Nothing} = nothing , stalecheck:: Bool = true )
3573
- # XXX : this function appears to dl all of the file validation, not just those checks related to stale
3589
+ # n.b. : this function does nearly all of the file validation, not just those checks related to stale, so the name is potentially unclear
3574
3590
io = open (cachefile, " r" )
3575
3591
try
3576
3592
checksum = isvalid_cache_header (io)
@@ -3624,8 +3640,8 @@ end
3624
3640
record_reason (reasons, " for different pkgid" )
3625
3641
return true
3626
3642
end
3643
+ id_build = (UInt128 (checksum) << 64 ) | id. second
3627
3644
if build_id != UInt128 (0 )
3628
- id_build = (UInt128 (checksum) << 64 ) | id. second
3629
3645
if id_build != build_id
3630
3646
@debug " Ignoring cache file $cachefile for $modkey ($((UUID (id_build))) ) since it does not provide desired build_id ($((UUID (build_id))) )"
3631
3647
record_reason (reasons, " for different buildid" )
@@ -3640,8 +3656,12 @@ end
3640
3656
depmods = Vector {Any} (undef, ndeps)
3641
3657
for i in 1 : ndeps
3642
3658
req_key, req_build_id = required_modules[i]
3643
- # Module is already loaded
3644
- if root_module_exists (req_key)
3659
+ # Check if module is already loaded
3660
+ if ! stalecheck && haskey (loaded_precompiles, req_key => req_build_id)
3661
+ M = loaded_precompiles[req_key => req_build_id]
3662
+ @assert PkgId (M) == req_key && module_build_id (M) === req_build_id
3663
+ depmods[i] = M
3664
+ elseif root_module_exists (req_key)
3645
3665
M = root_module (req_key)
3646
3666
if PkgId (M) == req_key && module_build_id (M) === req_build_id
3647
3667
depmods[i] = M
@@ -3672,17 +3692,19 @@ end
3672
3692
# check if this file is going to provide one of our concrete dependencies
3673
3693
# or if it provides a version that conflicts with our concrete dependencies
3674
3694
# or neither
3675
- for (req_key, req_build_id) in _concrete_dependencies
3676
- build_id = get (modules, req_key, UInt64 (0 ))
3677
- if build_id != = UInt64 (0 )
3678
- build_id |= UInt128 (checksum) << 64
3679
- if build_id === req_build_id
3680
- stalecheck = false
3681
- break
3695
+ if stalecheck
3696
+ for (req_key, req_build_id) in _concrete_dependencies
3697
+ build_id = get (modules, req_key, UInt64 (0 ))
3698
+ if build_id != = UInt64 (0 )
3699
+ build_id |= UInt128 (checksum) << 64
3700
+ if build_id === req_build_id
3701
+ stalecheck = false
3702
+ break
3703
+ end
3704
+ @debug " Rejecting cache file $cachefile because it provides the wrong build_id (got $((UUID (build_id))) ) for $req_key (want $(UUID (req_build_id)) )"
3705
+ record_reason (reasons, " wrong dep buildid" )
3706
+ return true # cachefile doesn't provide the required version of the dependency
3682
3707
end
3683
- @debug " Rejecting cache file $cachefile because it provides the wrong build_id (got $((UUID (build_id))) ) for $req_key (want $(UUID (req_build_id)) )"
3684
- record_reason (reasons, " wrong dep buildid" )
3685
- return true # cachefile doesn't provide the required version of the dependency
3686
3708
end
3687
3709
end
3688
3710
@@ -3770,7 +3792,7 @@ end
3770
3792
return true
3771
3793
end
3772
3794
3773
- return depmods, ocachefile # fresh cachefile
3795
+ return depmods, ocachefile, id_build # fresh cachefile
3774
3796
finally
3775
3797
close (io)
3776
3798
end
0 commit comments