Skip to content

Commit 4c2229a

Browse files
vtjnashKristofferC
authored andcommitted
Timer: handle timeout correctly (#42854)
I am not sure why we ever used round+1 instead of ceil+1, as this is simply strictly more correct. (cherry picked from commit d6f59fa)
1 parent 118344d commit 4c2229a

File tree

1 file changed

+19
-15
lines changed

1 file changed

+19
-15
lines changed

base/asyncevent.jl

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,13 @@ end
6060
6161
Create a timer that wakes up tasks waiting for it (by calling [`wait`](@ref) on the timer object).
6262
63-
Waiting tasks are woken after an initial delay of `delay` seconds, and then repeating with the given
64-
`interval` in seconds. If `interval` is equal to `0`, the timer is only triggered once. When
65-
the timer is closed (by [`close`](@ref)) waiting tasks are woken with an error. Use [`isopen`](@ref)
66-
to check whether a timer is still active.
63+
Waiting tasks are woken after an initial delay of at least `delay` seconds, and then repeating after
64+
at least `interval` seconds again elapse. If `interval` is equal to `0`, the timer is only triggered
65+
once. When the timer is closed (by [`close`](@ref)) waiting tasks are woken with an error. Use
66+
[`isopen`](@ref) to check whether a timer is still active.
67+
68+
Note: `interval` is subject to accumulating time skew. If you need precise events at a particular
69+
absolute time, create a new timer at each expiration with the difference to the next time computed.
6770
"""
6871
mutable struct Timer
6972
handle::Ptr{Cvoid}
@@ -74,8 +77,9 @@ mutable struct Timer
7477
function Timer(timeout::Real; interval::Real = 0.0)
7578
timeout 0 || throw(ArgumentError("timer cannot have negative timeout of $timeout seconds"))
7679
interval 0 || throw(ArgumentError("timer cannot have negative repeat interval of $interval seconds"))
77-
timeout = UInt64(round(timeout * 1000)) + 1
78-
interval = UInt64(ceil(interval * 1000))
80+
# libuv has a tendency to timeout 1 ms early, so we need +1 on the timeout (in milliseconds), unless it is zero
81+
timeoutms = ceil(UInt64, timeout * 1000) + !iszero(timeout)
82+
intervalms = ceil(UInt64, interval * 1000)
7983
loop = eventloop()
8084

8185
this = new(Libc.malloc(_sizeof_uv_timer), ThreadSynchronizer(), true, false)
@@ -87,7 +91,7 @@ mutable struct Timer
8791
ccall(:uv_update_time, Cvoid, (Ptr{Cvoid},), loop)
8892
err = ccall(:uv_timer_start, Cint, (Ptr{Cvoid}, Ptr{Cvoid}, UInt64, UInt64),
8993
this, @cfunction(uv_timercb, Cvoid, (Ptr{Cvoid},)),
90-
timeout, interval)
94+
timeoutms, intervalms)
9195
@assert err == 0
9296
iolock_end()
9397
return this
@@ -222,18 +226,18 @@ end
222226
"""
223227
Timer(callback::Function, delay; interval = 0)
224228
225-
Create a timer that wakes up tasks waiting for it (by calling [`wait`](@ref) on the timer object) and
226-
calls the function `callback`.
229+
Create a timer that runs the function `callback` at each timer expiration.
227230
228-
Waiting tasks are woken and the function `callback` is called after an initial delay of `delay` seconds,
229-
and then repeating with the given `interval` in seconds. If `interval` is equal to `0`, the timer
230-
is only triggered once. The function `callback` is called with a single argument, the timer itself.
231-
When the timer is closed (by [`close`](@ref)) waiting tasks are woken with an error. Use [`isopen`](@ref)
232-
to check whether a timer is still active.
231+
Waiting tasks are woken and the function `callback` is called after an initial delay of `delay`
232+
seconds, and then repeating with the given `interval` in seconds. If `interval` is equal to `0`, the
233+
callback is only run once. The function `callback` is called with a single argument, the timer
234+
itself. Stop a timer by calling `close`. The `cb` may still be run one final time, if the timer has
235+
already expired.
233236
234237
# Examples
235238
236-
Here the first number is printed after a delay of two seconds, then the following numbers are printed quickly.
239+
Here the first number is printed after a delay of two seconds, then the following numbers are
240+
printed quickly.
237241
238242
```julia-repl
239243
julia> begin

0 commit comments

Comments
 (0)