@@ -260,7 +260,7 @@ struct LoadingCache
260260 identified:: Dict{String, Union{Nothing, Tuple{PkgId, String}}}
261261 located:: Dict{Tuple{PkgId, Union{String, Nothing}}, Union{Tuple{String, String}, Nothing}}
262262end
263- const LOADING_CACHE = Ref {Union{LoadingCache, Nothing}} (nothing )
263+ const LOADING_CACHE = Ref {Union{LoadingCache, Nothing}} (nothing ) # n.b.: all access to and through this are protected by require_lock
264264LoadingCache () = LoadingCache (load_path (), Dict (), Dict (), Dict (), Set (), Dict (), Dict (), Dict ())
265265
266266
@@ -302,10 +302,12 @@ end
302302
303303# Used by Pkg but not used in loading itself
304304function find_package (arg) # ::Union{Nothing,String}
305+ @lock require_lock begin
305306 pkgenv = identify_package_env (arg)
306307 pkgenv === nothing && return nothing
307308 pkg, env = pkgenv
308309 return locate_package (pkg, env)
310+ end
309311end
310312
311313# is there a better/faster ground truth?
@@ -332,6 +334,7 @@ is also returned, except when the identity is not identified.
332334"""
333335identify_package_env (where :: Module , name:: String ) = identify_package_env (PkgId (where ), name)
334336function identify_package_env (where :: PkgId , name:: String )
337+ assert_havelock (require_lock)
335338 cache = LOADING_CACHE[]
336339 if cache != = nothing
337340 pkg_env = get (cache. identified_where, (where , name), missing )
@@ -364,6 +367,7 @@ function identify_package_env(where::PkgId, name::String)
364367 return pkg_env
365368end
366369function identify_package_env (name:: String )
370+ assert_havelock (require_lock)
367371 cache = LOADING_CACHE[]
368372 if cache != = nothing
369373 pkg_env = get (cache. identified, name, missing )
@@ -426,11 +430,12 @@ julia> using LinearAlgebra
426430julia> Base.identify_package(LinearAlgebra, "Pkg") # Pkg is not a dependency of LinearAlgebra
427431```
428432"""
429- identify_package (where :: Module , name:: String ) = _nothing_or_first (identify_package_env (where , name))
430- identify_package (where :: PkgId , name:: String ) = _nothing_or_first (identify_package_env (where , name))
431- identify_package (name:: String ) = _nothing_or_first (identify_package_env (name))
433+ identify_package (where :: Module , name:: String ) = @lock require_lock _nothing_or_first (identify_package_env (where , name))
434+ identify_package (where :: PkgId , name:: String ) = @lock require_lock _nothing_or_first (identify_package_env (where , name))
435+ identify_package (name:: String ) = @lock require_lock _nothing_or_first (identify_package_env (name))
432436
433437function locate_package_env (pkg:: PkgId , stopenv:: Union{String, Nothing} = nothing ):: Union{Nothing,Tuple{String,String}}
438+ assert_havelock (require_lock)
434439 cache = LOADING_CACHE[]
435440 if cache != = nothing
436441 pathenv = get (cache. located, (pkg, stopenv), missing )
@@ -508,7 +513,7 @@ julia> Base.locate_package(pkg)
508513```
509514"""
510515function locate_package (pkg:: PkgId , stopenv:: Union{String, Nothing} = nothing ):: Union{Nothing,String}
511- _nothing_or_first (locate_package_env (pkg, stopenv))
516+ @lock require_lock _nothing_or_first (locate_package_env (pkg, stopenv))
512517end
513518
514519"""
@@ -1810,51 +1815,60 @@ function show(io::IO, it::ImageTarget)
18101815end
18111816
18121817# should sync with the types of arguments of `stale_cachefile`
1813- const StaleCacheKey = Tuple{PkgId, UInt128, String, String}
1818+ const StaleCacheKey = Tuple{PkgId, UInt128, String, String, Bool, CacheFlags }
18141819
1815- function compilecache_path (pkg:: PkgId ;
1820+ function compilecache_freshest_path (pkg:: PkgId ;
18161821 ignore_loaded:: Bool = false ,
18171822 stale_cache:: Dict{StaleCacheKey,Bool} = Dict {StaleCacheKey, Bool} (),
18181823 cachepath_cache:: Dict{PkgId, Vector{String}} = Dict {PkgId, Vector{String}} (),
1819- cachepaths:: Vector{String} = get! (() -> find_all_in_cache_path (pkg), cachepath_cache, pkg),
1824+ cachepaths:: Vector{String} = get (() -> find_all_in_cache_path (pkg), cachepath_cache, pkg),
18201825 sourcepath:: Union{String,Nothing} = Base. locate_package (pkg),
18211826 flags:: CacheFlags = CacheFlags ())
1822- path = nothing
18231827 isnothing (sourcepath) && error (" Cannot locate source for $(repr (" text/plain" , pkg)) " )
1824- for path_to_try in cachepaths
1825- staledeps = stale_cachefile (sourcepath, path_to_try; ignore_loaded, requested_flags= flags)
1826- if staledeps === true
1827- continue
1828- end
1829- staledeps, _, _ = staledeps:: Tuple{Vector{Any}, Union{Nothing, String}, UInt128}
1830- # finish checking staledeps module graph
1831- for dep in staledeps
1832- dep isa Module && continue
1833- modpath, modkey, modbuild_id = dep:: Tuple{String, PkgId, UInt128}
1834- modpaths = get! (() -> find_all_in_cache_path (modkey), cachepath_cache, modkey)
1835- for modpath_to_try in modpaths:: Vector{String}
1836- stale_cache_key = (modkey, modbuild_id, modpath, modpath_to_try):: StaleCacheKey
1837- if get! (() -> stale_cachefile (stale_cache_key... ; ignore_loaded, requested_flags= flags) === true ,
1838- stale_cache, stale_cache_key)
1839- continue
1828+ try_build_ids = UInt128[UInt128 (0 )]
1829+ if ! ignore_loaded
1830+ let loaded = get (loaded_precompiles, pkg, nothing )
1831+ if loaded != = nothing
1832+ for mod in loaded # try these in reverse original load order to see if one is already valid
1833+ pushfirst! (try_build_ids, module_build_id (mod))
18401834 end
1841- @goto check_next_dep
18421835 end
1843- @goto check_next_path
1844- @label check_next_dep
18451836 end
1846- try
1847- # update timestamp of precompilation file so that it is the first to be tried by code loading
1848- touch (path_to_try)
1849- catch ex
1850- # file might be read-only and then we fail to update timestamp, which is fine
1851- ex isa IOError || rethrow ()
1837+ end
1838+ for build_id in try_build_ids
1839+ for path_to_try in cachepaths
1840+ staledeps = stale_cachefile (pkg, build_id, sourcepath, path_to_try; ignore_loaded, requested_flags= flags)
1841+ if staledeps === true
1842+ continue
1843+ end
1844+ staledeps, _, _ = staledeps:: Tuple{Vector{Any}, Union{Nothing, String}, UInt128}
1845+ # finish checking staledeps module graph
1846+ for dep in staledeps
1847+ dep isa Module && continue
1848+ modpath, modkey, modbuild_id = dep:: Tuple{String, PkgId, UInt128}
1849+ modpaths = get (() -> find_all_in_cache_path (modkey), cachepath_cache, modkey)
1850+ for modpath_to_try in modpaths:: Vector{String}
1851+ stale_cache_key = (modkey, modbuild_id, modpath, modpath_to_try, ignore_loaded, flags):: StaleCacheKey
1852+ if get! (() -> stale_cachefile (modkey, modbuild_id, modpath, modpath_to_try; ignore_loaded, requested_flags= flags) === true ,
1853+ stale_cache, stale_cache_key)
1854+ continue
1855+ end
1856+ @goto check_next_dep
1857+ end
1858+ @goto check_next_path
1859+ @label check_next_dep
1860+ end
1861+ try
1862+ # update timestamp of precompilation file so that it is the first to be tried by code loading
1863+ touch (path_to_try)
1864+ catch ex
1865+ # file might be read-only and then we fail to update timestamp, which is fine
1866+ ex isa IOError || rethrow ()
1867+ end
1868+ return path_to_try
1869+ @label check_next_path
18521870 end
1853- path = path_to_try
1854- break
1855- @label check_next_path
18561871 end
1857- return path
18581872end
18591873
18601874"""
@@ -1870,14 +1884,8 @@ fresh julia session specify `ignore_loaded=true`.
18701884!!! compat "Julia 1.10"
18711885 This function requires at least Julia 1.10.
18721886"""
1873- function isprecompiled (pkg:: PkgId ;
1874- ignore_loaded:: Bool = false ,
1875- stale_cache:: Dict{StaleCacheKey,Bool} = Dict {StaleCacheKey, Bool} (),
1876- cachepath_cache:: Dict{PkgId, Vector{String}} = Dict {PkgId, Vector{String}} (),
1877- cachepaths:: Vector{String} = get! (() -> find_all_in_cache_path (pkg), cachepath_cache, pkg),
1878- sourcepath:: Union{String,Nothing} = Base. locate_package (pkg),
1879- flags:: CacheFlags = CacheFlags ())
1880- path = compilecache_path (pkg; ignore_loaded, stale_cache, cachepath_cache, cachepaths, sourcepath, flags)
1887+ function isprecompiled (pkg:: PkgId ; ignore_loaded:: Bool = false )
1888+ path = compilecache_freshest_path (pkg; ignore_loaded)
18811889 return ! isnothing (path)
18821890end
18831891
@@ -1891,7 +1899,7 @@ associated cache is relocatable.
18911899 This function requires at least Julia 1.11.
18921900"""
18931901function isrelocatable (pkg:: PkgId )
1894- path = compilecache_path (pkg)
1902+ path = compilecache_freshest_path (pkg)
18951903 isnothing (path) && return false
18961904 io = open (path, " r" )
18971905 try
@@ -1911,6 +1919,23 @@ function isrelocatable(pkg::PkgId)
19111919 return true
19121920end
19131921
1922+ function parse_cache_buildid (cachepath:: String )
1923+ f = open (cachepath, " r" )
1924+ try
1925+ checksum = isvalid_cache_header (f)
1926+ iszero (checksum) && throw (ArgumentError (" Incompatible header in cache file $cachefile ." ))
1927+ flags = read (f, UInt8)
1928+ n = read (f, Int32)
1929+ n == 0 && error (" no module defined in $cachefile " )
1930+ skip (f, n) # module name
1931+ uuid = UUID ((read (f, UInt64), read (f, UInt64))) # pkg UUID
1932+ build_id = (UInt128 (checksum) << 64 ) | read (f, UInt64)
1933+ return build_id, uuid
1934+ finally
1935+ close (f)
1936+ end
1937+ end
1938+
19141939# search for a precompile cache file to load, after some various checks
19151940function _tryrequire_from_serialized (modkey:: PkgId , build_id:: UInt128 )
19161941 assert_havelock (require_lock)
@@ -2636,8 +2661,19 @@ function __require_prelocked(pkg::PkgId, env)
26362661 try
26372662 if ! generating_output () && ! parallel_precompile_attempted && ! disable_parallel_precompile && @isdefined (Precompilation)
26382663 parallel_precompile_attempted = true
2639- Precompilation. precompilepkgs ([pkg]; _from_loading= true , ignore_loaded= false )
2640- return
2664+ precompiled = Precompilation. precompilepkgs ([pkg]; _from_loading= true , ignore_loaded= false )
2665+ # prcompiled returns either nothing, indicating it needs serial precompile,
2666+ # or the entry(ies) that it found would be best to load (possibly because it just created it)
2667+ # or an empty set of entries (indicating the precompile should be skipped)
2668+ if precompiled != = nothing
2669+ isempty (precompiled) && return PrecompilableError () # oops, Precompilation forgot to report what this might actually be
2670+ local cachefile = precompiled[1 ]
2671+ local ocachefile = nothing
2672+ if JLOptions (). use_pkgimages == 1
2673+ ocachefile = ocachefile_from_cachefile (cachefile)
2674+ end
2675+ return cachefile, ocachefile
2676+ end
26412677 end
26422678 triggers = get (EXT_PRIMED, pkg, nothing )
26432679 loadable_exts = nothing
0 commit comments