Skip to content

Commit

Permalink
Merge c47f95c into dd9f38d
Browse files Browse the repository at this point in the history
  • Loading branch information
mark0n committed Feb 9, 2021
2 parents dd9f38d + c47f95c commit 1488cd7
Showing 1 changed file with 155 additions and 1 deletion.
156 changes: 155 additions & 1 deletion src/libCom/test/epicsTimerTest.cpp
Expand Up @@ -15,6 +15,11 @@
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <vector>
#include <algorithm>
#include <functional>
#include <atomic>

#include "epicsTimer.h"
#include "epicsEvent.h"
Expand All @@ -39,6 +44,7 @@ class notified : public epicsTimerNotify

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

epicsTimerQueueActive *Q1, *Q2;
Expand Down Expand Up @@ -449,9 +455,157 @@ void testPeriodic ()
queue.release ();
}

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::getMonotonic();
timer.start(*this, delay);
}
void cancel() { timer.cancel(); }
expireStatus expire(const epicsTime& currentTime) {
expireTime = epicsTime::getMonotonic();
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 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");
double delta_t = h1.getDelay() - arbitrary_time;
if (!testOk(std::abs(delta_t) < 1e-3, "timer expired after the expected time (+/-1 ms)")) {
testDiag("delta t = %g", delta_t);
}
} // 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");
double delta_t = hv[i].getDelay() - sleepTime[i];
if (!testOk(std::abs(delta_t) < 1e-3, "timer expired after the expected time (+/-1 ms)")) {
testDiag("delta t = %g", delta_t);
}
}
} // 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");
double delta_t = h1.getDelay() - arbitrary_time;
if (!testOk(std::abs(delta_t) < 1e-3, "timer expired after the expected time (+/-1 ms)")) {
testDiag("delta t = %g", delta_t);
}
} // 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();
}

MAIN(epicsTimerTest)
{
testPlan(41);
testPlan(60);
testTimerExpires();
testMultipleTimersExpireFirstTimerExpiresFirst();
testMultipleTimersExpireLastTimerExpiresFirst();
testTimerReschedule();
testCancelTimer();
testCancelTimerWhileExpireIsRunning();
testRefCount();
testAccuracy ();
testCancel ();
Expand Down

0 comments on commit 1488cd7

Please sign in to comment.