Skip to content

Commit 16f48a0

Browse files
committed
staticdata: Close data race after backedge insertion
Addresses review comment in #57212 (comment). The key is that the hand-off of responsibility for verification between the loading code and the ordinary backedge mechanism happens under the world counter lock to ensure synchronization.
1 parent 6ac507c commit 16f48a0

File tree

2 files changed

+44
-12
lines changed

2 files changed

+44
-12
lines changed

base/staticdata.jl

+16-12
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,16 @@ end
3737
function _insert_backedges(edges::Vector{Any}, stack::Vector{CodeInstance}, visiting::IdDict{CodeInstance,Int}, mwis::IdSet{Method}, external::Bool=false)
3838
for i = 1:length(edges)
3939
codeinst = edges[i]::CodeInstance
40-
verify_method_graph(codeinst, stack, visiting, mwis)
40+
validation_world = get_world_counter()
41+
verify_method_graph(codeinst, stack, visiting, mwis, validation_world)
42+
# After validation, under the world_counter_lock, set max_world to typemax(UInt) for all dependencies
43+
# (recursively). From that point onward the ordinary backedge mechanism is responsible for maintaining
44+
# validity.
45+
@ccall jl_promote_ci_to_current(codeinst::Any, validation_world::UInt)::Cvoid
4146
minvalid = codeinst.min_world
4247
maxvalid = codeinst.max_world
48+
# Finally, if this CI is still valid in some world age and and belongs to an external method(specialization),
49+
# poke it that mi's cache
4350
if maxvalid minvalid && external
4451
caller = get_ci_mi(codeinst)
4552
@assert isdefined(codeinst, :inferred) # See #53586, #53109
@@ -55,9 +62,9 @@ function _insert_backedges(edges::Vector{Any}, stack::Vector{CodeInstance}, visi
5562
end
5663
end
5764

58-
function verify_method_graph(codeinst::CodeInstance, stack::Vector{CodeInstance}, visiting::IdDict{CodeInstance,Int}, mwis::IdSet{Method})
65+
function verify_method_graph(codeinst::CodeInstance, stack::Vector{CodeInstance}, visiting::IdDict{CodeInstance,Int}, mwis::IdSet{Method}, validation_world::UInt)
5966
@assert isempty(stack); @assert isempty(visiting);
60-
child_cycle, minworld, maxworld = verify_method(codeinst, stack, visiting, mwis)
67+
child_cycle, minworld, maxworld = verify_method(codeinst, stack, visiting, mwis, validation_world)
6168
@assert child_cycle == 0
6269
@assert isempty(stack); @assert isempty(visiting);
6370
nothing
@@ -67,15 +74,14 @@ end
6774
# - Visit the entire call graph, starting from edges[idx] to determine if that method is valid
6875
# - Implements Tarjan's SCC (strongly connected components) algorithm, simplified to remove the count variable
6976
# and slightly modified with an early termination option once the computation reaches its minimum
70-
function verify_method(codeinst::CodeInstance, stack::Vector{CodeInstance}, visiting::IdDict{CodeInstance,Int}, mwis::IdSet{Method})
77+
function verify_method(codeinst::CodeInstance, stack::Vector{CodeInstance}, visiting::IdDict{CodeInstance,Int}, mwis::IdSet{Method}, validation_world::UInt)
7178
world = codeinst.min_world
7279
let max_valid2 = codeinst.max_world
7380
if max_valid2 WORLD_AGE_REVALIDATION_SENTINEL
7481
return 0, world, max_valid2
7582
end
7683
end
77-
current_world = get_world_counter()
78-
local minworld::UInt, maxworld::UInt = 1, current_world
84+
local minworld::UInt, maxworld::UInt = 1, validation_world
7985
def = get_ci_mi(codeinst).def
8086
@assert def isa Method
8187
if haskey(visiting, codeinst)
@@ -177,7 +183,7 @@ function verify_method(codeinst::CodeInstance, stack::Vector{CodeInstance}, visi
177183
end
178184
callee = edge
179185
local min_valid2::UInt, max_valid2::UInt
180-
child_cycle, min_valid2, max_valid2 = verify_method(callee, stack, visiting, mwis)
186+
child_cycle, min_valid2, max_valid2 = verify_method(callee, stack, visiting, mwis, validation_world)
181187
if minworld < min_valid2
182188
minworld = min_valid2
183189
end
@@ -209,16 +215,14 @@ function verify_method(codeinst::CodeInstance, stack::Vector{CodeInstance}, visi
209215
if maxworld 0
210216
@atomic :monotonic child.min_world = minworld
211217
end
212-
if maxworld == current_world
218+
@atomic :monotonic child.max_world = maxworld
219+
if maxworld == validation_world && validation_world == get_world_counter()
213220
Base.Compiler.store_backedges(child, child.edges)
214-
@atomic :monotonic child.max_world = typemax(UInt)
215-
else
216-
@atomic :monotonic child.max_world = maxworld
217221
end
218222
@assert visiting[child] == length(stack) + 1
219223
delete!(visiting, child)
220224
invalidations = _jl_debug_method_invalidation[]
221-
if invalidations !== nothing && maxworld < current_world
225+
if invalidations !== nothing && maxworld < validation_world
222226
push!(invalidations, child, "verify_methods", cause)
223227
end
224228
end

src/staticdata.c

+28
Original file line numberDiff line numberDiff line change
@@ -4265,6 +4265,34 @@ JL_DLLEXPORT jl_value_t *jl_restore_package_image_from_file(const char *fname, j
42654265
return mod;
42664266
}
42674267

4268+
JL_DLLEXPORT void _jl_promote_ci_to_current(jl_code_instance_t *ci, size_t validated_world) JL_NOTSAFEPOINT
4269+
{
4270+
if (jl_atomic_load_relaxed(&ci->max_world) != validated_world)
4271+
return;
4272+
jl_atomic_store_relaxed(&ci->max_world, (size_t)-1);
4273+
jl_svec_t *edges = jl_atomic_load_relaxed(&ci->edges);
4274+
for (size_t i = 0; i < jl_svec_len(edges); i++) {
4275+
jl_value_t *edge = jl_svecref(edges, i);
4276+
if (!jl_is_code_instance(edge))
4277+
continue;
4278+
_jl_promote_ci_to_current(ci, validated_world);
4279+
}
4280+
}
4281+
4282+
JL_DLLEXPORT void jl_promote_ci_to_current(jl_code_instance_t *ci, size_t validated_world)
4283+
{
4284+
size_t current_world = jl_atomic_load_relaxed(&jl_world_counter);
4285+
// No need to acquire the lock if we've been invalidated anyway
4286+
if (current_world > validated_world)
4287+
return;
4288+
JL_LOCK(&world_counter_lock);
4289+
current_world = jl_atomic_load_relaxed(&jl_world_counter);
4290+
if (current_world == validated_world) {
4291+
_jl_promote_ci_to_current(ci, validated_world);
4292+
}
4293+
JL_UNLOCK(&world_counter_lock);
4294+
}
4295+
42684296
#ifdef __cplusplus
42694297
}
42704298
#endif

0 commit comments

Comments
 (0)