6060
6161Create 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""" 
6871mutable 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 
239243julia> begin 
0 commit comments