Skip to content

Commit 8cc00ff

Browse files
authored
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).
1 parent a4a0b04 commit 8cc00ff

File tree

1 file changed

+42
-25
lines changed

1 file changed

+42
-25
lines changed

base/asyncevent.jl

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

@@ -115,6 +124,7 @@ function _trywait(t::Union{Timer, AsyncCondition})
115124
# full barrier now for AsyncCondition
116125
t isa Timer || Core.Intrinsics.atomic_fence(:acquire_release)
117126
else
127+
t.isopen || return false
118128
t.handle == C_NULL && return false
119129
iolock_begin()
120130
set = t.set
@@ -123,14 +133,12 @@ function _trywait(t::Union{Timer, AsyncCondition})
123133
lock(t.cond)
124134
try
125135
set = t.set
126-
if !set
127-
if t.handle != C_NULL
128-
iolock_end()
129-
set = wait(t.cond)
130-
unlock(t.cond)
131-
iolock_begin()
132-
lock(t.cond)
133-
end
136+
if !set && t.isopen && t.handle != C_NULL
137+
iolock_end()
138+
set = wait(t.cond)
139+
unlock(t.cond)
140+
iolock_begin()
141+
lock(t.cond)
134142
end
135143
finally
136144
unlock(t.cond)
@@ -266,19 +274,28 @@ julia> begin
266274
"""
267275
function Timer(cb::Function, timeout::Real; interval::Real=0.0)
268276
timer = Timer(timeout, interval=interval)
269-
t = @task while _trywait(timer)
270-
try
271-
cb(timer)
272-
catch err
273-
write(stderr, "Error in Timer:\n")
274-
showerror(stderr, err, catch_backtrace())
275-
return
277+
t = @task begin
278+
unpreserve_handle(timer)
279+
while _trywait(timer)
280+
try
281+
cb(timer)
282+
catch err
283+
write(stderr, "Error in Timer:\n")
284+
showerror(stderr, err, catch_backtrace())
285+
return
286+
end
287+
isopen(timer) || return
288+
end
289+
end
290+
# here we are mimicking parts of _trywait, in coordination with task `t`
291+
preserve_handle(timer)
292+
@lock timer.cond begin
293+
if timer.set
294+
schedule(t)
295+
else
296+
_wait2(timer.cond, t)
276297
end
277-
isopen(timer) || return
278298
end
279-
lock(timer.cond)
280-
_wait2(timer.cond, t)
281-
unlock(timer.cond)
282299
return timer
283300
end
284301

0 commit comments

Comments
 (0)