Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Timer: handle timeout correctly #42854

Merged
merged 1 commit into from
Nov 8, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 19 additions & 15 deletions base/asyncevent.jl
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,13 @@ end

Create a timer that wakes up tasks waiting for it (by calling [`wait`](@ref) on the timer object).

Waiting tasks are woken after an initial delay of `delay` seconds, and then repeating with the given
`interval` in seconds. If `interval` is equal to `0`, the timer is only triggered once. When
the timer is closed (by [`close`](@ref)) waiting tasks are woken with an error. Use [`isopen`](@ref)
to check whether a timer is still active.
Waiting tasks are woken after an initial delay of at least `delay` seconds, and then repeating after
at least `interval` seconds again elapse. If `interval` is equal to `0`, the timer is only triggered
once. When the timer is closed (by [`close`](@ref)) waiting tasks are woken with an error. Use
[`isopen`](@ref) to check whether a timer is still active.

Note: `interval` is subject to accumulating time skew. If you need precise events at a particular
absolute time, create a new timer at each expiration with the difference to the next time computed.
"""
mutable struct Timer
handle::Ptr{Cvoid}
Expand All @@ -74,8 +77,9 @@ mutable struct Timer
function Timer(timeout::Real; interval::Real = 0.0)
timeout ≥ 0 || throw(ArgumentError("timer cannot have negative timeout of $timeout seconds"))
interval ≥ 0 || throw(ArgumentError("timer cannot have negative repeat interval of $interval seconds"))
timeout = UInt64(round(timeout * 1000)) + 1
interval = UInt64(ceil(interval * 1000))
# libuv has a tendency to timeout 1 ms early, so we need +1 on the timeout (in milliseconds), unless it is zero
timeoutms = ceil(UInt64, timeout * 1000) + !iszero(timeout)
intervalms = ceil(UInt64, interval * 1000)
loop = eventloop()

this = new(Libc.malloc(_sizeof_uv_timer), ThreadSynchronizer(), true, false)
Expand All @@ -87,7 +91,7 @@ mutable struct Timer
ccall(:uv_update_time, Cvoid, (Ptr{Cvoid},), loop)
err = ccall(:uv_timer_start, Cint, (Ptr{Cvoid}, Ptr{Cvoid}, UInt64, UInt64),
this, @cfunction(uv_timercb, Cvoid, (Ptr{Cvoid},)),
timeout, interval)
timeoutms, intervalms)
@assert err == 0
iolock_end()
return this
Expand Down Expand Up @@ -222,18 +226,18 @@ end
"""
Timer(callback::Function, delay; interval = 0)

Create a timer that wakes up tasks waiting for it (by calling [`wait`](@ref) on the timer object) and
calls the function `callback`.
Create a timer that runs the function `callback` at each timer expiration.

Waiting tasks are woken and the function `callback` is called after an initial delay of `delay` seconds,
and then repeating with the given `interval` in seconds. If `interval` is equal to `0`, the timer
is only triggered once. The function `callback` is called with a single argument, the timer itself.
When the timer is closed (by [`close`](@ref)) waiting tasks are woken with an error. Use [`isopen`](@ref)
to check whether a timer is still active.
Waiting tasks are woken and the function `callback` is called after an initial delay of `delay`
seconds, and then repeating with the given `interval` in seconds. If `interval` is equal to `0`, the
callback is only run once. The function `callback` is called with a single argument, the timer
itself. Stop a timer by calling `close`. The `cb` may still be run one final time, if the timer has
already expired.

# Examples

Here the first number is printed after a delay of two seconds, then the following numbers are printed quickly.
Here the first number is printed after a delay of two seconds, then the following numbers are
printed quickly.

```julia-repl
julia> begin
Expand Down