Timer threads

#include <x/threads/timer.H>

x::timer bTimer(x::timer::create());
bTimer->setTimerName("background-task");

An x::timer is a reference-counted object that starts an on-demand thread that executes run() methods defined in x::timertaskObj objects:

#include <x/threads/timertask.H>

class myTaskObj : public x::timertaskObj {

// ...
public:
    myTaskObj() noexcept {}
    ~myTaskObj() noexcept {}

    void run();
};

typedef x::ptr<myTaskObj> myTask;

myTask newTask(myTask::create());

bTimer->schedule(newTask, std::chrono::system_clock::now()+std::chrono::seconds(2));

The tasks get executed in a separate execution thread. Each task gets executed once, or repeatedly, at predetermined intervals, until it's cancelled. One execution thread is associated with each x::timer object. It is not necessary to subclass x::timer and override its getName() method to set the execution thread's name for logging purposes. Instead, invoke setTimerName() before adding the first task to the timer object.

In this example, myTaskObj's run() method gets executed in two seconds. Other schedule() methods are available which schedule the task in various ways, including repeated executions at pretedermined intervals, until the task get cancelled:

newTask->cancel();

A task's run() may invoke its own cancel(). The cancellation takes effect when the task's run() method returns. Multiple x::timertaskObj instances can be scheduled with the same x::timer. Each x::timer runs a single execution thread. While the thread is executing a x::timertaskObj's run() method, all other pending x::timertaskObj objects wait until the current object's run() method terminates. This timer object is not suitable for long running tasks.

The timer thread holds a strong reference on each task object that's pending execution. In the case of a one-time task, the thread releases its reference on the task object after executing it, or until it gets cancelled. In the case of a repeated task, the thread releases its reference on the task object after it gets cancelled.

x::timertask::base::make_timer_task() constructs a subclass of x::timertaskObj from a functor:

bTimer->schedule(x::timertask::base::make_timer_task([]
    {
        dosomething();
    }), std::chrono::system_clock::now()+std::chrono::seconds(2));

make_timer_task takes a reference to a functor, and returns a reference to an instantiated subclass of a x::timertaskObj whose run() invokes the functor.

The functor takes no arguments, but it can use captured objects. Except under very controlled circumstances, the functor should capture by value, since the functor ends up getting executed by a different thread.

Using mcguffins to cancel recurring tasks

Once scheduled, the timer thread holds a strong reference on the timer task object, until the task is no longer scheduled. A recurring task must be explicitly cancelled.

x::timertaskObj's autocancel() method returns a mcguffin whose destructor invokes cancel() on the timer task. When the mcguffin goes out of scope and gets destroyed, this results in the task's automatic cancellation.

This also applies to task objects created by x::timertask::base::make_timer_task(), since they subclass x::timertaskObj:

auto doSomethingTask=x::timertask::base::make_timer_task([]
    {
        dosomething();
    });

x::ref<x::obj> mcguffin=doSomethingTask->autocancel();

bTimer->scheduleAtFixedRate(doSomethingTask, std::chrono::seconds(30));

When this mcguffin goes out of scope and gets destroyed, the task's cancel() method gets invoked, to cancel it.