Skip to content

Commit 9d56856

Browse files
committed
fix use-after-free in test (detected in win32 CI)
1 parent dbbd4d9 commit 9d56856

File tree

1 file changed

+41
-34
lines changed

1 file changed

+41
-34
lines changed

test/threads.jl

Lines changed: 41 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -415,8 +415,8 @@ let e = Base.Event(true),
415415
newthreads = zeros(Int16, length(tids))
416416
onces = Vector{Vector{Nothing}}(undef, length(tids))
417417
allonces = Vector{Vector{Vector{Nothing}}}(undef, length(tids))
418-
for i = 1:length(tids)
419-
function cl()
418+
# allocate closure memory to last until all threads are started
419+
cls = [function cl()
420420
GC.gc(false) # stress test the GC-safepoint mechanics of jl_adopt_thread
421421
try
422422
newthreads[i] = threadid()
@@ -434,43 +434,50 @@ let e = Base.Event(true),
434434
GC.gc(false) # stress test the GC-safepoint mechanics of jl_delete_thread
435435
nothing
436436
end
437-
function threadcallclosure(cl::F) where {F} # create sparam so we can reference the type of cl in the ccall type
438-
threadwork = @cfunction cl -> cl() Cvoid (Ref{F},) # create a cfunction that specializes on cl as an argument and calls it
439-
err = @ccall uv_thread_create(Ref(tids, i)::Ptr{UInt}, threadwork::Ptr{Cvoid}, cl::Ref{F})::Cint # call that on a thread
440-
err == 0 || Base.uv_error("uv_thread_create", err)
441-
end
442-
threadcallclosure(cl)
443-
end
444-
@noinline function waitallthreads(tids)
437+
for i = 1:length(tids)]
438+
GC.@preserve cls begin # this memory must survive until each corresponding thread exits (waitallthreads / uv_thread_join)
439+
Base.preserve_handle(cls)
445440
for i = 1:length(tids)
446-
tid = Ref(tids, i)
447-
tidp = Base.unsafe_convert(Ptr{UInt}, tid)::Ptr{UInt}
448-
gc_state = @ccall jl_gc_safe_enter()::Int8
449-
GC.@preserve tid err = @ccall uv_thread_join(tidp::Ptr{UInt})::Cint
450-
@ccall jl_gc_safe_leave(gc_state::Int8)::Cvoid
451-
err == 0 || Base.uv_error("uv_thread_join", err)
452-
end
453-
end
454-
try
455-
# let them finish in batches of 10
456-
for i = 1:length(tids) ÷ 10
457-
for i = 1:10
458-
newid = take!(started)
459-
@test newid != threadid()
441+
function threadcallclosure(tid::Ref{UInt}, cl::Ref{F}) where {F} # create sparam so we can reference the type of cl in the ccall type
442+
threadwork = @cfunction cl -> cl() Cvoid (Ref{F},) # create a cfunction that specializes on cl as an argument and calls it
443+
err = @ccall uv_thread_create(tid::Ptr{UInt}, threadwork::Ptr{Cvoid}, cl::Ref{F})::Cint # call that on a thread
444+
err == 0 || Base.uv_error("uv_thread_create", err)
445+
nothing
460446
end
461-
for i = 1:10
462-
push!(finish, nothing)
447+
threadcallclosure(Ref(tids, i), Ref(cls, i))
448+
end
449+
@noinline function waitallthreads(tids, cls)
450+
for i = 1:length(tids)
451+
tid = Ref(tids, i)
452+
tidp = Base.unsafe_convert(Ptr{UInt}, tid)::Ptr{UInt}
453+
gc_state = @ccall jl_gc_safe_enter()::Int8
454+
GC.@preserve tid err = @ccall uv_thread_join(tidp::Ptr{UInt})::Cint
455+
@ccall jl_gc_safe_leave(gc_state::Int8)::Cvoid
456+
err == 0 || Base.uv_error("uv_thread_join", err)
463457
end
458+
Base.unpreserve_handle(cls)
464459
end
465-
@test isempty(started)
466-
# now run the second part of the test where they all try to access the other threads elements
467-
notify(starttest2)
468-
finally
469-
for _ = 1:length(tids)
470-
# run IO loop until all threads are close to exiting
471-
take!(exiting)
460+
try
461+
# let them finish in batches of 10
462+
for i = 1:length(tids) ÷ 10
463+
for i = 1:10
464+
newid = take!(started)
465+
@test newid != threadid()
466+
end
467+
for i = 1:10
468+
push!(finish, nothing)
469+
end
470+
end
471+
@test isempty(started)
472+
# now run the second part of the test where they all try to access the other threads elements
473+
notify(starttest2)
474+
finally
475+
for _ = 1:length(tids)
476+
# run IO loop until all threads are close to exiting
477+
take!(exiting)
478+
end
479+
waitallthreads(tids, cls)
472480
end
473-
waitallthreads(tids)
474481
end
475482
@test isempty(started)
476483
@test isempty(finish)

0 commit comments

Comments
 (0)