This is Unofficial EPICS BASE Doxygen Site
timer.cpp
Go to the documentation of this file.
1 /* timer.cpp */
2 /*
3  * Copyright information and license terms for this software can be
4  * found in the file LICENSE that is included with the distribution
5  */
10 #include <stdexcept>
11 #include <string>
12 #include <iostream>
13 
14 #include <epicsThread.h>
15 #include <epicsGuard.h>
16 
17 #define epicsExportSharedSymbols
18 #include <pv/timer.h>
19 
20 using std::string;
21 
22 namespace epics { namespace pvData {
23 
25 : period(0.0),
26  onList(false)
27 {
28 }
29 
30 Timer::Timer(string threadName,ThreadPriority priority)
32  ,waiting(false)
33  ,alive(true)
34  ,thread(threadName,priority,this)
35 {}
36 
38  bool operator()(const TimerCallbackPtr& lhs, const TimerCallbackPtr& rhs) {
39  assert(lhs && rhs);
40  return lhs->timeToRun < rhs->timeToRun;
41  }
42 };
43 
44 // call with mutex held
45 void Timer::addElement(TimerCallbackPtr const & timerCallback)
46 {
47  assert(!timerCallback->onList);
48 
49  queue_t temp;
50  temp.push_back(timerCallback);
51 
52  timerCallback->onList = true;
53 
54  // merge sorted lists.
55  // for us effectively insertion sort.
56  queue.merge(temp, TimerCallback::IncreasingTime());
57 }
58 
59 
60 bool Timer::cancel(TimerCallbackPtr const &timerCallback)
61 {
62  Lock xx(mutex);
63  if(!timerCallback->onList) return false;
64  if(!alive) {
65  timerCallback->onList = false;
66  return true;
67  }
68  for(queue_t::iterator it(queue.begin()), end(queue.end()); it != end; ++it)
69  {
70  TimerCallbackPtr& cur = *it;
71  if(cur.get() == timerCallback.get()) {
72  cur->onList = false;
73  queue.erase(it); // invalidates cur and it
74  return true;
75  }
76  }
77  throw std::logic_error("Timer::cancel() onList==true, but not found");
78 }
79 
80 bool Timer::isScheduled(TimerCallbackPtr const &timerCallback) const
81 {
82  Lock xx(mutex);
83  return timerCallback->onList;
84 }
85 
86 
87 void Timer::run()
88 {
90 
91  epicsTime now(epicsTime::getCurrent());
92 
93  while(alive) {
94  double waitfor;
95 
96  if(queue.empty()) {
97  // no jobs, just go to sleep
98  waiting = true;
100 
101  waitForWork.wait();
102  now = epicsTime::getCurrent();
103 
104  } else if((waitfor = queue.front()->timeToRun - now) <= 0) {
105  // execute first expired job
106 
107  TimerCallbackPtr work;
108  work.swap(queue.front());
109  work->onList = false;
110  queue.pop_front();
111 
112  {
114 
115  work->callback();
116  }
117 
118  if(work->period > 0.0 && alive) {
119  work->timeToRun += work->period;
120  addElement(work);
121  }
122 
123  // don't update 'now' until all expired jobs run
124 
125  } else {
126  waiting = true;
127  // wait for first un-expired
129 
130  waitForWork.wait(waitfor);
131  now = epicsTime::getCurrent();
132  }
133  waiting = false;
134  }
135 }
136 
138  close();
139 }
140 
141 void Timer::close() {
142  {
143  Lock xx(mutex);
144  if(!alive)
145  return; // already closed
146  alive = false;
147  }
148  waitForWork.signal();
149  thread.exitWait();
150 
151  queue_t temp;
152  temp.swap(queue);
153 
154  for(;!temp.empty(); temp.pop_front()) {
155  TimerCallbackPtr& head = temp.front();
156  head->onList = false;
157  head->timerStopped();
158  }
159 }
160 
162  TimerCallbackPtr const &timerCallback,
163  double delay)
164 {
165  schedulePeriodic(timerCallback,delay,0.0);
166 }
167 
169  TimerCallbackPtr const &timerCallback,
170  double delay,
171  double period)
172 {
173  epicsTime now(epicsTime::getCurrent());
174 
175  bool wakeup;
176  {
177  Lock xx(mutex);
178  if(timerCallback->onList) {
179  throw std::logic_error(string("already queued"));
180  }
181 
182  if(!alive) {
183  xx.unlock();
184  timerCallback->timerStopped();
185  return;
186  }
187 
188  timerCallback->timeToRun = now + delay;
189  timerCallback->period = period;
190 
191  addElement(timerCallback);
192  wakeup = waiting && queue.front()==timerCallback;
193  }
194  if(wakeup) waitForWork.signal();
195 }
196 
197 void Timer::dump(std::ostream& o) const
198 {
199  Lock xx(mutex);
200  if(!alive) return;
201  epicsTime now(epicsTime::getCurrent());
202 
203  for(queue_t::const_iterator it(queue.begin()), end(queue.end()); it!=end; ++it) {
204  const TimerCallbackPtr& nodeToCall = *it;
205  o << "timeToRun " << (nodeToCall->timeToRun - now)
206  << " period " << nodeToCall->period << "\n";
207  }
208 }
209 
210 std::ostream& operator<<(std::ostream& o, const Timer& timer)
211 {
212  timer.dump(o);
213  return o;
214 }
215 
216 }}
void close()
Prevent new callbacks from being scheduled, and cancel pending callbacks.
Definition: timer.cpp:141
void schedulePeriodic(TimerCallbackPtr const &timerCallback, double delay, double period)
Definition: timer.cpp:168
#define assert(exp)
Declare that a condition should be true.
Definition: epicsAssert.h:70
bool cancel(TimerCallbackPtr const &timerCallback)
Definition: timer.cpp:60
void dump(std::ostream &o) const
Definition: timer.cpp:197
#define true
Definition: flexdef.h:84
virtual ~Timer()
Definition: timer.cpp:137
TODO only here because of the Lockable.
Definition: ntaggregate.cpp:16
A lock for multithreading.
Definition: lock.h:36
void unlock()
Definition: lock.h:66
bool isScheduled(TimerCallbackPtr const &timerCallback) const
Definition: timer.cpp:80
std::ostream & operator<<(std::ostream &o, const Field &f)
bool operator()(const TimerCallbackPtr &lhs, const TimerCallbackPtr &rhs)
Definition: timer.cpp:38
Support for delayed or periodic callback execution.
Definition: timer.h:71
epicsMutex mutex
Definition: pvAccess.cpp:71
std::tr1::shared_ptr< TimerCallback > TimerCallbackPtr
Definition: timer.h:32
void scheduleAfterDelay(TimerCallbackPtr const &timerCallback, double delay)
Definition: timer.cpp:161
epicsEventId waitForWork
Definition: errlog.c:66
C++ and C descriptions for a thread.
#define false
Definition: flexdef.h:85
Timer(std::string threadName, ThreadPriority priority)
Definition: timer.cpp:30