19 TimeOutTask

Hosts a thread with explicit Start/Stop functions. While the thread is running the task passed to Start() will be executed repeatedly. Between each execution of the task the threads waits for a timeout specified in milliseconds.

If Signal() is called while the thread is waiting then waiting is aborted and the task is called straightaway with signaled set to true.

Therefore if calls to Signal() are made regularly enough the task is always called with signaled set to true.

For each call to Start() there must be a subsequent call to Stop(). The TimeOutTask can be started then stopped any number of times.

Signal(), SetTimeOut(), Abort() can be called by any thread at any time - whether the TimeOutTask is started or not.

The implementation is based on an AutoResetEvent.

TimeOutTask.h

// TimeOutTask.h
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2013

#pragma once
#ifndef Ceda_cxThread_TimeOutTask_H
#define Ceda_cxThread_TimeOutTask_H

#include "cxThread.h"
#include "AutoResetEvent.h"
#include "ThreadName.h"
#include <thread>
#include <functional>
#include <atomic>

#ifdef _MSC_VER
    // struct 'X' needs to have dll-interface to be used by clients of class 'Y'
    #pragma warning(disable:4251)
#endif

namespace ceda
{
class cxThread_API TimeOutTask
{
public:
    TimeOutTask();
    ~TimeOutTask();

    void Start(const char* threadName, int32 timeOutmsec, std::function<void(bool signaled)> task);
    void Stop();
    void SetTimeOut(int32 timeOutmsec);
    void Signal();

    // A task can call this to poll whether it should complete as soon as possible
    bool Abort() const { return !started_; } 

private:
    xstring threadName_;
    std::thread thread_;
    std::atomic<int32> timeOutmsec_;
    volatile bool started_;
    AutoResetEvent wakeThreadEvent_;
    std::function<void(bool signaled)> task_;
};
} // namespace ceda
#endif // include guard

TimeOutTask.cpp

// TimeOutTask.cpp
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2013

#include "TimeOutTask.h"

namespace ceda
{
TimeOutTask::TimeOutTask() : timeOutmsec_(0), started_(false) {}

TimeOutTask::~TimeOutTask()
{
    cxAssert(!started_);
}

void TimeOutTask::Start(const char* threadName, int32 timeOutmsec, std::function<void(bool)> task)
{
    threadName_ = threadName;
    task_ = task;
    timeOutmsec_ = timeOutmsec;
    cxAssert(!started_);
    started_ = true;
    thread_ = std::thread( 
        [this]
        {
            SetThreadName(threadName_.c_str());
            while(1)
            {
                bool signaled = wakeThreadEvent_.Wait(timeOutmsec_);
                if (!started_) return;
                task_(signaled);
            }
        });
}

void TimeOutTask::Stop()
{
    cxAssert(started_);
    started_ = false;
    wakeThreadEvent_.Signal();
    thread_.join();
}

void TimeOutTask::SetTimeOut(int32 timeOutmsec)
{
    timeOutmsec_ = timeOutmsec;
}

void TimeOutTask::Signal()
{
    wakeThreadEvent_.Signal();
}

} // namespace ceda

TimeOutTaskTests.cpp in txThread

When the main thread is sleeping the TimeOutTask object is timing out so the task is called with signaled equal to false.

When the main thread is being signaled often enough the TimeOutTask object is not timing out so the task is called with signaled equal to true.

UnitTestTimeOutTask
    Begin sleep
        task(false) called
        task(false) called
        task(false) called
    End sleep
        task(true) called
        task(true) called
        task(true) called
        task(true) called
        task(true) called
        task(true) called
        task(true) called
        task(true) called
        task(true) called
        task(true) called
        task(true) called
        task(true) called
        task(true) called
        task(true) called
        task(true) called
        task(true) called
        task(true) called
        task(true) called
        task(true) called
        task(true) called
        task(true) called
        task(true) called
        task(true) called
        task(true) called
        task(true) called
        task(true) called
        task(true) called
        task(true) called
        task(true) called
        task(true) called
// TimeOutTaskTests.cpp
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2013

#include "Ceda/cxThread/TimeOutTask.h"
#include "Ceda/cxUtils/Tracer.h"
#include "Ceda/cxUtils/HPTime.h"

void UnitTestTimeOutTask()
{
    Tracer() << "UnitTestTimeOutTask\n";
    ceda::TraceIndenter indent(4);
    
    ceda::TimeOutTask st;

    st.Start("UnitTestTimeOutTask", 20, 
        [](bool signaled)
        {
            ceda::TraceIndenter indent(8);
            Tracer() << "task(" << (signaled?"true":"false") << ") called\n";
        });

    Tracer() << "Begin sleep\n";
    Sleep(100);
    Tracer() << "End sleep\n";

    for (int i=0 ; i < 5 ; ++i)
    {
        st.Signal();
        st.Signal();
        st.Signal();
        Sleep(10);

        st.Signal();
        Sleep(10);

        st.Signal();
        Sleep(10);

        st.Signal();
        Sleep(10);

        st.Signal();
        Sleep(10);

        st.Signal();
        st.Signal();
        Sleep(10);
    }

    st.Stop();
}