From 622a181b5a2ebb1eadf5a910e83f327a18a8b29b Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Sat, 5 Mar 2022 13:46:49 -0500 Subject: [PATCH] process: ensure uvfinalize and _uv_close_cb are synchronized There appeared to be a possibility they could race and the data field might already be destroyed before we reached the close callback, from looking at the state of the program when reproducing #44460. This is because the uv_return_spawn set the handle to NULL, which later can cause the uvfinalize to exit early (if the finalizer gets run on another thread, since we have disabled finalizers on our thread). Then the GC can reap the julia Process object prior to uv_close cleaning up the object. We solve this by calling disassociate_julia_struct before dropping the reference to the handle. But then we also fully address any remaining race condition by having uvfinalize acquire a lock also. Fixes #44460 --- base/process.jl | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/base/process.jl b/base/process.jl index 6aac514191681..fd5a963064e31 100644 --- a/base/process.jl +++ b/base/process.jl @@ -38,9 +38,13 @@ pipe_writer(p::ProcessChain) = p.in # release ownership of the libuv handle function uvfinalize(proc::Process) if proc.handle != C_NULL - disassociate_julia_struct(proc.handle) - ccall(:jl_close_uv, Cvoid, (Ptr{Cvoid},), proc.handle) - proc.handle = C_NULL + iolock_begin() + if proc.handle != C_NULL + disassociate_julia_struct(proc.handle) + ccall(:jl_close_uv, Cvoid, (Ptr{Cvoid},), proc.handle) + proc.handle = C_NULL + end + iolock_end() end nothing end @@ -52,6 +56,7 @@ function uv_return_spawn(p::Ptr{Cvoid}, exit_status::Int64, termsignal::Int32) proc = unsafe_pointer_to_objref(data)::Process proc.exitcode = exit_status proc.termsignal = termsignal + disassociate_julia_struct(proc) # ensure that data field is set to C_NULL ccall(:jl_close_uv, Cvoid, (Ptr{Cvoid},), proc.handle) proc.handle = C_NULL lock(proc.exitnotify)