From 54322d5f8032b7f5e46c0145b7aecdafb20800dc Mon Sep 17 00:00:00 2001 From: lgeissbauer Date: Thu, 27 Jun 2024 05:30:31 -0700 Subject: [PATCH 1/2] don't throw EOFError from sleep We will no longer throw a error if the timer fires after we first checked set. But still keeps this case functional: ``` julia> t = Timer(1000000000); julia> close(t) julia> t.set false julia> wait(t) ERROR: EOFError: read end of file Stacktrace: [1] wait(t::Timer) @ Base .\asyncevent.jl:159 [2] top-level scope @ REPL[16]:1 ``` --- base/asyncevent.jl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/base/asyncevent.jl b/base/asyncevent.jl index 3c782be10e194..e63963b23689a 100644 --- a/base/asyncevent.jl +++ b/base/asyncevent.jl @@ -127,8 +127,11 @@ function _trywait(t::Union{Timer, AsyncCondition}) t isa Timer || Core.Intrinsics.atomic_fence(:acquire_release) else if !isopen(t) - close(t) # wait for the close to complete - return false + set = t.set + if !set + close(t) # wait for the close to complete + return false + end end iolock_begin() set = t.set From 4f978db8ceed369a0038597b4822bbfedc2ea64e Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 16 Jul 2024 20:51:38 -0400 Subject: [PATCH 2/2] add acquire/release ordering --- base/asyncevent.jl | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/base/asyncevent.jl b/base/asyncevent.jl index e63963b23689a..c6cb3d3fa73bb 100644 --- a/base/asyncevent.jl +++ b/base/asyncevent.jl @@ -154,7 +154,7 @@ function _trywait(t::Union{Timer, AsyncCondition}) end iolock_end() end - @atomic :monotonic t.set = false + @atomic :monotonic t.set = false # if there are multiple waiters, an unspecified number may short-circuit past here return set end @@ -164,14 +164,14 @@ function wait(t::Union{Timer, AsyncCondition}) end -isopen(t::Union{Timer, AsyncCondition}) = t.isopen && t.handle != C_NULL +isopen(t::Union{Timer, AsyncCondition}) = @atomic :acquire t.isopen function close(t::Union{Timer, AsyncCondition}) - t.handle == C_NULL && return # short-circuit path + t.handle == C_NULL && !t.isopen && return # short-circuit path, :monotonic iolock_begin() if t.handle != C_NULL if t.isopen - @atomic :monotonic t.isopen = false + @atomic :release t.isopen = false ccall(:jl_close_uv, Cvoid, (Ptr{Cvoid},), t) end # implement _trywait here without the auto-reset function, just waiting for the final close signal @@ -189,6 +189,8 @@ function close(t::Union{Timer, AsyncCondition}) unlock(t.cond) unpreserve_handle(t) end + elseif t.isopen + @atomic :release t.isopen = false end iolock_end() nothing @@ -201,8 +203,8 @@ function uvfinalize(t::Union{Timer, AsyncCondition}) if t.handle != C_NULL disassociate_julia_struct(t.handle) # not going to call the usual close hooks anymore if t.isopen - @atomic :monotonic t.isopen = false - ccall(:jl_close_uv, Cvoid, (Ptr{Cvoid},), t.handle) + @atomic :release t.isopen = false + ccall(:jl_close_uv, Cvoid, (Ptr{Cvoid},), t.handle) # this will call Libc.free end @atomic :monotonic t.handle = C_NULL notify(t.cond, false) @@ -217,8 +219,10 @@ end function _uv_hook_close(t::Union{Timer, AsyncCondition}) lock(t.cond) try - @atomic :monotonic t.isopen = false - Libc.free(@atomicswap :monotonic t.handle = C_NULL) + handle = t.handle + @atomic :release t.isopen = false + @atomic :monotonic t.handle = C_NULL + Libc.free(handle) notify(t.cond, false) finally unlock(t.cond) @@ -246,7 +250,7 @@ function uv_timercb(handle::Ptr{Cvoid}) if ccall(:uv_timer_get_repeat, UInt64, (Ptr{Cvoid},), t) == 0 # timer is stopped now if t.isopen - @atomic :monotonic t.isopen = false + @atomic :release t.isopen = false ccall(:jl_close_uv, Cvoid, (Ptr{Cvoid},), t) end end