@@ -260,7 +260,7 @@ struct LoadingCache
260
260
identified:: Dict{String, Union{Nothing, Tuple{PkgId, String}}}
261
261
located:: Dict{Tuple{PkgId, Union{String, Nothing}}, Union{Tuple{String, String}, Nothing}}
262
262
end
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
264
264
LoadingCache () = LoadingCache (load_path (), Dict (), Dict (), Dict (), Set (), Dict (), Dict (), Dict ())
265
265
266
266
@@ -302,10 +302,12 @@ end
302
302
303
303
# Used by Pkg but not used in loading itself
304
304
function find_package (arg) # ::Union{Nothing,String}
305
+ @lock require_lock begin
305
306
pkgenv = identify_package_env (arg)
306
307
pkgenv === nothing && return nothing
307
308
pkg, env = pkgenv
308
309
return locate_package (pkg, env)
310
+ end
309
311
end
310
312
311
313
# is there a better/faster ground truth?
@@ -332,6 +334,7 @@ is also returned, except when the identity is not identified.
332
334
"""
333
335
identify_package_env (where :: Module , name:: String ) = identify_package_env (PkgId (where ), name)
334
336
function identify_package_env (where :: PkgId , name:: String )
337
+ assert_havelock (require_lock)
335
338
cache = LOADING_CACHE[]
336
339
if cache != = nothing
337
340
pkg_env = get (cache. identified_where, (where , name), missing )
@@ -364,6 +367,7 @@ function identify_package_env(where::PkgId, name::String)
364
367
return pkg_env
365
368
end
366
369
function identify_package_env (name:: String )
370
+ assert_havelock (require_lock)
367
371
cache = LOADING_CACHE[]
368
372
if cache != = nothing
369
373
pkg_env = get (cache. identified, name, missing )
@@ -426,11 +430,12 @@ julia> using LinearAlgebra
426
430
julia> Base.identify_package(LinearAlgebra, "Pkg") # Pkg is not a dependency of LinearAlgebra
427
431
```
428
432
"""
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))
432
436
433
437
function locate_package_env (pkg:: PkgId , stopenv:: Union{String, Nothing} = nothing ):: Union{Nothing,Tuple{String,String}}
438
+ assert_havelock (require_lock)
434
439
cache = LOADING_CACHE[]
435
440
if cache != = nothing
436
441
pathenv = get (cache. located, (pkg, stopenv), missing )
@@ -508,7 +513,7 @@ julia> Base.locate_package(pkg)
508
513
```
509
514
"""
510
515
function 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))
512
517
end
513
518
514
519
"""
@@ -1824,51 +1829,60 @@ function show(io::IO, it::ImageTarget)
1824
1829
end
1825
1830
1826
1831
# 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 }
1828
1833
1829
- function compilecache_path (pkg:: PkgId ;
1834
+ function compilecache_freshest_path (pkg:: PkgId ;
1830
1835
ignore_loaded:: Bool = false ,
1831
1836
stale_cache:: Dict{StaleCacheKey,Bool} = Dict {StaleCacheKey, Bool} (),
1832
1837
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),
1834
1839
sourcepath:: Union{String,Nothing} = Base. locate_package (pkg),
1835
1840
flags:: CacheFlags = CacheFlags ())
1836
- path = nothing
1837
1841
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))
1854
1848
end
1855
- @goto check_next_dep
1856
1849
end
1857
- @goto check_next_path
1858
- @label check_next_dep
1859
1850
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
1866
1884
end
1867
- path = path_to_try
1868
- break
1869
- @label check_next_path
1870
1885
end
1871
- return path
1872
1886
end
1873
1887
1874
1888
"""
@@ -1884,14 +1898,8 @@ fresh julia session specify `ignore_loaded=true`.
1884
1898
!!! compat "Julia 1.10"
1885
1899
This function requires at least Julia 1.10.
1886
1900
"""
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)
1895
1903
return ! isnothing (path)
1896
1904
end
1897
1905
@@ -1905,7 +1913,7 @@ associated cache is relocatable.
1905
1913
This function requires at least Julia 1.11.
1906
1914
"""
1907
1915
function isrelocatable (pkg:: PkgId )
1908
- path = compilecache_path (pkg)
1916
+ path = compilecache_freshest_path (pkg)
1909
1917
isnothing (path) && return false
1910
1918
io = open (path, " r" )
1911
1919
try
@@ -1925,6 +1933,23 @@ function isrelocatable(pkg::PkgId)
1925
1933
return true
1926
1934
end
1927
1935
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
+
1928
1953
# search for a precompile cache file to load, after some various checks
1929
1954
function _tryrequire_from_serialized (modkey:: PkgId , build_id:: UInt128 )
1930
1955
assert_havelock (require_lock)
@@ -2682,8 +2707,19 @@ function __require_prelocked(pkg::PkgId, env)
2682
2707
try
2683
2708
if ! generating_output () && ! parallel_precompile_attempted && ! disable_parallel_precompile && @isdefined (Precompilation)
2684
2709
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
2687
2723
end
2688
2724
triggers = get (EXT_PRIMED, pkg, nothing )
2689
2725
loadable_exts = nothing
0 commit comments