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();
}