@@ -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"""
@@ -1824,51 +1829,60 @@ function show(io::IO, it::ImageTarget)
18241829end
18251830
18261831# should sync with the types of arguments of `stale_cachefile`
1827- const StaleCacheKey = Tuple{PkgId, UInt128, String, String}
1832+ const StaleCacheKey = Tuple{PkgId, UInt128, String, String, Bool, CacheFlags }
18281833
1829- function compilecache_path (pkg:: PkgId ;
1834+ function compilecache_freshest_path (pkg:: PkgId ;
18301835 ignore_loaded:: Bool = false ,
18311836 stale_cache:: Dict{StaleCacheKey,Bool} = Dict {StaleCacheKey, Bool} (),
18321837 cachepath_cache:: Dict{PkgId, Vector{String}} = Dict {PkgId, Vector{String}} (),
1833- cachepaths:: Vector{String} = get! (() -> find_all_in_cache_path (pkg), cachepath_cache, pkg),
1838+ cachepaths:: Vector{String} = get (() -> find_all_in_cache_path (pkg), cachepath_cache, pkg),
18341839 sourcepath:: Union{String,Nothing} = Base. locate_package (pkg),
18351840 flags:: CacheFlags = CacheFlags ())
1836- path = nothing
18371841 isnothing (sourcepath) && error (" Cannot locate source for $(repr (" text/plain" , pkg)) " )
1838- for path_to_try in cachepaths
1839- staledeps = stale_cachefile (sourcepath, path_to_try; ignore_loaded, requested_flags= flags)
1840- if staledeps === true
1841- continue
1842- end
1843- staledeps, _, _ = staledeps:: Tuple{Vector{Any}, Union{Nothing, String}, UInt128}
1844- # finish checking staledeps module graph
1845- for dep in staledeps
1846- dep isa Module && continue
1847- modpath, modkey, modbuild_id = dep:: Tuple{String, PkgId, UInt128}
1848- modpaths = get! (() -> find_all_in_cache_path (modkey), cachepath_cache, modkey)
1849- for modpath_to_try in modpaths:: Vector{String}
1850- stale_cache_key = (modkey, modbuild_id, modpath, modpath_to_try):: StaleCacheKey
1851- if get! (() -> stale_cachefile (stale_cache_key... ; ignore_loaded, requested_flags= flags) === true ,
1852- stale_cache, stale_cache_key)
1853- continue
1842+ try_build_ids = UInt128[UInt128 (0 )]
1843+ if ! ignore_loaded
1844+ let loaded = get (loaded_precompiles, pkg, nothing )
1845+ if loaded != = nothing
1846+ for mod in loaded # try these in reverse original load order to see if one is already valid
1847+ pushfirst! (try_build_ids, module_build_id (mod))
18541848 end
1855- @goto check_next_dep
18561849 end
1857- @goto check_next_path
1858- @label check_next_dep
18591850 end
1860- try
1861- # update timestamp of precompilation file so that it is the first to be tried by code loading
1862- touch (path_to_try)
1863- catch ex
1864- # file might be read-only and then we fail to update timestamp, which is fine
1865- ex isa IOError || rethrow ()
1851+ end
1852+ for build_id in try_build_ids
1853+ for path_to_try in cachepaths
1854+ staledeps = stale_cachefile (pkg, build_id, sourcepath, path_to_try; ignore_loaded, requested_flags= flags)
1855+ if staledeps === true
1856+ continue
1857+ end
1858+ staledeps, _, _ = staledeps:: Tuple{Vector{Any}, Union{Nothing, String}, UInt128}
1859+ # finish checking staledeps module graph
1860+ for dep in staledeps
1861+ dep isa Module && continue
1862+ modpath, modkey, modbuild_id = dep:: Tuple{String, PkgId, UInt128}
1863+ modpaths = get (() -> find_all_in_cache_path (modkey), cachepath_cache, modkey)
1864+ for modpath_to_try in modpaths:: Vector{String}
1865+ stale_cache_key = (modkey, modbuild_id, modpath, modpath_to_try, ignore_loaded, flags):: StaleCacheKey
1866+ if get! (() -> stale_cachefile (modkey, modbuild_id, modpath, modpath_to_try; ignore_loaded, requested_flags= flags) === true ,
1867+ stale_cache, stale_cache_key)
1868+ continue
1869+ end
1870+ @goto check_next_dep
1871+ end
1872+ @goto check_next_path
1873+ @label check_next_dep
1874+ end
1875+ try
1876+ # update timestamp of precompilation file so that it is the first to be tried by code loading
1877+ touch (path_to_try)
1878+ catch ex
1879+ # file might be read-only and then we fail to update timestamp, which is fine
1880+ ex isa IOError || rethrow ()
1881+ end
1882+ return path_to_try
1883+ @label check_next_path
18661884 end
1867- path = path_to_try
1868- break
1869- @label check_next_path
18701885 end
1871- return path
18721886end
18731887
18741888"""
@@ -1884,14 +1898,8 @@ fresh julia session specify `ignore_loaded=true`.
18841898!!! compat "Julia 1.10"
18851899 This function requires at least Julia 1.10.
18861900"""
1887- function isprecompiled (pkg:: PkgId ;
1888- ignore_loaded:: Bool = false ,
1889- stale_cache:: Dict{StaleCacheKey,Bool} = Dict {StaleCacheKey, Bool} (),
1890- cachepath_cache:: Dict{PkgId, Vector{String}} = Dict {PkgId, Vector{String}} (),
1891- cachepaths:: Vector{String} = get! (() -> find_all_in_cache_path (pkg), cachepath_cache, pkg),
1892- sourcepath:: Union{String,Nothing} = Base. locate_package (pkg),
1893- flags:: CacheFlags = CacheFlags ())
1894- path = compilecache_path (pkg; ignore_loaded, stale_cache, cachepath_cache, cachepaths, sourcepath, flags)
1901+ function isprecompiled (pkg:: PkgId ; ignore_loaded:: Bool = false )
1902+ path = compilecache_freshest_path (pkg; ignore_loaded)
18951903 return ! isnothing (path)
18961904end
18971905
@@ -1905,7 +1913,7 @@ associated cache is relocatable.
19051913 This function requires at least Julia 1.11.
19061914"""
19071915function isrelocatable (pkg:: PkgId )
1908- path = compilecache_path (pkg)
1916+ path = compilecache_freshest_path (pkg)
19091917 isnothing (path) && return false
19101918 io = open (path, " r" )
19111919 try
@@ -1925,6 +1933,23 @@ function isrelocatable(pkg::PkgId)
19251933 return true
19261934end
19271935
1936+ function parse_cache_buildid (cachepath:: String )
1937+ f = open (cachepath, " r" )
1938+ try
1939+ checksum = isvalid_cache_header (f)
1940+ iszero (checksum) && throw (ArgumentError (" Incompatible header in cache file $cachefile ." ))
1941+ flags = read (f, UInt8)
1942+ n = read (f, Int32)
1943+ n == 0 && error (" no module defined in $cachefile " )
1944+ skip (f, n) # module name
1945+ uuid = UUID ((read (f, UInt64), read (f, UInt64))) # pkg UUID
1946+ build_id = (UInt128 (checksum) << 64 ) | read (f, UInt64)
1947+ return build_id, uuid
1948+ finally
1949+ close (f)
1950+ end
1951+ end
1952+
19281953# search for a precompile cache file to load, after some various checks
19291954function _tryrequire_from_serialized (modkey:: PkgId , build_id:: UInt128 )
19301955 assert_havelock (require_lock)
@@ -2682,8 +2707,19 @@ function __require_prelocked(pkg::PkgId, env)
26822707 try
26832708 if ! generating_output () && ! parallel_precompile_attempted && ! disable_parallel_precompile && @isdefined (Precompilation)
26842709 parallel_precompile_attempted = true
2685- Precompilation. precompilepkgs ([pkg]; _from_loading= true , ignore_loaded= false )
2686- return
2710+ precompiled = Precompilation. precompilepkgs ([pkg]; _from_loading= true , ignore_loaded= false )
2711+ # prcompiled returns either nothing, indicating it needs serial precompile,
2712+ # or the entry(ies) that it found would be best to load (possibly because it just created it)
2713+ # or an empty set of entries (indicating the precompile should be skipped)
2714+ if precompiled != = nothing
2715+ isempty (precompiled) && return PrecompilableError () # oops, Precompilation forgot to report what this might actually be
2716+ local cachefile = precompiled[1 ]
2717+ local ocachefile = nothing
2718+ if JLOptions (). use_pkgimages == 1
2719+ ocachefile = ocachefile_from_cachefile (cachefile)
2720+ end
2721+ return cachefile, ocachefile
2722+ end
26872723 end
26882724 triggers = get (EXT_PRIMED, pkg, nothing )
26892725 loadable_exts = nothing
0 commit comments