Skip to content

Commit

Permalink
Merge e8c2fa0 into dd9f38d
Browse files Browse the repository at this point in the history
  • Loading branch information
mark0n committed Feb 11, 2021
2 parents dd9f38d + e8c2fa0 commit 077b394
Showing 1 changed file with 162 additions and 1 deletion.
163 changes: 162 additions & 1 deletion src/libCom/test/epicsTimerTest.cpp
@@ -1,4 +1,5 @@
/*************************************************************************\
* Copyright (c) 2021 Facility for Rare Isotope Beams.
* Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne
* National Laboratory.
* Copyright (c) 2002 The Regents of the University of California, as
Expand All @@ -15,6 +16,13 @@
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#if __cplusplus >= 201103L
#include <string>
#include <vector>
#include <algorithm>
#include <functional>
#include <atomic>
#endif

#include "epicsTimer.h"
#include "epicsEvent.h"
Expand All @@ -27,6 +35,148 @@
#define verify(exp) ((exp) ? (void)0 : \
epicsAssert(__FILE__, __LINE__, #exp, epicsAssertAuthor))

#if __cplusplus >= 201103L
class handler : public epicsTimerNotify
{
public:
handler(const char* nameIn, epicsTimerQueue& queue, std::function<void()> expireFctIn = [] {}) : name(nameIn), timer(queue.createTimer()), expireFct(expireFctIn) {}
~handler() { timer.destroy(); }
void start(double delay) {
startTime = epicsTime::getCurrent();
timer.start(*this, delay);
}
void cancel() { timer.cancel(); }
expireStatus expire(const epicsTime& currentTime) {
expireTime = epicsTime::getCurrent();
expireCount++;
expireFct();
return expireStatus(noRestart);
}
int getExpireCount() const { return expireCount; }
double getDelay() const { return expireTime - startTime; }
private:
const std::string name;
epicsTimer &timer;
int expireCount = 0;
epicsTime startTime;
epicsTime expireTime;
std::function<void()> expireFct;
};

void verifyExpirationTime(double delta_t) {
if (!testOk(std::abs(delta_t) < 1e-3, "timer expired after the expected time (+/-1 ms)")) {
const char * msg = delta_t < 0.0 ? "early" : "late";
testDiag("delta t = %g ms (timer expired too %s)", 1000 * delta_t, msg);
}
}

void testTimerExpires() {
testDiag("Timer expires");
epicsTimerQueueActive &queue = epicsTimerQueueActive::allocate(false);
{
handler h1("timer1", queue);
const double arbitrary_time { 0.3 };
h1.start(arbitrary_time);

epicsThreadSleep(arbitrary_time + 0.1);

testOk(h1.getExpireCount() == 1, "timer expired exactly once");
verifyExpirationTime(h1.getDelay() - arbitrary_time);
} // destroy timer
queue.release();
}

void testMultipleTimersExpire(std::vector<double> && sleepTime) {
epicsTimerQueueActive &queue = epicsTimerQueueActive::allocate(false);
{
std::vector<handler> hv;
hv.reserve(3);
for (unsigned i = 0; i < 3; i++) {
std::string name("timer" + std::to_string(i));
hv.emplace_back(name.c_str(), queue);
}
for (unsigned i = 0; i < hv.size(); i++) {
hv[i].start(sleepTime[i]);
}

epicsThreadSleep(*std::max_element(sleepTime.cbegin(), sleepTime.cend()) + 0.1);

for (unsigned i = 0; i < hv.size(); i++) {
testOk(hv[i].getExpireCount() == 1, "timer expired exactly once");
verifyExpirationTime(hv[i].getDelay() - sleepTime[i]);
}
} // destroy timers
queue.release();
}

void testMultipleTimersExpireFirstTimerExpiresFirst() {
testDiag("Multiple timers expire - first timer expires first");
testMultipleTimersExpire({ 1.0, 1.2, 1.1 });
}

void testMultipleTimersExpireLastTimerExpiresFirst() {
testDiag("Multiple timers expire - last timer expires first");
testMultipleTimersExpire({ 1.1, 1.2, 1.0 });
}

void testTimerReschedule() {
testDiag("Reschedule timer");
epicsTimerQueueActive &queue = epicsTimerQueueActive::allocate(false);
{
handler h1("timer1", queue);
double arbitrary_time { 10.0 };
h1.start(arbitrary_time);
arbitrary_time = 0.3;
h1.start(arbitrary_time);

epicsThreadSleep(arbitrary_time + 0.1);

testOk(h1.getExpireCount() == 1, "timer expired exactly once");
verifyExpirationTime(h1.getDelay() - arbitrary_time);
} // destroy timer
queue.release();
}

void testCancelTimer() {
testDiag("Cancel timer");
epicsTimerQueueActive &queue = epicsTimerQueueActive::allocate(false);
{
handler h1("timer1", queue);
h1.start(0.1);
h1.cancel();

epicsThreadSleep(0.2);

testOk(h1.getExpireCount() == 0, "timer expired exactly 0 times");
} // destroy timer
queue.release();
}

void testCancelTimerWhileExpireIsRunning() {
testDiag("Cancel timer while expire handler is running");
epicsTimerQueueActive &queue = epicsTimerQueueActive::allocate(false);
{
epicsEvent expireStarted;
std::atomic_flag endOfExpireReached = ATOMIC_FLAG_INIT;
auto longRunningFct = [&] {
expireStarted.trigger();
epicsThreadSleep(0.1);
endOfExpireReached.test_and_set();
};
handler h1("timer1", queue, longRunningFct );
h1.start(0.0);
expireStarted.wait(); // wait for expire() to be called

h1.cancel(); // cancel while expire() is running
bool cancelBlocked = endOfExpireReached.test_and_set();

testOk(h1.getExpireCount() == 1, "timer expired exactly once");
testOk(cancelBlocked, "cancel() blocks until all expire() calls are finished");
} // destroy timer
queue.release();
}
#endif

class notified : public epicsTimerNotify
{
public:
Expand All @@ -39,6 +189,7 @@ class notified : public epicsTimerNotify

void testRefCount()
{
testDiag("Reference counting");
notified action;

epicsTimerQueueActive *Q1, *Q2;
Expand Down Expand Up @@ -451,7 +602,17 @@ void testPeriodic ()

MAIN(epicsTimerTest)
{
testPlan(41);
testPlan(60);
#if __cplusplus >= 201103L
testTimerExpires();
testMultipleTimersExpireFirstTimerExpiresFirst();
testMultipleTimersExpireLastTimerExpiresFirst();
testTimerReschedule();
testCancelTimer();
testCancelTimerWhileExpireIsRunning();
#else
testSkip(19, "Test requires a compiler which supports C++11");
#endif
testRefCount();
testAccuracy ();
testCancel ();
Expand Down

0 comments on commit 077b394

Please sign in to comment.