The library is tested on linux, macOs and windows.
Schedulers are instances of the CallScheduler
class, which resides in the ttt
(task time table) namespace:
#include "scheduler.h"
{
bool compensate = true; // Whether to calculate the time point of task
// executions by adding the interval to the:
// true: task start / false: task finish.
unsigned nWorkers = 1; // Number of workers that run tasks.
ttt::CallScheduler plan(compensate, nWorkers);
}
Adding a task to the scheduler is done using its add
method:
{
auto myTask = []{
ttt::Result ret{ ttt::Result::Repeat };
if ( /*user logic*/ )
{
ret = ttt::Result::Finish;
}
return ret; // A user task returns whether to repeat or not.
};
auto token = plan.add(
myTask, // User tasks are std::function<ttt::Result()>
500ms, // Interval for execution or repetition
false); // Whether to immediately queue the task for execution
}
As shown above, the addition of a task returns a token marked [[no_discard]]
. Tokens control the behavior of the associated task:
- The token is alive
=>
Task is allowed to run. - The token is destroyed
=>
Tasks are prevented from running after token destruction and are removed from the scheduler. - The token is detached
=>
Task is independent from the token state.
To detach a token from its task, call the detach()
method. If there is no need to ever cancel a task, e.g. it dies with the scheduler, the following syntax can be used to ignore the token upon creation:
plan.add(myTask, 500ms, false).detach();
// user task ^^ ^^ ^^ ^^^^^^ Dissociate the token from task execution.
// interval ^^ ^^
// schedule immediately ^^
Users that don't want to use scopes and object lifetimes to control the execution of a task, i.e. let the destructor of token cancel the task, can wrap tokens in std::optional
and explicitly trigger the cancellation of a task:
auto token = std::optional(plan.add(myTask, 500ms, false));
//
// Let the task run for a desired amount of time.
//
token.reset(); // Triggers the token's destructor which cancels task execution.
The timeline class is a container of chrono-restricted tasks. Different flavors of tasks that can be defined include:
- Timers : repeatable with stateful countdown
- Pulses : repeatable with period state
- Alarms : one-off task with interval state e.g. a deferred notification.
Usage is documented in the respective header (timeline.h
), but essentially each task type has methods to be added, removed, stopped and so on, as seen in the following example.
ttt::Timeline schedule;
std::string const name("timer1");
schedule.timerAdd(
name, // Timer name.
100ms, // Resolution (frequency of timer ticks)
1s, // Duration - what we count down from
false, // Countdown again, after running a duration cycle
[](TimerState const& s) { // Invoked on every resolution step
std::cout << s.remaining.count() << "ms out of " << s.duration.count() << "ms\n";
},
true // Trigger the timer upon addition - Callback invoked with remaining=duration
);
auto serializedState = schedule.serialize(true, true, true);
// what to serialize, i.e. timers^ pulses^ ^alarms
ttt::Timeline tl2(serializedState); // Recreation of a timeline from serialized state.
Build by making a build directory (i.e. build/
), run cmake
in that dir, and then use make
to build the desired target.
Example:
> mkdir build && cd build
> cmake .. -DCMAKE_BUILD_TYPE=[Debug | Coverage | Release]
> make
> ./main
> make test # Makes and runs the tests.
> make coverage # Generate a coverage report.
> make doc # Generate html documentation.
A convenience script rebuild_all.sh
is provided for users that want to generate all types of build, i.e. release, sanitizers (thread & address) and debug.