A C++ scheduling library using cron formatting.
Libcron offers an easy to use API to add callbacks with corresponding cron-formatted strings:
libcron::Cron cron;
cron.add_schedule("Hello from Cron", "* * * * * ?", [=](auto&) {
std::cout << "Hello from libcron!" std::endl;
});
To trigger the execution of callbacks, one must call libcron::Cron::tick
at least once a second to prevent missing schedules:
while(true)
{
cron.tick();
std::this_thread::sleep_for(500mS);
}
The callback must have the following signature:
std::function<void(const libcron::TaskInformation&)>
libcron::Taskinformation
offers a convenient API to retrieve further information:
libcron::TaskInformation::get_delay
informs about the delay between planned and actual execution of the callback. Hence, it is possible to ensure that a task was executed within a specific tolerance:
libcron::Cron cron;
cron.add_schedule("Hello from Cron", "* * * * * ?", [=](auto& i) {
using namespace std::chrono_literals;
if (i.get_delay() >= 1s)
{
std::cout << "The Task was executed too late..." << std::endl;
}
});
libcron::Cron offers two convenient functions to remove schedules:
clear_schedules()
will remove all schedulesremove_schedule(std::string)
will remove a specific schedule
For example, cron.remove_schedule("Hello from Cron")
will remove the previously added task.
When Calling libcron::Cron::tick
from another thread than add_schedule
, clear_schedule
and remove_schedule
, one must take care to protect the internal resources of libcron::Cron
so that tasks are not removed or added while libcron::Cron
is iterating over the schedules. libcron::Cron
can take care of that, you simply have to define your own aliases:
/* The default class uses NullLock, which does not lock the resources at runtime */
template<typename ClockType = libcron::LocalClock, typename LockType = libcron::NullLock>
class Cron
{
...
}
/* Define an alias for a thread-safe Cron scheduler which automatically locks ressources when needed */
using CronMt = libcron::Cron<libcron::LocalClock, libcron::Locker>
CronMt cron;
cron.add_schedule("Hello from Cron", "* * * * * ?", [=]() {
std::cout << "Hello from CronMt!" std::endl;
});
....
However, this comes with costs: Whenever you call tick
, a std::mutex
will be locked and unlocked. So only use the libcron::Locker
to protect resources when you really need too.
This library uses std::chrono::system_clock::timepoint
as its time unit. While that is UTC by default, the Cron-class
uses a LocalClock
by default which offsets system_clock::now()
by the current UTC-offset. If you wish to work in
UTC, then construct the Cron instance, passing it a libcron::UTCClock
.
This implementation supports cron format, as specified below.
Each schedule expression conststs of 6 parts, all mandatory. However, if 'day of month' specifies specific days, then 'day of week' is ignored.
┌──────────────seconds (0 - 59)
│ ┌───────────── minute (0 - 59)
│ │ ┌───────────── hour (0 - 23)
│ │ │ ┌───────────── day of month (1 - 31)
│ │ │ │ ┌───────────── month (1 - 12)
│ │ │ │ │ ┌───────────── day of week (0 - 6) (Sunday to Saturday)
│ │ │ │ │ │
│ │ │ │ │ │
│ │ │ │ │ │
* * * * * *
- Allowed formats:
-
Special characters: '*', meaning the entire range.
-
'?' used to ignore day of month/day of week as noted below.
-
Ranges: 1,2,4-6
- Result: 1,2,4,5,6
-
Steps: n/m, where n is the start and m is the step.
1/2
yields 1,3,5,7...5/3
yields 5,8,11,14...*/2
yields Result: 1,3,5,7...
-
Reversed ranges:
0 0 23-2 * * *
, meaning top of each minute and hour, of hours, 23, 0, 1 and 2, every day.- Compare to
0 0 2-23 * * *
which means top of each minute and hour, of hours, 2,3...21,22,23 every day.
- Compare to
-
For month
, these (case insensitive) strings can be used instead of numbers: JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC
.
Example: JAN,MAR,SEP-NOV
For day of week
, these (case insensitive) strings can be used instead of numbers: SUN, MON, TUE, WED, THU, FRI, SAT
.
Example: MON-THU,SAT
Each part is separated by one or more whitespaces. It is thus important to keep whitespaces out of the respective parts.
-
Valid:
- 0,3,40-50 * * * * ?
-
Invalid:
- 0, 3, 40-50 * * * * ?
Day of month
and day of week
are mutually exclusive so one of them must at always be ignored using
the '?'-character to ensure that it is not possible to specify a statement which results in an impossible mix of these fields.
Expression | Meaning |
---|---|
* * * * * ? | Every second |
0 0 12 * * MON-FRI | Every Weekday at noon |
0 0 12 1/2 * ? | Every 2 days, starting on the 1st at noon |
0 0 */12 ? * * | Every twelve hours |
The standard cron format does not allow for randomization, but with the use of CronRandomization
you can generate random
schedules using the following format: R(range_start-range_end)
, where range_start
and range_end
follow the same rules
as for a regular cron range (step-syntax is not supported). All the rules for a regular cron expression still applies
when using randomization, i.e. mutual exclusiveness and no extra spaces.
Expression | Meaning |
---|---|
0 0 R(13-20) * * ? | On the hour, on a random hour 13-20, inclusive. |
0 0 0 ? * R(0-6) | A random weekday, every week, at midnight. |
0 R(45-15) */12 ? * * | A random minute between 45-15, inclusive, every 12 hours. |
0 0 0 ? R(DEC-MAR) R(SAT-SUN) | On the hour, on a random month december to march, on a random weekday saturday to sunday. |
Howard Hinnant's date libraries