Files
pvData/testApp/misc/testTimer.cpp
2018-04-02 09:34:15 -07:00

243 lines
7.2 KiB
C++

/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
/*
* testTimer.cpp
*
* Created on: 2010.11
* Author: Marty Kraimer
*/
#include <cstddef>
#include <cstdlib>
#include <cstddef>
#include <string>
#include <cstdio>
#include <iostream>
#include <exception>
#include <epicsUnitTest.h>
#include <testMain.h>
#include <epicsGuard.h>
#include <pv/timeStamp.h>
#include <pv/event.h>
#include <pv/timer.h>
#include <pv/thread.h>
using namespace epics::pvData;
using std::string;
static const double delays[3] = {1.0, 2.0, 4.0};
static const unsigned ntimes = 3;
namespace {
struct Marker : public TimerCallback
{
POINTER_DEFINITIONS(Marker);
Event wait, hold;
virtual ~Marker() {}
virtual void callback()
{
wait.signal();
hold.wait();
}
virtual void timerStopped() {}
};
struct MyCallback : public TimerCallback {
POINTER_DEFINITIONS(MyCallback);
MyCallback(const string& name)
:name(name)
,counter(0)
,first_gbl(0)
{}
virtual ~MyCallback() {}
virtual void callback()
{
testDiag("expire %s",name.c_str());
{
epicsGuard<Mutex> G(gbl_mutex);
counter++;
if(first_gbl==0) {
first_gbl = ++gbl_counter;
}
}
wait.signal();
}
virtual void timerStopped()
{
testDiag("timerStopped %s",name.c_str());
}
void clear() {
epicsGuard<Mutex> G(gbl_mutex);
counter = gbl_counter = 0;
}
const string name;
unsigned counter, first_gbl;
Event wait;
static unsigned gbl_counter;
static Mutex gbl_mutex;
};
unsigned MyCallback::gbl_counter;
Mutex MyCallback::gbl_mutex;
typedef std::tr1::shared_ptr<MyCallback> MyCallbackPtr;
}// namespace
static void testBasic(unsigned oneOrd, unsigned twoOrd, unsigned threeOrd)
{
testDiag("testBasic oneOrd %u twoOrd %u threeOrd %u",
oneOrd,twoOrd,threeOrd);
Timer timer("timer" ,middlePriority);
Marker::shared_pointer marker(new Marker);
MyCallbackPtr callbackOne(new MyCallback("one"));
MyCallbackPtr callbackTwo(new MyCallback("two"));
MyCallbackPtr callbackThree(new MyCallback("three"));
for(unsigned n=0; n<ntimes; n++) {
testDiag("iteration %u", n);
testOk1(!timer.isScheduled(marker));
timer.scheduleAfterDelay(marker, 0.01);
marker->wait.wait();
// timer worker is blocked
testOk1(!timer.isScheduled(callbackOne));
testOk1(!timer.isScheduled(callbackTwo));
testOk1(!timer.isScheduled(callbackThree));
callbackOne->clear();
callbackTwo->clear();
callbackThree->clear();
timer.scheduleAfterDelay(callbackOne,delays[oneOrd]);
timer.scheduleAfterDelay(callbackTwo,delays[twoOrd]);
timer.scheduleAfterDelay(callbackThree,delays[threeOrd]);
testOk1(timer.isScheduled(callbackOne));
testOk1(timer.isScheduled(callbackTwo));
testOk1(timer.isScheduled(callbackThree));
marker->hold.signal(); // let the worker loose
callbackOne->wait.wait();
callbackTwo->wait.wait();
callbackThree->wait.wait();
testOk1(!timer.isScheduled(callbackOne));
testOk1(!timer.isScheduled(callbackTwo));
testOk1(!timer.isScheduled(callbackThree));
{
epicsGuard<Mutex> G(MyCallback::gbl_mutex);
testOk(callbackOne->counter==1, "%s counter %u = 1", callbackOne->name.c_str(), callbackOne->counter);
testOk(callbackTwo->counter==1, "%s counter %u = 1", callbackTwo->name.c_str(), callbackTwo->counter);
testOk(callbackThree->counter==1, "%s counter %u = 1", callbackThree->name.c_str(), callbackThree->counter);
testOk(callbackOne->first_gbl==oneOrd+1, "%s first_gbl %u = %u", callbackOne->name.c_str(), callbackOne->first_gbl, oneOrd+1);
testOk(callbackTwo->first_gbl==twoOrd+1, "%s first_gbl %u = %u", callbackTwo->name.c_str(), callbackTwo->first_gbl, twoOrd+1);
testOk(callbackThree->first_gbl==threeOrd+1, "%s first_gbl %u = %u", callbackThree->name.c_str(), callbackThree->first_gbl, threeOrd+1);
}
}
}
static void testCancel(unsigned oneOrd, unsigned twoOrd, unsigned threeOrd,
unsigned oneReal, unsigned threeReal)
{
testDiag("testCancel oneOrd %u twoOrd %u threeOrd %u",
oneOrd,twoOrd,threeOrd);
Timer timer("timer" ,middlePriority);
Marker::shared_pointer marker(new Marker);
MyCallbackPtr callbackOne(new MyCallback("one"));
MyCallbackPtr callbackTwo(new MyCallback("two"));
MyCallbackPtr callbackThree(new MyCallback("three"));
for(unsigned n=0; n<ntimes; n++) {
testDiag("iteration %u", n);
testOk1(!timer.isScheduled(marker));
timer.scheduleAfterDelay(marker, 0.01);
marker->wait.wait();
// timer worker is blocked
testOk1(!timer.isScheduled(callbackOne));
testOk1(!timer.isScheduled(callbackTwo));
testOk1(!timer.isScheduled(callbackThree));
callbackOne->clear();
callbackTwo->clear();
callbackThree->clear();
timer.scheduleAfterDelay(callbackOne,delays[oneOrd]);
timer.scheduleAfterDelay(callbackTwo,delays[twoOrd]);
timer.scheduleAfterDelay(callbackThree,delays[threeOrd]);
testOk1(timer.isScheduled(callbackOne));
testOk1(timer.isScheduled(callbackTwo));
testOk1(timer.isScheduled(callbackThree));
timer.cancel(callbackTwo);
testOk1(timer.isScheduled(callbackOne));
testOk1(!timer.isScheduled(callbackTwo));
testOk1(timer.isScheduled(callbackThree));
marker->hold.signal(); // let the worker loose
callbackOne->wait.wait();
callbackThree->wait.wait();
testOk1(!timer.isScheduled(callbackOne));
testOk1(!timer.isScheduled(callbackTwo));
testOk1(!timer.isScheduled(callbackThree));
{
epicsGuard<Mutex> G(MyCallback::gbl_mutex);
testOk(callbackOne->counter==1, "%s counter %u = 1", callbackOne->name.c_str(), callbackOne->counter);
testOk(callbackTwo->counter==0, "%s counter %u = 0", callbackTwo->name.c_str(), callbackTwo->counter);
testOk(callbackThree->counter==1, "%s counter %u = 1", callbackThree->name.c_str(), callbackThree->counter);
testOk(callbackOne->first_gbl==oneReal+1, "%s first_gbl %u = %u", callbackOne->name.c_str(), callbackOne->first_gbl, oneOrd+1);
testOk(callbackTwo->first_gbl==0, "%s first_gbl %u = %u", callbackTwo->name.c_str(), callbackTwo->first_gbl, 0);
testOk(callbackThree->first_gbl==threeReal+1, "%s first_gbl %u = %u", callbackThree->name.c_str(), callbackThree->first_gbl, threeOrd+1);
}
}
}
MAIN(testTimer)
{
testPlan(315);
try {
testDiag("Tests timer");
testBasic(2, 1, 0);
testCancel(2, 1, 0, 1, 0);
testBasic(0, 1, 2);
testCancel(0, 1, 2, 0, 1);
testBasic(0, 2, 1);
testCancel(0, 2, 1, 0, 1);
}catch(std::exception& e) {
testFail("Unhandled exception: %s", e.what());
}
return testDone();
}