From 9029179eeaae973756d91e713fa5272cccfccdd0 Mon Sep 17 00:00:00 2001 From: Ludovic Raess Date: Thu, 24 Sep 2026 00:10:34 +0200 Subject: [PATCH] Skip prepare_state in GC finalizers --- src/tls.jl | 6 +++++- test/core/tls.jl | 13 +++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/tls.jl b/src/tls.jl index 9f994a988..fe7b7ba60 100644 --- a/src/tls.jl +++ b/src/tls.jl @@ -189,7 +189,11 @@ function priority!(f::Function, p::Symbol) end end -@inline function prepare_state(state = task_local_state!()) +@inline function prepare_state(state = nothing) + # Finalizers run on whichever task GC interrupted and pick their own context + # with `HIP.context!`, so leave that task's state and sticky error alone. + GC.in_finalizer() && return + isnothing(state) && (state = task_local_state!()) HIP.clear_last_error() # Drain any sticky HIP error left by a prior kernel failure hip_ctx = Ref{HIP.hipCtx_t}() HIP.hipCtxGetCurrent(hip_ctx) diff --git a/test/core/tls.jl b/test/core/tls.jl index b0adc452e..c13ec0661 100644 --- a/test/core/tls.jl +++ b/test/core/tls.jl @@ -15,6 +15,19 @@ using AMDGPU: ROCArray, HIPDevice, HIPStream @test AMDGPU.device(x) ≡ d1 end +@testset "GC finalizers leave task-local state alone" begin + # GC runs finalizers on whichever task it interrupts. Freeing an array makes + # HIP calls, which must not create state on that task. + weak_array() = WeakRef(ROCArray{Float32}(undef, 16)) + w = weak_array() + no_state, collected = fetch(@async begin + GC.gc(true) + (AMDGPU.task_local_state() ≡ nothing, w.value ≡ nothing) + end) + @test collected + @test no_state +end + @testset "Stream" begin s1 = @inferred AMDGPU.stream() @test s1 isa AMDGPU.HIPStream