Skip to content

Commit 8d13430

Browse files
vtjnashKristofferC
authored andcommitted
asyncevents: fix missing GC root and race (#44956)
The event might have triggered on another thread before we observed it here, or it might have gotten finalized before it got triggered. Either outcome can result in a lost event. (I observed the later situation occurring locally during the Dates test once). (cherry picked from commit 8cc00ff)
1 parent 5b8d99a commit 8d13430

File tree

1 file changed

+46
-26
lines changed

1 file changed

+46
-26
lines changed

base/asyncevent.jl

Lines changed: 46 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,22 @@ the async condition object itself.
4343
"""
4444
function AsyncCondition(cb::Function)
4545
async = AsyncCondition()
46-
t = @task while _trywait(async)
47-
cb(async)
48-
isopen(async) || return
46+
t = @task begin
47+
unpreserve_handle(async)
48+
while _trywait(async)
49+
cb(async)
50+
isopen(async) || return
51+
end
52+
end
53+
# here we are mimicking parts of _trywait, in coordination with task `t`
54+
preserve_handle(async)
55+
@lock async.cond begin
56+
if async.set
57+
schedule(t)
58+
else
59+
_wait2(async.cond, t)
60+
end
4961
end
50-
lock(async.cond)
51-
_wait2(async.cond, t)
52-
unlock(async.cond)
5362
return async
5463
end
5564

@@ -103,7 +112,11 @@ unsafe_convert(::Type{Ptr{Cvoid}}, async::AsyncCondition) = async.handle
103112

104113
function _trywait(t::Union{Timer, AsyncCondition})
105114
set = t.set
106-
if !set
115+
if set
116+
# full barrier now for AsyncCondition
117+
t isa Timer || Core.Intrinsics.atomic_fence(:acquire_release)
118+
else
119+
t.isopen || return false
107120
t.handle == C_NULL && return false
108121
iolock_begin()
109122
set = t.set
@@ -112,14 +125,12 @@ function _trywait(t::Union{Timer, AsyncCondition})
112125
lock(t.cond)
113126
try
114127
set = t.set
115-
if !set
116-
if t.handle != C_NULL
117-
iolock_end()
118-
set = wait(t.cond)
119-
unlock(t.cond)
120-
iolock_begin()
121-
lock(t.cond)
122-
end
128+
if !set && t.isopen && t.handle != C_NULL
129+
iolock_end()
130+
set = wait(t.cond)
131+
unlock(t.cond)
132+
iolock_begin()
133+
lock(t.cond)
123134
end
124135
finally
125136
unlock(t.cond)
@@ -255,19 +266,28 @@ julia> begin
255266
"""
256267
function Timer(cb::Function, timeout::Real; interval::Real=0.0)
257268
timer = Timer(timeout, interval=interval)
258-
t = @task while _trywait(timer)
259-
try
260-
cb(timer)
261-
catch err
262-
write(stderr, "Error in Timer:\n")
263-
showerror(stderr, err, catch_backtrace())
264-
return
269+
t = @task begin
270+
unpreserve_handle(timer)
271+
while _trywait(timer)
272+
try
273+
cb(timer)
274+
catch err
275+
write(stderr, "Error in Timer:\n")
276+
showerror(stderr, err, catch_backtrace())
277+
return
278+
end
279+
isopen(timer) || return
280+
end
281+
end
282+
# here we are mimicking parts of _trywait, in coordination with task `t`
283+
preserve_handle(timer)
284+
@lock timer.cond begin
285+
if timer.set
286+
schedule(t)
287+
else
288+
_wait2(timer.cond, t)
265289
end
266-
isopen(timer) || return
267290
end
268-
lock(timer.cond)
269-
_wait2(timer.cond, t)
270-
unlock(timer.cond)
271291
return timer
272292
end
273293

0 commit comments

Comments
 (0)