Skip to content

Commit 4929aa2

Browse files
vtjnashKristofferC
authored andcommitted
fix concurrent module loading return value (#54898)
Previously this might return `nothing` which would confuse the caller of `start_loading` which expects that to mean the Module didn't load. It is not entirely clear if this code ever worked, even single-threaded. Fix #54813 (cherry picked from commit 0ef2bb6)
1 parent b3b0416 commit 4929aa2

File tree

3 files changed

+41
-32
lines changed

3 files changed

+41
-32
lines changed

base/loading.jl

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1935,8 +1935,12 @@ debug_loading_deadlocks::Bool = true # Enable a slightly more expensive, but mor
19351935
function start_loading(modkey::PkgId)
19361936
# handle recursive calls to require
19371937
assert_havelock(require_lock)
1938-
loading = get(package_locks, modkey, nothing)
1939-
if loading !== nothing
1938+
while true
1939+
loading = get(package_locks, modkey, nothing)
1940+
if loading === nothing
1941+
package_locks[modkey] = current_task() => Threads.Condition(require_lock)
1942+
return nothing
1943+
end
19401944
# load already in progress for this module on the task
19411945
task, cond = loading
19421946
deps = String[modkey.name]
@@ -1975,10 +1979,9 @@ function start_loading(modkey::PkgId)
19751979
end
19761980
throw(ConcurrencyViolationError(msg))
19771981
end
1978-
return wait(cond)
1982+
loading = wait(cond)
1983+
loading isa Module && return loading
19791984
end
1980-
package_locks[modkey] = current_task() => Threads.Condition(require_lock)
1981-
return
19821985
end
19831986

19841987
function end_loading(modkey::PkgId, @nospecialize loaded)
@@ -2343,9 +2346,9 @@ function _require(pkg::PkgId, env=nothing)
23432346
# attempt to load the module file via the precompile cache locations
23442347
if JLOptions().use_compiled_modules != 0
23452348
@label load_from_cache
2346-
m = _require_search_from_serialized(pkg, path, UInt128(0), true; reasons)
2347-
if m isa Module
2348-
return m
2349+
loaded = _require_search_from_serialized(pkg, path, UInt128(0), true; reasons)
2350+
if loaded isa Module
2351+
return loaded
23492352
end
23502353
end
23512354

@@ -2381,31 +2384,30 @@ function _require(pkg::PkgId, env=nothing)
23812384
@goto load_from_cache
23822385
end
23832386
# spawn off a new incremental pre-compile task for recursive `require` calls
2384-
cachefile_or_module = maybe_cachefile_lock(pkg, path) do
2385-
# double-check now that we have lock
2387+
loaded = maybe_cachefile_lock(pkg, path) do
2388+
# double-check the search now that we have lock
23862389
m = _require_search_from_serialized(pkg, path, UInt128(0), true)
23872390
m isa Module && return m
2388-
compilecache(pkg, path; reasons)
2391+
return compilecache(pkg, path; reasons)
23892392
end
2390-
cachefile_or_module isa Module && return cachefile_or_module::Module
2391-
cachefile = cachefile_or_module
2392-
if isnothing(cachefile) # maybe_cachefile_lock returns nothing if it had to wait for another process
2393+
loaded isa Module && return loaded
2394+
if isnothing(loaded) # maybe_cachefile_lock returns nothing if it had to wait for another process
23932395
@goto load_from_cache # the new cachefile will have the newest mtime so will come first in the search
2394-
elseif isa(cachefile, Exception)
2395-
if precompilableerror(cachefile)
2396+
elseif isa(loaded, Exception)
2397+
if precompilableerror(loaded)
23962398
verbosity = isinteractive() ? CoreLogging.Info : CoreLogging.Debug
2397-
@logmsg verbosity "Skipping precompilation due to precompilable error. Importing $(repr("text/plain", pkg))." exception=m
2399+
@logmsg verbosity "Skipping precompilation due to precompilable error. Importing $(repr("text/plain", pkg))." exception=loaded
23982400
else
2399-
@warn "The call to compilecache failed to create a usable precompiled cache file for $(repr("text/plain", pkg))" exception=m
2401+
@warn "The call to compilecache failed to create a usable precompiled cache file for $(repr("text/plain", pkg))" exception=loaded
24002402
end
24012403
# fall-through to loading the file locally if not incremental
24022404
else
2403-
cachefile, ocachefile = cachefile::Tuple{String, Union{Nothing, String}}
2404-
m = _tryrequire_from_serialized(pkg, cachefile, ocachefile)
2405-
if !isa(m, Module)
2406-
@warn "The call to compilecache failed to create a usable precompiled cache file for $(repr("text/plain", pkg))" exception=m
2405+
cachefile, ocachefile = loaded::Tuple{String, Union{Nothing, String}}
2406+
loaded = _tryrequire_from_serialized(pkg, cachefile, ocachefile)
2407+
if !isa(loaded, Module)
2408+
@warn "The call to compilecache failed to create a usable precompiled cache file for $(repr("text/plain", pkg))" exception=loaded
24072409
else
2408-
return m
2410+
return loaded
24092411
end
24102412
end
24112413
if JLOptions().incremental != 0

test/loading.jl

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1206,6 +1206,11 @@ end
12061206
empty!(Base.DEPOT_PATH)
12071207
append!(Base.DEPOT_PATH, original_depot_path)
12081208

1209+
module loaded_pkgid1 end
1210+
module loaded_pkgid2 end
1211+
module loaded_pkgid3 end
1212+
module loaded_pkgid4 end
1213+
12091214
@testset "loading deadlock detector" begin
12101215
pkid1 = Base.PkgId("pkgid1")
12111216
pkid2 = Base.PkgId("pkgid2")
@@ -1217,25 +1222,25 @@ append!(Base.DEPOT_PATH, original_depot_path)
12171222
t1 = @async begin
12181223
@test nothing === @lock Base.require_lock Base.start_loading(pkid2) # @async module pkgid2; using pkgid1; end
12191224
notify(e)
1220-
@test "loaded_pkgid1" == @lock Base.require_lock Base.start_loading(pkid1)
1221-
@lock Base.require_lock Base.end_loading(pkid2, "loaded_pkgid2")
1225+
@test loaded_pkgid1 == @lock Base.require_lock Base.start_loading(pkid1)
1226+
@lock Base.require_lock Base.end_loading(pkid2, loaded_pkgid2)
12221227
end
12231228
wait(e)
12241229
reset(e)
12251230
t2 = @async begin
12261231
@test nothing === @lock Base.require_lock Base.start_loading(pkid3) # @async module pkgid3; using pkgid2; end
12271232
notify(e)
1228-
@test "loaded_pkgid2" == @lock Base.require_lock Base.start_loading(pkid2)
1229-
@lock Base.require_lock Base.end_loading(pkid3, "loaded_pkgid3")
1233+
@test loaded_pkgid2 == @lock Base.require_lock Base.start_loading(pkid2)
1234+
@lock Base.require_lock Base.end_loading(pkid3, loaded_pkgid3)
12301235
end
12311236
wait(e)
12321237
reset(e)
12331238
@test_throws(ConcurrencyViolationError("deadlock detected in loading pkgid3 -> pkgid2 -> pkgid1 -> pkgid3 && pkgid4"),
12341239
@lock Base.require_lock Base.start_loading(pkid3)).value # try using pkgid3
12351240
@test_throws(ConcurrencyViolationError("deadlock detected in loading pkgid4 -> pkgid4 && pkgid1"),
12361241
@lock Base.require_lock Base.start_loading(pkid4)).value # try using pkgid4
1237-
@lock Base.require_lock Base.end_loading(pkid1, "loaded_pkgid1") # end
1238-
@lock Base.require_lock Base.end_loading(pkid4, "loaded_pkgid4") # end
1242+
@lock Base.require_lock Base.end_loading(pkid1, loaded_pkgid1) # end
1243+
@lock Base.require_lock Base.end_loading(pkid4, loaded_pkgid4) # end
12391244
wait(t2)
12401245
wait(t1)
12411246
end

test/threads_exec.jl

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1122,23 +1122,25 @@ end
11221122

11231123
# issue #41546, thread-safe package loading
11241124
@testset "package loading" begin
1125-
ch = Channel{Bool}(threadpoolsize())
1125+
ntasks = max(threadpoolsize(), 4)
1126+
ch = Channel{Bool}(ntasks)
11261127
barrier = Base.Event()
11271128
old_act_proj = Base.ACTIVE_PROJECT[]
11281129
try
11291130
pushfirst!(LOAD_PATH, "@")
11301131
Base.ACTIVE_PROJECT[] = joinpath(@__DIR__, "TestPkg")
11311132
@sync begin
1132-
for _ in 1:threadpoolsize()
1133+
for _ in 1:ntasks
11331134
Threads.@spawn begin
11341135
put!(ch, true)
11351136
wait(barrier)
11361137
@eval using TestPkg
11371138
end
11381139
end
1139-
for _ in 1:threadpoolsize()
1140+
for _ in 1:ntasks
11401141
take!(ch)
11411142
end
1143+
close(ch)
11421144
notify(barrier)
11431145
end
11441146
@test Base.root_module(@__MODULE__, :TestPkg) isa Module

0 commit comments

Comments
 (0)