Skip to content

Commit 4ea7216

Browse files
committed
prevent doing excessive file system operations in require calls
1 parent 27066a7 commit 4ea7216

File tree

2 files changed

+104
-46
lines changed

2 files changed

+104
-46
lines changed

base/initdefs.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,8 @@ Return the fully expanded value of [`LOAD_PATH`](@ref) that is searched for proj
314314
packages.
315315
"""
316316
function load_path()
317+
cache = LOADING_CACHE[]
318+
cache !== nothing && return cache.load_path
317319
paths = String[]
318320
for env in LOAD_PATH
319321
path = load_path_expand(env)

base/loading.jl

Lines changed: 102 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -129,12 +129,21 @@ end
129129
const ns_dummy_uuid = UUID("fe0723d6-3a44-4c41-8065-ee0f42c8ceab")
130130

131131
function dummy_uuid(project_file::String)
132+
cache = LOADING_CACHE[]
133+
if cache !== nothing
134+
uuid = get(cache.dummy_uuid, project_file, nothing)
135+
uuid === nothing || return uuid
136+
end
132137
project_path = try
133138
realpath(project_file)
134139
catch
135140
project_file
136141
end
137-
return uuid5(ns_dummy_uuid, project_path)
142+
uuid = uuid5(ns_dummy_uuid, project_path)
143+
if cache !== nothing
144+
cache.dummy_uuid[project_file] = uuid
145+
end
146+
return uuid
138147
end
139148

140149
## package path slugs: turning UUID + SHA1 into a pair of 4-byte "slugs" ##
@@ -210,6 +219,16 @@ function get_updated_dict(p::TOML.Parser, f::CachedTOMLDict)
210219
return f.d
211220
end
212221

222+
struct LoadingCache
223+
load_path::Vector{String}
224+
dummy_uuid::Dict{String, UUID}
225+
env_project_file::Dict{String, Union{Bool, String}}
226+
project_file_manifest_path::Dict{String, Union{Nothing, String}}
227+
end
228+
const LOADING_CACHE = Ref{Union{LoadingCache, Nothing}}(nothing)
229+
LoadingCache() = LoadingCache(load_path(), Dict(), Dict(), Dict())
230+
231+
213232
struct TOMLCache
214233
p::TOML.Parser
215234
d::Dict{String, CachedTOMLDict}
@@ -226,6 +245,10 @@ function parsed_toml(project_file::AbstractString, toml_cache::TOMLCache, toml_l
226245
return d.d
227246
else
228247
d = toml_cache.d[project_file]
248+
# We are in a require call, assume project file has not been modified
249+
if LOADING_CACHE[] !== nothing
250+
return d.d
251+
end
229252
return get_updated_dict(toml_cache.p, d)
230253
end
231254
end
@@ -352,16 +375,29 @@ const preferences_names = ("JuliaLocalPreferences.toml", "LocalPreferences.toml"
352375
# - `true`: `env` is an implicit environment
353376
# - `path`: the path of an explicit project file
354377
function env_project_file(env::String)::Union{Bool,String}
378+
cache = LOADING_CACHE[]
379+
if cache !== nothing
380+
project_file = get(cache.env_project_file, env, nothing)
381+
project_file === nothing || return project_file
382+
end
355383
if isdir(env)
356384
for proj in project_names
357-
project_file = joinpath(env, proj)
358-
isfile_casesensitive(project_file) && return project_file
385+
maybe_project_file = joinpath(env, proj)
386+
if isfile_casesensitive(maybe_project_file)
387+
project_file = maybe_project_file
388+
break
389+
end
359390
end
360-
return true
391+
project_file =true
361392
elseif basename(env) in project_names && isfile_casesensitive(env)
362-
return env
393+
project_file = env
394+
else
395+
project_file = false
396+
end
397+
if cache !== nothing
398+
cache.env_project_file[env] = project_file
363399
end
364-
return false
400+
return project_file
365401
end
366402

367403
function project_deps_get(env::String, name::String)::Union{Nothing,PkgId}
@@ -415,10 +451,9 @@ end
415451

416452
# find project file's top-level UUID entry (or nothing)
417453
function project_file_name_uuid(project_file::String, name::String)::PkgId
418-
uuid = dummy_uuid(project_file)
419454
d = parsed_toml(project_file)
420455
uuid′ = get(d, "uuid", nothing)::Union{String, Nothing}
421-
uuid′ === nothing || (uuid = UUID(uuid′))
456+
uuid = uuid=== nothing ? dummy_uuid(project_file) : UUID(uuid′)
422457
name = get(d, "name", name)::String
423458
return PkgId(uuid, name)
424459
end
@@ -430,18 +465,34 @@ end
430465

431466
# find project file's corresponding manifest file
432467
function project_file_manifest_path(project_file::String)::Union{Nothing,String}
468+
cache = LOADING_CACHE[]
469+
if cache !== nothing
470+
manifest_path = get(cache.project_file_manifest_path, project_file, missing)
471+
manifest_path === missing || return manifest_path
472+
end
433473
dir = abspath(dirname(project_file))
434474
d = parsed_toml(project_file)
435475
explicit_manifest = get(d, "manifest", nothing)::Union{String, Nothing}
476+
manifest_path = nothing
436477
if explicit_manifest !== nothing
437478
manifest_file = normpath(joinpath(dir, explicit_manifest))
438-
isfile_casesensitive(manifest_file) && return manifest_file
479+
if isfile_casesensitive(manifest_file)
480+
manifest_path = manifest_file
481+
end
482+
end
483+
if manifest_path === nothing
484+
for mfst in manifest_names
485+
manifest_file = joinpath(dir, mfst)
486+
if isfile_casesensitive(manifest_file)
487+
manifest_path = manifest_file
488+
break
489+
end
490+
end
439491
end
440-
for mfst in manifest_names
441-
manifest_file = joinpath(dir, mfst)
442-
isfile_casesensitive(manifest_file) && return manifest_file
492+
if cache !== nothing
493+
cache.project_file_manifest_path[project_file] = manifest_path
443494
end
444-
return nothing
495+
return manifest_path
445496
end
446497

447498
# given a directory (implicit env from LOAD_PATH) and a name,
@@ -876,42 +927,47 @@ For more details regarding code loading, see the manual sections on [modules](@r
876927
[parallel computing](@ref code-availability).
877928
"""
878929
function require(into::Module, mod::Symbol)
879-
uuidkey = identify_package(into, String(mod))
880-
# Core.println("require($(PkgId(into)), $mod) -> $uuidkey")
881-
if uuidkey === nothing
882-
where = PkgId(into)
883-
if where.uuid === nothing
884-
throw(ArgumentError("""
885-
Package $mod not found in current path:
886-
- Run `import Pkg; Pkg.add($(repr(String(mod))))` to install the $mod package.
887-
"""))
888-
else
889-
s = """
890-
Package $(where.name) does not have $mod in its dependencies:
891-
- If you have $(where.name) checked out for development and have
892-
added $mod as a dependency but haven't updated your primary
893-
environment's manifest file, try `Pkg.resolve()`.
894-
- Otherwise you may need to report an issue with $(where.name)"""
895-
896-
uuidkey = identify_package(PkgId(string(into)), String(mod))
897-
uuidkey === nothing && throw(ArgumentError(s))
898-
899-
# fall back to toplevel loading with a warning
900-
if !(where in modules_warned_for)
901-
@warn string(
902-
full_warning_showed[] ? "" : s, "\n",
903-
string("Loading $(mod) into $(where.name) from project dependency, ",
904-
"future warnings for $(where.name) are suppressed.")
905-
) _module = nothing _file = nothing _group = nothing
906-
push!(modules_warned_for, where)
930+
LOADING_CACHE[] = LoadingCache()
931+
try
932+
uuidkey = identify_package(into, String(mod))
933+
# Core.println("require($(PkgId(into)), $mod) -> $uuidkey")
934+
if uuidkey === nothing
935+
where = PkgId(into)
936+
if where.uuid === nothing
937+
throw(ArgumentError("""
938+
Package $mod not found in current path:
939+
- Run `import Pkg; Pkg.add($(repr(String(mod))))` to install the $mod package.
940+
"""))
941+
else
942+
s = """
943+
Package $(where.name) does not have $mod in its dependencies:
944+
- If you have $(where.name) checked out for development and have
945+
added $mod as a dependency but haven't updated your primary
946+
environment's manifest file, try `Pkg.resolve()`.
947+
- Otherwise you may need to report an issue with $(where.name)"""
948+
949+
uuidkey = identify_package(PkgId(string(into)), String(mod))
950+
uuidkey === nothing && throw(ArgumentError(s))
951+
952+
# fall back to toplevel loading with a warning
953+
if !(where in modules_warned_for)
954+
@warn string(
955+
full_warning_showed[] ? "" : s, "\n",
956+
string("Loading $(mod) into $(where.name) from project dependency, ",
957+
"future warnings for $(where.name) are suppressed.")
958+
) _module = nothing _file = nothing _group = nothing
959+
push!(modules_warned_for, where)
960+
end
961+
full_warning_showed[] = true
907962
end
908-
full_warning_showed[] = true
909963
end
964+
if _track_dependencies[]
965+
push!(_require_dependencies, (into, binpack(uuidkey), 0.0))
966+
end
967+
return require(uuidkey)
968+
finally
969+
LOADING_CACHE[] = nothing
910970
end
911-
if _track_dependencies[]
912-
push!(_require_dependencies, (into, binpack(uuidkey), 0.0))
913-
end
914-
return require(uuidkey)
915971
end
916972

917973
mutable struct PkgOrigin

0 commit comments

Comments
 (0)