/* 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 #include #include #include #include #include #include "pvType.h" #include "lock.h" #include "noDefaultMethods.h" #include "showConstructDestruct.h" #include "linkedList.h" #include "thread.h" #include "timeStamp.h" #include "timer.h" #include "event.h" namespace epics { namespace pvData { static volatile int64 totalNodeConstruct = 0; static volatile int64 totalNodeDestruct = 0; static volatile int64 totalTimerConstruct = 0; static volatile int64 totalTimerDestruct = 0; static Mutex *globalMutex = 0; static int64 getTotalTimerNodeConstruct() { Lock xx(globalMutex); return totalNodeConstruct; } static int64 getTotalTimerNodeDestruct() { Lock xx(globalMutex); return totalNodeDestruct; } static int64 getTotalTimerConstruct() { Lock xx(globalMutex); return totalTimerConstruct; } static int64 getTotalTimerDestruct() { Lock xx(globalMutex); return totalTimerDestruct; } static ConstructDestructCallback *pCDCallbackTimerNode; static ConstructDestructCallback *pCDCallbackTimer; static void init() { static Mutex mutex = Mutex(); Lock xx(&mutex); if(globalMutex==0) { globalMutex = new Mutex(); pCDCallbackTimerNode = new ConstructDestructCallback( "timerNode", getTotalTimerNodeConstruct,getTotalTimerNodeDestruct,0); pCDCallbackTimer = new ConstructDestructCallback( "timer", getTotalTimerConstruct,getTotalTimerDestruct,0); } } ConstructDestructCallback * TimerNode::getConstructDestructCallback() { init(); return pCDCallbackTimerNode; } ConstructDestructCallback * Timer::getConstructDestructCallback() { init(); return pCDCallbackTimer; } class TimerNodePvt; typedef LinkedListNode TimerListNode; typedef LinkedList TimerList; class TimerNodePvt { public: TimerNode *timerNode; TimerCallback *callback; TimerListNode *timerListNode; TimeStamp timeToRun; TimerPvt *timerPvt; double period; TimerNodePvt(TimerNode *timerNode,TimerCallback *callback); ~TimerNodePvt(); }; TimerNodePvt::TimerNodePvt(TimerNode *timerNode,TimerCallback *callback) : timerNode(timerNode),callback(callback), timerListNode(new TimerListNode(this)),timeToRun(TimeStamp()), timerPvt(0), period(0.0) {} TimerNodePvt::~TimerNodePvt() { delete timerListNode; } struct TimerPvt : public Runnable{ public: TimerPvt(String threadName,ThreadPriority priority); ~TimerPvt(); virtual void run(); public: // only used by this source module TimerList *timerList; Mutex mutex; Event *waitForWork; Event *waitForDone; volatile bool alive; Thread *thread; }; TimerPvt::TimerPvt(String threadName,ThreadPriority priority) : timerList(new TimerList()), mutex(Mutex()), waitForWork(new Event(false)), waitForDone(new Event(false)), alive(true), thread(new Thread(threadName,priority,this)) {} TimerPvt::~TimerPvt() { delete thread; delete waitForDone; delete waitForWork; delete timerList; } static void addElement(TimerPvt *timer,TimerNodePvt *node) { TimerList *timerList = timer->timerList; TimerListNode *nextNode = timerList->getHead(); if(nextNode==0) { timerList->addTail(node->timerListNode); return; } while(true) { TimerNodePvt *timerListNode = nextNode->getObject(); if((node->timeToRun)<(timerListNode->timeToRun)) { timerList->insertBefore(timerListNode->timerListNode,node->timerListNode); return; } nextNode = timerList->getNext(timerListNode->timerListNode); if(nextNode==0) { timerList->addTail(node->timerListNode); return; } } } TimerNode::TimerNode(TimerCallback *callback) : pImpl(new TimerNodePvt(this,callback)) { init(); Lock xx(globalMutex); totalNodeConstruct++; } TimerNode::~TimerNode() { cancel(); delete pImpl; Lock xx(globalMutex); totalNodeDestruct++; } void TimerNode::cancel() { TimerPvt *timerPvt = pImpl->timerPvt; if(timerPvt==0) return; Lock xx(&timerPvt->mutex); if(pImpl->timerPvt==0) return; pImpl->timerPvt->timerList->remove(pImpl); pImpl->timerPvt = 0; } bool TimerNode::isScheduled() { TimerPvt *pvt = pImpl->timerPvt; if(pvt==0) return false; Lock xx(&pvt->mutex); return pImpl->timerListNode->isOnList(); } void TimerPvt::run() { TimeStamp currentTime; while(alive) { currentTime.getCurrent(); TimeStamp *timeToRun = 0; double period = 0.0; TimerNodePvt *nodeToCall = 0; { Lock xx(&mutex); TimerListNode *timerListNode = timerList->getHead(); if(timerListNode!=0) { TimerNodePvt *timerNodePvt = timerListNode->getObject(); timeToRun = &timerNodePvt->timeToRun; double diff = TimeStamp::diff( *timeToRun,currentTime); if(diff<=0.0) { nodeToCall = timerNodePvt; timerList->removeHead(); period = timerNodePvt->period; if(period>0.0) { timerNodePvt->timeToRun += period; addElement(this,timerNodePvt); } else { timerNodePvt->timerPvt = 0; } timerListNode = timerList->getHead(); if(timerListNode!=0) { timerNodePvt = timerListNode->getObject(); timeToRun = &timerNodePvt->timeToRun; } else { timeToRun = 0; } } } } if(nodeToCall!=0) { nodeToCall->callback->callback(); } if(!alive) break; if(timeToRun==0) { waitForWork->wait(); } else { double delay = TimeStamp::diff(*timeToRun,currentTime); waitForWork->wait(delay); } } waitForDone->signal(); } Timer::Timer(String threadName, ThreadPriority priority) : pImpl(new TimerPvt(threadName,priority)) { init(); Lock xx(globalMutex); totalTimerConstruct++; } Timer::~Timer() { { Lock xx(&pImpl->mutex); pImpl->alive = false; } pImpl->waitForWork->signal(); pImpl->waitForDone->wait(); TimerList *timerList = pImpl->timerList; TimerListNode *node = 0; while((node = timerList->removeHead())!=0) { node->getObject()->callback->timerStopped(); } delete pImpl; Lock xx(globalMutex); totalTimerDestruct++; } void Timer::scheduleAfterDelay(TimerNode *timerNode,double delay) { schedulePeriodic(timerNode,delay,0.0); } void Timer::schedulePeriodic(TimerNode *timerNode,double delay,double period) { TimerNodePvt *timerNodePvt = timerNode->pImpl; if(timerNodePvt->timerListNode->isOnList()) { throw std::logic_error(String("already queued")); } if(!pImpl->alive) { timerNodePvt->callback->timerStopped(); return; } TimeStamp *timeStamp = &timerNodePvt->timeToRun; timeStamp->getCurrent(); *timeStamp += delay; timerNodePvt->period = period; bool isFirst = false; { Lock xx(&pImpl->mutex); timerNodePvt->timerPvt = pImpl; addElement(pImpl,timerNodePvt); TimerNodePvt *first = pImpl->timerList->getHead()->getObject(); if(first==timerNodePvt) isFirst = true; } if(isFirst) pImpl->waitForWork->signal(); } }}