212 lines
5.3 KiB
C++
212 lines
5.3 KiB
C++
/* timer.cpp */
|
|
/**
|
|
* Copyright - See the COPYRIGHT that is included with this distribution.
|
|
* EPICS pvDataCPP is distributed subject to a Software License Agreement found
|
|
* in file LICENSE that is included with this distribution.
|
|
*/
|
|
#include <stddef.h>
|
|
#include <stdlib.h>
|
|
#include <stddef.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <stdexcept>
|
|
|
|
#include <pv/timer.h>
|
|
#include <pv/convert.h>
|
|
|
|
namespace epics { namespace pvData {
|
|
|
|
TimerCallback::TimerCallback()
|
|
: period(0.0),
|
|
onList(false)
|
|
{
|
|
}
|
|
|
|
Timer::Timer(String threadName,ThreadPriority priority)
|
|
: waitForWork(false),
|
|
waitForDone(false),
|
|
alive(true),
|
|
thread(threadName,priority,this)
|
|
{}
|
|
|
|
void Timer::addElement(TimerCallbackPtr const & timerCallback)
|
|
{
|
|
timerCallback->onList = true;
|
|
if(head.get()==NULL) {
|
|
head = timerCallback;
|
|
timerCallback->next.reset();
|
|
return;
|
|
}
|
|
TimerCallbackPtr nextNode(head);
|
|
TimerCallbackPtr prevNode;
|
|
while(true) {
|
|
if(timerCallback->timeToRun < nextNode->timeToRun) {
|
|
if(prevNode.get()!=NULL) {
|
|
prevNode->next = timerCallback;
|
|
} else {
|
|
head = timerCallback;
|
|
}
|
|
timerCallback->next = nextNode;
|
|
return;
|
|
}
|
|
if(nextNode->next.get()==NULL) {
|
|
nextNode->next = timerCallback;
|
|
timerCallback->next.reset();
|
|
return;
|
|
}
|
|
prevNode = nextNode;
|
|
nextNode = nextNode->next;
|
|
}
|
|
}
|
|
|
|
|
|
void Timer::cancel(TimerCallbackPtr const &timerCallback)
|
|
{
|
|
Lock xx(mutex);
|
|
if(!timerCallback->onList) return;
|
|
TimerCallbackPtr nextNode(head);
|
|
TimerCallbackPtr prevNode;
|
|
while(true) {
|
|
if(nextNode.get()==timerCallback.get()) {
|
|
if(prevNode.get()!=NULL) {
|
|
prevNode->next = timerCallback->next;
|
|
} else {
|
|
head = timerCallback->next;
|
|
}
|
|
timerCallback->next.reset();
|
|
timerCallback->onList = false;
|
|
return;
|
|
}
|
|
prevNode = nextNode;
|
|
nextNode = nextNode->next;
|
|
}
|
|
throw std::logic_error(String(""));
|
|
}
|
|
|
|
bool Timer::isScheduled(TimerCallbackPtr const &timerCallback)
|
|
{
|
|
Lock xx(mutex);
|
|
return timerCallback->onList;
|
|
}
|
|
|
|
|
|
void Timer::run()
|
|
{
|
|
TimeStamp currentTime;
|
|
while(true) {
|
|
double period = 0.0;
|
|
TimerCallbackPtr nodeToCall;
|
|
{
|
|
Lock xx(mutex);
|
|
currentTime.getCurrent();
|
|
if (!alive) break;
|
|
TimerCallbackPtr timerCallback = head;
|
|
if(timerCallback.get()!=NULL) {
|
|
double diff = TimeStamp::diff(
|
|
timerCallback->timeToRun,currentTime);
|
|
if(diff<=0.0) {
|
|
nodeToCall = timerCallback;
|
|
nodeToCall->onList = false;
|
|
head = head->next;
|
|
period = timerCallback->period;
|
|
if(period>0.0) {
|
|
timerCallback->timeToRun += period;
|
|
addElement(timerCallback);
|
|
}
|
|
timerCallback = head;
|
|
}
|
|
}
|
|
}
|
|
if(nodeToCall.get()!=NULL) {
|
|
nodeToCall->callback();
|
|
}
|
|
{
|
|
Lock xx(mutex);
|
|
if(!alive) break;
|
|
}
|
|
if(head.get()==NULL) {
|
|
waitForWork.wait();
|
|
} else {
|
|
double delay = TimeStamp::diff(head->timeToRun,currentTime);
|
|
waitForWork.wait(delay);
|
|
}
|
|
}
|
|
waitForDone.signal();
|
|
}
|
|
|
|
Timer::~Timer() {
|
|
{
|
|
Lock xx(mutex);
|
|
alive = false;
|
|
}
|
|
waitForWork.signal();
|
|
waitForDone.wait();
|
|
TimerCallbackPtr timerCallback;
|
|
while(true) {
|
|
timerCallback = head;
|
|
if(head.get()==NULL) break;
|
|
head->timerStopped();
|
|
head = timerCallback->next;
|
|
timerCallback->next.reset();
|
|
timerCallback->onList = false;
|
|
}
|
|
}
|
|
|
|
void Timer::scheduleAfterDelay(
|
|
TimerCallbackPtr const &timerCallback,
|
|
double delay)
|
|
{
|
|
schedulePeriodic(timerCallback,delay,0.0);
|
|
}
|
|
|
|
void Timer::schedulePeriodic(
|
|
TimerCallbackPtr const &timerCallback,
|
|
double delay,
|
|
double period)
|
|
{
|
|
if(isScheduled(timerCallback)) {
|
|
throw std::logic_error(String("already queued"));
|
|
}
|
|
{
|
|
Lock xx(mutex);
|
|
if(!alive) {
|
|
timerCallback->timerStopped();
|
|
return;
|
|
}
|
|
}
|
|
TimeStamp timeStamp;
|
|
timeStamp.getCurrent();
|
|
timeStamp += delay;
|
|
timerCallback->timeToRun.getCurrent();
|
|
timerCallback->timeToRun += delay;
|
|
timerCallback->period = period;
|
|
bool isFirst = false;
|
|
{
|
|
Lock xx(mutex);
|
|
addElement(timerCallback);
|
|
if(timerCallback.get()==head.get()) isFirst = true;
|
|
}
|
|
if(isFirst) waitForWork.signal();
|
|
}
|
|
|
|
void Timer::toString(StringBuilder builder)
|
|
{
|
|
Lock xx(mutex);
|
|
if(!alive) return;
|
|
TimeStamp currentTime;
|
|
TimerCallbackPtr nodeToCall(head);
|
|
currentTime.getCurrent();
|
|
while(true) {
|
|
if(nodeToCall.get()==NULL) return;
|
|
TimeStamp timeToRun = nodeToCall->timeToRun;
|
|
double period = nodeToCall->period;
|
|
double diff = TimeStamp::diff(timeToRun,currentTime);
|
|
char buffer[50];
|
|
sprintf(buffer,"timeToRun %f period %f\n",diff,period);
|
|
*builder += buffer;
|
|
nodeToCall = nodeToCall->next;
|
|
}
|
|
}
|
|
|
|
}}
|