Skip to content

Commit fc65ba4

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 0b9525b commit fc65ba4

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
@@ -36,9 +36,16 @@ end
3636
function _insert_backedges(edges::Vector{Any}, stack::Vector{CodeInstance}, visiting::IdDict{CodeInstance,Int}, external::Bool=false)
3737
for i = 1:length(edges)
3838
codeinst = edges[i]::CodeInstance
39-
verify_method_graph(codeinst, stack, visiting)
39+
validation_world = get_world_counter()
40+
verify_method_graph(codeinst, stack, visiting, validation_world)
41+
# After validation, under the world_counter_lock, set max_world to typemax(UInt) for all dependencies
42+
# (recursively). From that point onward the ordinary backedge mechanism is responsible for maintaining
43+
# validity.
44+
@ccall jl_promote_ci_to_current(codeinst::Any, validation_world::UInt)::Cvoid
4045
minvalid = codeinst.min_world
4146
maxvalid = codeinst.max_world
47+
# Finally, if this CI is still valid in some world age and and belongs to an external method(specialization),
48+
# poke it that mi's cache
4249
if maxvalid minvalid && external
4350
caller = get_ci_mi(codeinst)
4451
@assert isdefined(codeinst, :inferred) # See #53586, #53109
@@ -54,9 +61,9 @@ function _insert_backedges(edges::Vector{Any}, stack::Vector{CodeInstance}, visi
5461
end
5562
end
5663

57-
function verify_method_graph(codeinst::CodeInstance, stack::Vector{CodeInstance}, visiting::IdDict{CodeInstance,Int})
64+
function verify_method_graph(codeinst::CodeInstance, stack::Vector{CodeInstance}, visiting::IdDict{CodeInstance,Int}, validation_world::UInt)
5865
@assert isempty(stack); @assert isempty(visiting);
59-
child_cycle, minworld, maxworld = verify_method(codeinst, stack, visiting)
66+
child_cycle, minworld, maxworld = verify_method(codeinst, stack, visiting, validation_world)
6067
@assert child_cycle == 0
6168
@assert isempty(stack); @assert isempty(visiting);
6269
nothing
@@ -66,15 +73,14 @@ end
6673
# - Visit the entire call graph, starting from edges[idx] to determine if that method is valid
6774
# - Implements Tarjan's SCC (strongly connected components) algorithm, simplified to remove the count variable
6875
# and slightly modified with an early termination option once the computation reaches its minimum
69-
function verify_method(codeinst::CodeInstance, stack::Vector{CodeInstance}, visiting::IdDict{CodeInstance,Int})
76+
function verify_method(codeinst::CodeInstance, stack::Vector{CodeInstance}, visiting::IdDict{CodeInstance,Int}, validation_world::UInt)
7077
world = codeinst.min_world
7178
let max_valid2 = codeinst.max_world
7279
if max_valid2 WORLD_AGE_REVALIDATION_SENTINEL
7380
return 0, world, max_valid2
7481
end
7582
end
76-
current_world = get_world_counter()
77-
local minworld::UInt, maxworld::UInt = 1, current_world
83+
local minworld::UInt, maxworld::UInt = 1, validation_world
7884
@assert get_ci_mi(codeinst).def isa Method
7985
if haskey(visiting, codeinst)
8086
return visiting[codeinst], minworld, maxworld
@@ -156,7 +162,7 @@ function verify_method(codeinst::CodeInstance, stack::Vector{CodeInstance}, visi
156162
end
157163
callee = edge
158164
local min_valid2::UInt, max_valid2::UInt
159-
child_cycle, min_valid2, max_valid2 = verify_method(callee, stack, visiting)
165+
child_cycle, min_valid2, max_valid2 = verify_method(callee, stack, visiting, validation_world)
160166
if minworld < min_valid2
161167
minworld = min_valid2
162168
end
@@ -188,16 +194,14 @@ function verify_method(codeinst::CodeInstance, stack::Vector{CodeInstance}, visi
188194
if maxworld 0
189195
@atomic :monotonic child.min_world = minworld
190196
end
191-
if maxworld == current_world
197+
@atomic :monotonic child.max_world = maxworld
198+
if maxworld == validation_world && validation_world == get_world_counter()
192199
Base.Compiler.store_backedges(child, child.edges)
193-
@atomic :monotonic child.max_world = typemax(UInt)
194-
else
195-
@atomic :monotonic child.max_world = maxworld
196200
end
197201
@assert visiting[child] == length(stack) + 1
198202
delete!(visiting, child)
199203
invalidations = _jl_debug_method_invalidation[]
200-
if invalidations !== nothing && maxworld < current_world
204+
if invalidations !== nothing && maxworld < validation_world
201205
push!(invalidations, child, "verify_methods", cause)
202206
end
203207
end

src/staticdata.c

+28
Original file line numberDiff line numberDiff line change
@@ -4256,6 +4256,34 @@ JL_DLLEXPORT jl_value_t *jl_restore_package_image_from_file(const char *fname, j
42564256
return mod;
42574257
}
42584258

4259+
JL_DLLEXPORT void _jl_promote_ci_to_current(jl_code_instance_t *ci, size_t validated_world) JL_NOTSAFEPOINT
4260+
{
4261+
if (jl_atomic_load_relaxed(&ci->max_world) != validated_world)
4262+
return;
4263+
jl_atomic_store_relaxed(&ci->max_world, (size_t)-1);
4264+
jl_svec_t *edges = jl_atomic_load_relaxed(&ci->edges);
4265+
for (size_t i = 0; i < jl_svec_len(edges); i++) {
4266+
jl_value_t *edge = jl_svecref(edges, i);
4267+
if (!jl_is_code_instance(edge))
4268+
continue;
4269+
_jl_promote_ci_to_current(ci, validated_world);
4270+
}
4271+
}
4272+
4273+
JL_DLLEXPORT void jl_promote_ci_to_current(jl_code_instance_t *ci, size_t validated_world)
4274+
{
4275+
size_t current_world = jl_atomic_load_relaxed(&jl_world_counter);
4276+
// No need to acquire the lock if we've been invalidated anyway
4277+
if (current_world > validated_world)
4278+
return;
4279+
JL_LOCK(&world_counter_lock);
4280+
current_world = jl_atomic_load_relaxed(&jl_world_counter);
4281+
if (current_world == validated_world) {
4282+
_jl_promote_ci_to_current(ci, validated_world);
4283+
}
4284+
JL_UNLOCK(&world_counter_lock);
4285+
}
4286+
42594287
#ifdef __cplusplus
42604288
}
42614289
#endif

0 commit comments

Comments
 (0)