Skip to content

Commit 62cb02b

Browse files
committed
fix concurrent module loading return value
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
1 parent 5da1f06 commit 62cb02b

File tree

2 files changed

+23
-18
lines changed

2 files changed

+23
-18
lines changed

base/loading.jl

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1995,8 +1995,12 @@ debug_loading_deadlocks::Bool = true # Enable a slightly more expensive, but mor
19951995
function start_loading(modkey::PkgId)
19961996
# handle recursive calls to require
19971997
assert_havelock(require_lock)
1998-
loading = get(package_locks, modkey, nothing)
1999-
if loading !== nothing
1998+
while true
1999+
loading = get(package_locks, modkey, nothing)
2000+
if loading === nothing
2001+
package_locks[modkey] = current_task() => Threads.Condition(require_lock)
2002+
return nothing
2003+
end
20002004
# load already in progress for this module on the task
20012005
task, cond = loading
20022006
deps = String[modkey.name]
@@ -2035,10 +2039,9 @@ function start_loading(modkey::PkgId)
20352039
end
20362040
throw(ConcurrencyViolationError(msg))
20372041
end
2038-
return wait(cond)
2042+
loading = wait(cond)
2043+
loading isa Module && return loading
20392044
end
2040-
package_locks[modkey] = current_task() => Threads.Condition(require_lock)
2041-
return
20422045
end
20432046

20442047
function end_loading(modkey::PkgId, @nospecialize loaded)
@@ -2417,9 +2420,9 @@ function _require(pkg::PkgId, env=nothing)
24172420
# attempt to load the module file via the precompile cache locations
24182421
if JLOptions().use_compiled_modules != 0
24192422
@label load_from_cache
2420-
m = _require_search_from_serialized(pkg, path, UInt128(0), true; reasons)
2421-
if m isa Module
2422-
return m
2423+
loaded = _require_search_from_serialized(pkg, path, UInt128(0), true; reasons)
2424+
if loaded isa Module
2425+
return loaded
24232426
end
24242427
end
24252428

@@ -2455,14 +2458,14 @@ function _require(pkg::PkgId, env=nothing)
24552458
@goto load_from_cache
24562459
end
24572460
# spawn off a new incremental pre-compile task for recursive `require` calls
2458-
cachefile_or_module = maybe_cachefile_lock(pkg, path) do
2461+
loaded = maybe_cachefile_lock(pkg, path) do
24592462
# double-check now that we have lock
24602463
m = _require_search_from_serialized(pkg, path, UInt128(0), true)
24612464
m isa Module && return m
2462-
compilecache(pkg, path; reasons)
2465+
return compilecache(pkg, path; reasons)
24632466
end
2464-
cachefile_or_module isa Module && return cachefile_or_module::Module
2465-
cachefile = cachefile_or_module
2467+
loaded isa Module && return loaded
2468+
cachefile = loaded
24662469
if isnothing(cachefile) # maybe_cachefile_lock returns nothing if it had to wait for another process
24672470
@goto load_from_cache # the new cachefile will have the newest mtime so will come first in the search
24682471
elseif isa(cachefile, Exception)
@@ -2475,11 +2478,11 @@ function _require(pkg::PkgId, env=nothing)
24752478
# fall-through to loading the file locally if not incremental
24762479
else
24772480
cachefile, ocachefile = cachefile::Tuple{String, Union{Nothing, String}}
2478-
m = _tryrequire_from_serialized(pkg, cachefile, ocachefile)
2479-
if !isa(m, Module)
2481+
loaded = _tryrequire_from_serialized(pkg, cachefile, ocachefile)
2482+
if !isa(loaded, Module)
24802483
@warn "The call to compilecache failed to create a usable precompiled cache file for $(repr("text/plain", pkg))" exception=m
24812484
else
2482-
return m
2485+
return loaded
24832486
end
24842487
end
24852488
if JLOptions().incremental != 0

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)