Skip to content

Commit a4e9eae

Browse files
committed
fix #41546, make using thread-safe
1 parent 468b157 commit a4e9eae

File tree

3 files changed

+42
-8
lines changed

3 files changed

+42
-8
lines changed

base/loading.jl

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,7 @@ function parsed_toml(project_file::AbstractString, toml_cache::TOMLCache, toml_l
263263
end
264264

265265
## package identification: determine unique identity of package to be loaded ##
266+
const require_lock = ReentrantLock()
266267

267268
# Used by Pkg but not used in loading itself
268269
function find_package(arg)
@@ -862,7 +863,7 @@ function _require_search_from_serialized(pkg::PkgId, sourcepath::String)
862863
end
863864

864865
# to synchronize multiple tasks trying to import/using something
865-
const package_locks = Dict{PkgId,Condition}()
866+
const package_locks = Dict{PkgId,Threads.Condition}()
866867

867868
# to notify downstream consumers that a module was successfully loaded
868869
# Callbacks take the form (mod::Base.PkgId) -> nothing.
@@ -957,6 +958,7 @@ For more details regarding code loading, see the manual sections on [modules](@r
957958
[parallel computing](@ref code-availability).
958959
"""
959960
function require(into::Module, mod::Symbol)
961+
@lock require_lock begin
960962
LOADING_CACHE[] = LoadingCache()
961963
try
962964
uuidkey = identify_package(into, String(mod))
@@ -998,6 +1000,7 @@ function require(into::Module, mod::Symbol)
9981000
finally
9991001
LOADING_CACHE[] = nothing
10001002
end
1003+
end
10011004
end
10021005

10031006
mutable struct PkgOrigin
@@ -1009,6 +1012,7 @@ PkgOrigin() = PkgOrigin(nothing, nothing)
10091012
const pkgorigins = Dict{PkgId,PkgOrigin}()
10101013

10111014
function require(uuidkey::PkgId)
1015+
@lock require_lock begin
10121016
if !root_module_exists(uuidkey)
10131017
cachefile = _require(uuidkey)
10141018
if cachefile !== nothing
@@ -1020,13 +1024,14 @@ function require(uuidkey::PkgId)
10201024
end
10211025
end
10221026
return root_module(uuidkey)
1027+
end
10231028
end
10241029

10251030
const loaded_modules = Dict{PkgId,Module}()
10261031
const module_keys = IdDict{Module,PkgId}() # the reverse
10271032

1028-
is_root_module(m::Module) = haskey(module_keys, m)
1029-
root_module_key(m::Module) = module_keys[m]
1033+
is_root_module(m::Module) = @lock require_lock haskey(module_keys, m)
1034+
root_module_key(m::Module) = @lock require_lock module_keys[m]
10301035

10311036
function register_root_module(m::Module)
10321037
key = PkgId(m, String(nameof(m)))
@@ -1053,12 +1058,13 @@ using Base
10531058
end
10541059

10551060
# get a top-level Module from the given key
1056-
root_module(key::PkgId) = loaded_modules[key]
1061+
root_module(key::PkgId) = @lock require_lock loaded_modules[key]
10571062
root_module(where::Module, name::Symbol) =
10581063
root_module(identify_package(where, String(name)))
1064+
maybe_root_module(key::PkgId) = @lock require_lock get(loaded_modules, key, nothing)
10591065

1060-
root_module_exists(key::PkgId) = haskey(loaded_modules, key)
1061-
loaded_modules_array() = collect(values(loaded_modules))
1066+
root_module_exists(key::PkgId) = @lock require_lock haskey(loaded_modules, key)
1067+
loaded_modules_array() = @lock require_lock collect(values(loaded_modules))
10621068

10631069
function unreference_module(key::PkgId)
10641070
if haskey(loaded_modules, key)
@@ -1077,7 +1083,7 @@ function _require(pkg::PkgId)
10771083
wait(loading)
10781084
return
10791085
end
1080-
package_locks[pkg] = Condition()
1086+
package_locks[pkg] = Threads.Condition(require_lock)
10811087

10821088
last = toplevel_load[]
10831089
try

base/toml_parser.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ function Parser(str::String; filepath=nothing)
104104
IdSet{TOMLDict}(), # defined_tables
105105
root,
106106
filepath,
107-
isdefined(Base, :loaded_modules) ? get(Base.loaded_modules, DATES_PKGID, nothing) : nothing,
107+
isdefined(Base, :loaded_modules) ? Base.maybe_root_module(DATES_PKGID) : nothing,
108108
)
109109
startup(l)
110110
return l

test/threads_exec.jl

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -912,3 +912,31 @@ end
912912
@test reproducible_rand(r, 10) == val
913913
end
914914
end
915+
916+
# issue #41546, thread-safe package loading
917+
@testset "package loading" begin
918+
ch = Channel{Bool}(nthreads())
919+
barrier = Base.Event()
920+
old_act_proj = Base.ACTIVE_PROJECT[]
921+
try
922+
pushfirst!(LOAD_PATH, "@")
923+
Base.ACTIVE_PROJECT[] = joinpath(@__DIR__, "TestPkg")
924+
@sync begin
925+
for _ in 1:nthreads()
926+
Threads.@spawn begin
927+
put!(ch, true)
928+
wait(barrier)
929+
@eval using TestPkg
930+
end
931+
end
932+
for _ in 1:nthreads()
933+
take!(ch)
934+
end
935+
notify(barrier)
936+
end
937+
@test Base.root_module(@__MODULE__, :TestPkg) isa Module
938+
finally
939+
Base.ACTIVE_PROJECT[] = old_act_proj
940+
popfirst!(LOAD_PATH)
941+
end
942+
end

0 commit comments

Comments
 (0)