Skip to content

Commit 15df9c0

Browse files
authored
fix deadlock between require_lock and package callbacks (#43936)
1 parent 6c1eb93 commit 15df9c0

File tree

2 files changed

+43
-10
lines changed

2 files changed

+43
-10
lines changed

base/loading.jl

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -813,6 +813,22 @@ function _include_from_serialized(path::String, depmods::Vector{Any})
813813
return restored
814814
end
815815

816+
function run_package_callbacks(modkey::PkgId)
817+
unlock(require_lock)
818+
try
819+
for callback in package_callbacks
820+
invokelatest(callback, modkey)
821+
end
822+
catch
823+
# Try to continue loading if a callback errors
824+
errs = current_exceptions()
825+
@error "Error during package callback" exception=errs
826+
finally
827+
lock(require_lock)
828+
end
829+
nothing
830+
end
831+
816832
function _tryrequire_from_serialized(modkey::PkgId, build_id::UInt64, modpath::Union{Nothing, String}, depth::Int = 0)
817833
if root_module_exists(modkey)
818834
M = root_module(modkey)
@@ -827,9 +843,7 @@ function _tryrequire_from_serialized(modkey::PkgId, build_id::UInt64, modpath::U
827843
mod = _require_search_from_serialized(modkey, String(modpath), depth)
828844
get!(PkgOrigin, pkgorigins, modkey).path = modpath
829845
if !isa(mod, Bool)
830-
for callback in package_callbacks
831-
invokelatest(callback, modkey)
832-
end
846+
run_package_callbacks(modkey)
833847
for M in mod::Vector{Any}
834848
M = M::Module
835849
if PkgId(M) == modkey && module_build_id(M) === build_id
@@ -1062,7 +1076,7 @@ function require(into::Module, mod::Symbol)
10621076
if _track_dependencies[]
10631077
push!(_require_dependencies, (into, binpack(uuidkey), 0.0))
10641078
end
1065-
return require(uuidkey)
1079+
return _require_prelocked(uuidkey)
10661080
finally
10671081
LOADING_CACHE[] = nothing
10681082
end
@@ -1077,26 +1091,24 @@ end
10771091
PkgOrigin() = PkgOrigin(nothing, nothing)
10781092
const pkgorigins = Dict{PkgId,PkgOrigin}()
10791093

1080-
function require(uuidkey::PkgId)
1081-
@lock require_lock begin
1094+
require(uuidkey::PkgId) = @lock require_lock _require_prelocked(uuidkey)
1095+
1096+
function _require_prelocked(uuidkey::PkgId)
10821097
just_loaded_pkg = false
10831098
if !root_module_exists(uuidkey)
10841099
cachefile = _require(uuidkey)
10851100
if cachefile !== nothing
10861101
get!(PkgOrigin, pkgorigins, uuidkey).cachepath = cachefile
10871102
end
10881103
# After successfully loading, notify downstream consumers
1089-
for callback in package_callbacks
1090-
invokelatest(callback, uuidkey)
1091-
end
1104+
run_package_callbacks(uuidkey)
10921105
just_loaded_pkg = true
10931106
end
10941107
if just_loaded_pkg && !root_module_exists(uuidkey)
10951108
error("package `$(uuidkey.name)` did not define the expected \
10961109
module `$(uuidkey.name)`, check for typos in package module name")
10971110
end
10981111
return root_module(uuidkey)
1099-
end
11001112
end
11011113

11021114
const loaded_modules = Dict{PkgId,Module}()

test/precompile.jl

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -767,6 +767,27 @@ precompile_test_harness("package_callbacks") do dir
767767
finally
768768
pop!(Base.package_callbacks)
769769
end
770+
L = ReentrantLock()
771+
E = Base.Event()
772+
t = errormonitor(@async lock(L) do
773+
wait(E)
774+
Base.root_module_key(Base)
775+
end)
776+
Test4_module = :Teste4095a84
777+
write(joinpath(dir, "$(Test4_module).jl"),
778+
"""
779+
module $(Test4_module)
780+
end
781+
""")
782+
Base.compilecache(Base.PkgId("$(Test4_module)"))
783+
push!(Base.package_callbacks, _->(notify(E); lock(L) do; end))
784+
# should not hang here
785+
try
786+
@eval using $(Symbol(Test4_module))
787+
wait(t)
788+
finally
789+
pop!(Base.package_callbacks)
790+
end
770791
end
771792

772793
# Issue #19960

0 commit comments

Comments
 (0)