PerMalmberg/libcron

every second cron doesn't trigger `tick` but `time_until_next` returns 0

Closed this issue · 3 comments

libcron::Cron<> cron;

cron.add_schedule("Test 1", "* * * * * ?", [](auto&& a) {
    if (a.get_delay() > std::chrono::seconds{1}) {
        std::cout << "Late launch " << a.get_delay().count() << std::endl;
    }

    std::cout << "SECONDS" << std::endl;

    return;
});

while (true) {
    cron.tick();

    std::cout << '.';
    std::this_thread::sleep_for(cron.time_until_next());
}

With this code thread will sleep for 0 seconds around 100k+ times, then it triggers the task, then time_until_next returns almost perfect 1s.

I expected it to always return a valid time that can be used for sleep_for function, but something is wrong and I suspect tick, because time_until_next is pretty straightforward.

The code above can be fixed by adding a check:

cron.tick();
auto&& sleep_for = cron.time_until_next();

if (sleep_for == std::chrono::seconds{0}) {
    sleep_for = std::chrono::seconds{1};
}

std::this_thread::sleep_for(sleep_for);

But this prints that task is late (Example: Late launch 1001189130). So adding delay of 1 second tells me I missed previous second.
What's interesting, calling time_until_next then tick fixes that problem. (but only for every second schedule)
Perhaps, next_schedule_time method could solve this by returning task.next_schedule value. It can be used in std::this_thread::sleep_until

cron.time_until_next() will return 0 as long as a task is due for the current second, it was not designed to be used as you do. I can see why you'd want to use it like this though, but it is working as intended.

as long as a task is due for the current second

If it's due for the current second, why .tick() doesn't process it? Or it's arbitrary?

Tasks are only updated once at least a second has passed which is why things work the way they do.

https://github.com/PerMalmberg/libcron/blob/master/libcron%2Finclude%2Flibcron%2FCron.h#L196