@@ -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"  " 
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