Unexpected second print value in a repeating schedule timer of DispatchSource, what is it?

I'm testing a timer that is returning two fired messages when I only expect one at each time or trigger:

let queue = DispatchQueue(label: "com.domain.app.timer", qos: .userInteractive)
let timer = DispatchSource.makeTimerSource(flags: .strict, queue: queue)

timer.schedule(deadline: .now(), repeating: .seconds(1), leeway: .nanoseconds(0))

var elapsedTime = DispatchTime.now().uptimeNanoseconds

timer.setEventHandler {
  elapsedTime = DispatchTime.now().uptimeNanoseconds - elapsedTime
  print("Timer Fired: elapsedTime: \(elapsedTime/1000000000)")


The output is:

**Timer Fired: elapsedTime: 0**
**Timer Fired: elapsedTime: 131188**

**Timer Fired: elapsedTime: 1**
**Timer Fired: elapsedTime: 131189**

**Timer Fired: elapsedTime: 2**
**Timer Fired: elapsedTime: 131190**

**Timer Fired: elapsedTime: 3**
**Timer Fired: elapsedTime: 131191**

**Timer Fired: elapsedTime: 4**
**Timer Fired: elapsedTime: 131192**

**Timer Fired: elapsedTime: 5**
**Timer Fired: elapsedTime: 131193**

What's the second value per call?

I tried your code, it fires once per second, as expected. But your elapsedTime computation is a little fishy. Try this:

var last = DispatchTime.now().uptimeNanoseconds

timer.setEventHandler {
  let current = DispatchTime.now().uptimeNanoseconds
  let elapsed = current - last
  last = current
  print("Timer Fired: elapsedTime: \(Double(elapsed)/1000000000)")


I don't know how you run it, but there's no runloop in your sample code, so I use dispatchMain for that.

Another thing. If you don't plan to relax the firing time, you don't need to specify leeway value (which is already 0 by default). Even if you specify leeway of 0, it's not strictly guaranteed that it will fire at/before the deadline.

Yours works fine! Thanks once again @Lantua
Yeh, I was using the PlayGround, sorry. So, I thought this part was fine:

elapsedTime = DispatchTime.now().uptimeNanoseconds - elapsedTime

where elapsedTime is a mutable variable outside the setEventHandler, was that the problem? Do I have to keep all values in separate variables in Swift? I thought it'd compute and update elapsedTime for each subsequent call...

Well, umm, I think your understanding of the variable works fine, and every call does use the shared elapsedTime. Just that the way you compute it doesn't make much sense. Try to hand-calculate it on the paper for the first few values and you might see. With now returning something like 0s, 1s, 2s, etc.

You're right, haven't noticed and that was the problem; Thank you : )