2

I'm trying to create multiple periodic timers for my macOS launch daemon. I've written the following class to deal with the timer object:

struct MyTimer
{

MyTimer()
{
}

~MyTimer()
{
    stopTimer();
}


bool setTimer(size_t nmsPeriod, //Period in milliseconds
              void (*pfn)(void) //Function to call when timer fires
             )
{
    stopTimer();

    CFTimeInterval fInterval = ((CFTimeInterval)nmsPeriod) / 1000.0;

    CFRunLoopTimerContext ctx = {};
    ctx.info = this;

    _pfnCallback = pfn;

    _refTmr = CFRunLoopTimerCreate(kCFAllocatorDefault,
                                   CFAbsoluteTimeGetCurrent() + fInterval,
                                   fInterval,
                                   0, 0,
                                   _callbackTmr,
                                   &ctx);

    if(!_refTmr)
        return false;

    CFRunLoopAddTimer(CFRunLoopGetCurrent(), _refTmr, kCFRunLoopCommonModes);

    return true;
}

void stopTimer()
{
    if(_refTmr)
    {
        CFRunLoopTimerInvalidate(_refTmr);
            
        CFRelease(_refTmr);
            
        _refTmr = NULL;
    }
}


protected:
    static void _callbackTmr(CFRunLoopTimerRef timer, void *info)
    {
        MyTimer* pThis = (MyTimer*)info;
        pThis->_pfnCallback();
    }


private:

    CFRunLoopTimerRef _refTmr = NULL;
    void (*_pfnCallback)(void) = NULL;
}

Both timer classes are defined on the global scale for the daemon:

MyTimer gTmr1;
MyTimer gTmr2;

And this is how I initiate those timers when the daemon launches:

//Set 1st timer 2 minutes from now
gTmr1.setTimer(2 * 60 * 1000, callback1);

//Set 2nd timer 300 seconds from now
gTmr2.setTimer(300 * 1000, callback2);

//And then reset the 2nd timer 2 seconds from now
gTmr2.setTimer(2 * 1000, callback2);

// ...

//Enter the run-loop...
CFRunLoopRun();

What happens is that callback2 never fires, and only callback1:

static void callback1(void)
{
   log("Callback 1 fired");
}

static void callback2(void)
{
   log("Callback 2 fired");
}

So what am I doing wrong?

c00000fd
  • 20,994
  • 29
  • 177
  • 400
  • It would probably help to know what the behaviour is when you don't reset `gTmr2` and just let it run after the first setting. Also, just to check: `callback1` is fired only for `gTmr1`, you're not erroneously getting a `callback1` for the second timer too? – pmdj May 05 '23 at 12:18
  • @pmdj: I found what the issue was. I was calling `gTmr2.setTimer(2 * 1000, callback2)` from a thread other than the main one where the run-loop was. – c00000fd May 05 '23 at 12:56
  • Do you know btw, how I can set that timer from another thread? – c00000fd May 05 '23 at 12:57
  • 1
    Instead of `CFRunLoopGetCurrent()`, use [`CFRunLoopGetMain()`](https://developer.apple.com/documentation/corefoundation/1542890-cfrunloopgetmain?language=objc). I *think* `CFRunLoopAddTimer` should be safe to call from a thread other than the one the runloop is on. – pmdj May 05 '23 at 13:29

0 Answers0