artdaq  v3_07_01
Timeout.cc
1 // This file (Timer.cxx) was created by Ron Rechenmacher <ron@fnal.gov> on
2 // Sep 28, 2009. "TERMS AND CONDITIONS" governing this file are in the README
3 // or COPYING file. If you do not have such a file, one can be obtained by
4 // contacting Ron or Fermi Lab in Batavia IL, 60510, phone: 630-840-3000.
5 // $RCSfile: Timeout.cxx,v $
6 // rev="$Revision: 1.13 $$Date: 2016/10/12 21:00:13 $";
7 /*
8  g++ -Wall -g -std=c++0x -c Timeout.cxx
9 OR
10  g++ -Wall -g -std=c++0x -shared -fPIC -o Timeout.so Timeout.cxx -lrt
11 */
12 
13 #define TRACE_NAME "Timeout"
14 
15 #include <assert.h> /* assert */
16 #include <stdio.h> // printf
17 #include <stdlib.h> // exit
18 #include <string.h> /* strcmp */
19 #include <sys/time.h> /* struct timeval */
20 #include <list>
21 using std::list;
22 #include "artdaq-core/Utilities/TimeUtils.hh"
23 #include "artdaq/DAQdata/Globals.hh" // TRACE
24 #include "artdaq/TransferPlugins/detail/Timeout.hh"
25 
26 // public:
27 
28 #if 0
29 Timeout::timeoutspec::timeoutspec()
30  : desc(), tag(), function()
31  , ts(), period()
32  , missed_periods(), check()
33 {
34  TLOG(18) << "Timeout::timeoutspec ctor this=" << this;
35 }
36 Timeout::timeoutspec::timeoutspec(const timeoutspec & other)
37  : desc(other.desc), tag(other.tag), function(other.function)
38  , ts(other.ts), period(other.period)
39  , missed_periods(other.missed_periods), check(other.check)
40 {
41  TLOG(18) << "Timeout::timeoutspec copy ctor";
42 }
43 Timeout::timeoutspec & Timeout::timeoutspec::operator=(const Timeout::timeoutspec & other)
44 {
45  TLOG(18) << "Timeout::timeoutspec copy assignment (operator= other.desc=" << other.desc << ")";
46  desc = other.desc;
47  tag = other.tag;
48  function = other.function;
49  ts = other.ts;
50  period = other.period;
52  check = other.check;
53  return *this;
54 }
55 #endif
56 
57 Timeout::Timeout(int max_tmos)
58  : tmospecs_(max_tmos)
59 {
60  TLOG(16) << "Timeout ctor";
61  timeoutlist_init();
62 }
63 
64 void Timeout::add_periodic(const char* desc, void* tag, std::function<void()>& function, uint64_t period_us, uint64_t start_us)
65 {
66  timeoutspec tmo;
67  tmo.desc = desc;
68  tmo.tag = tag;
69  tmo.function = function;
70  tmo.tmo_tod_us = start_us ? start_us : artdaq::TimeUtils::gettimeofday_us() + period_us;
71  tmo.period_us = period_us;
72  tmo.check = tmo.missed_periods = 0;
73  copy_in_timeout(tmo);
74 } // add_periodic
75 
76 void Timeout::add_periodic(const char* desc, void* tag, std::function<void()>& function, int rel_ms)
77 {
78  timeoutspec tmo;
79  tmo.desc = desc;
80  tmo.tag = tag;
81  tmo.function = function;
82  tmo.period_us = rel_ms * 1000;
83  tmo.tmo_tod_us = artdaq::TimeUtils::gettimeofday_us() + tmo.period_us;
84  tmo.check = tmo.missed_periods = 0;
85  copy_in_timeout(tmo);
86 } // add_periodic
87 
88 void Timeout::add_periodic(const char* desc, uint64_t period_us, uint64_t start_us)
89 {
90  TLOG(19) << "add_periodic - desc=" << desc << " period_us=" << period_us << " start_us=" << start_us;
91  timeoutspec tmo;
92  tmo.desc = desc;
93  tmo.tag = 0;
94  tmo.function = 0;
95  tmo.tmo_tod_us = start_us;
96  tmo.period_us = period_us;
97  tmo.missed_periods = tmo.check = 0;
98  copy_in_timeout(tmo);
99 } // add_periodic
100 
101 void Timeout::add_relative(const char* desc, void* tag, std::function<void()>& function, int rel_ms)
102 {
103  timeoutspec tmo;
104  tmo.desc = desc;
105  tmo.tag = tag;
106  tmo.function = function;
107  tmo.tmo_tod_us = artdaq::TimeUtils::gettimeofday_us() + rel_ms * 1000;
108  tmo.period_us = 0;
109  tmo.missed_periods = tmo.check = 0;
110  copy_in_timeout(tmo);
111 } // add_periodic
112 
113 void Timeout::add_relative(std::string desc, int rel_ms)
114 {
115  timeoutspec tmo;
116  tmo.desc = desc.c_str();
117  tmo.tag = 0;
118  tmo.function = 0;
119  tmo.period_us = 0;
120  tmo.tmo_tod_us = artdaq::TimeUtils::gettimeofday_us() + rel_ms * 1000;
121  tmo.missed_periods = tmo.check = 0;
122  copy_in_timeout(tmo);
123 } // add_periodic
124 
125 int // tmo_tod_us is an output
126 Timeout::get_next_expired_timeout(std::string& desc, void** tag, std::function<void()>& function, uint64_t* tmo_tod_us)
127 {
128  int skipped = 0;
129  timeoutspec tmo;
130  TLOG(15) << "get_next_expired_timeout b4 get_clear_next_expired_timeout";
131  skipped = get_clear_next_expired_timeout(tmo, artdaq::TimeUtils::gettimeofday_us());
132  if (skipped == -1)
133  {
134  TLOG(18) << "get_next_expired_timeout - get_clear_next_expired_timeout returned false";
135  desc = std::string(""); // 2 ways to check for none timed out
136  }
137  else
138  {
139  desc = tmo.desc;
140  *tag = tmo.tag;
141  function = tmo.function;
142  *tmo_tod_us = tmo.tmo_tod_us;
143  }
144  return (skipped);
145 } // get_next_expired_timeout
146 
147 void Timeout::get_next_timeout_delay(int64_t* delay_us)
148 {
149  std::unique_lock<std::mutex> ulock(lock_mutex_);
150  size_t active_time_size = active_time_.size();
151  if (active_time_size == 0)
152  {
153  TLOG(17) << "get_next_timeout_delay active_.size() == 0";
154  *delay_us = -1; // usually means a very very long time
155  }
156  else
157  {
158  TLOG(17) << "get_next_timeout_delay active_.size() != 0: " << active_time_size;
159  uint64_t tod_us = artdaq::TimeUtils::gettimeofday_us();
160  timeoutspec* tmo = &tmospecs_[(*(active_time_.begin())).second];
161  *delay_us = tmo->tmo_tod_us - tod_us;
162  if (*delay_us < 0)
163  *delay_us = 0;
164  }
165 } // get_next_timeout_delay
166 
168 {
169  int64_t delay_us;
170  int tmo;
171  get_next_timeout_delay(&delay_us);
172  if (delay_us == -1)
173  {
174  tmo = -1;
175  }
176  else
177  { // NOTE THE + 1 b/c of integer division and/or system HZ resolution
178  tmo = delay_us / 1000;
179  }
180  return (tmo);
181 } // get_next_timeout_msdly
182 
184 {
185  std::map<uint64_t, size_t>::iterator itactive;
186  std::list<size_t>::iterator itfree;
187  for (unsigned ii = 0; ii < tmospecs_.size(); ++ii)
188  tmospecs_[ii].check = 1;
189  for (itactive = active_time_.begin(); itactive != active_time_.end(); ++itactive)
190  tmospecs_[(*itactive).second].check--;
191  for (itfree = free_.begin(); itfree != free_.end(); ++itfree)
192  tmospecs_[*itfree].check--;
193  for (unsigned ii = 0; ii < tmospecs_.size(); ++ii)
194  if (tmospecs_[ii].check != 0) return false;
195  return (true);
196 }
197 
198 void Timeout::timeoutlist_init()
199 {
200  size_t list_sz = tmospecs_.size();
201  for (size_t ii = 0; ii < list_sz; ++ii)
202  { //bzero( &tmospecs_[list_sz], sizeof(tmospecs_[0]) );
203  free_.push_front(ii);
204  }
205 }
206 
207 int Timeout::get_clear_next_expired_timeout(timeoutspec& tmo, uint64_t tod_now_us)
208 {
209  int skipped = 0;
210  if (active_time_.size() == 0)
211  {
212  TLOG(17) << "get_clear_next_expired_timeout - nothing to get/clear!";
213  return (false);
214  }
215  else
216  {
217  std::unique_lock<std::mutex> ulock(lock_mutex_);
218  std::multimap<uint64_t, size_t>::iterator itfront = active_time_.begin();
219  size_t idx = (*itfront).second;
220  if (tmospecs_[idx].tmo_tod_us < tod_now_us)
221  {
222  tmo = tmospecs_[idx];
223  TLOG(17) << "get_clear_next_expired_timeout - clearing tag=" << tmo.tag << " desc=" << tmo.desc << " period=" << tmo.period_us << " idx=" << idx;
224 
225  active_time_.erase(itfront);
226  // now, be effecient -- if periodic, add back at new time, else
227  // find/erase active_desc_ with same idx and free
228  if (tmo.period_us)
229  {
230  // PERIODIC
231  int64_t delta_us;
232  uint64_t period_us = tmo.period_us;
233  delta_us = tod_now_us - tmo.tmo_tod_us;
234  skipped = delta_us / period_us;
235  assert(skipped >= 0);
236  tmo.missed_periods += skipped;
237 
238  /* now fast forward over skipped */
239  period_us += period_us * skipped;
240  tmospecs_[idx].tmo_tod_us += period_us;
241  active_time_.insert(std::pair<uint64_t, size_t>(tmospecs_[idx].tmo_tod_us, idx));
242  TLOG(18) << "get_clear_next_expired_timeout - periodic timeout desc=" << tmo.desc
243  << " period_us=" << period_us << " delta_us=" << delta_us
244  << " skipped=" << skipped << " next tmo at:" << tmospecs_[idx].tmo_tod_us;
245  }
246  else
247  {
248  // find active_desc_ with same idx
249  std::unordered_multimap<std::string, size_t>::iterator i2;
250  i2 = active_desc_.equal_range(tmospecs_[idx].desc).first;
251  while (1)
252  { // see also in cancel_timeout below
253  if (i2->second == idx)
254  break;
255  ++i2;
256  }
257  active_desc_.erase(i2);
258  free_.push_front(idx);
259  }
260  }
261  else
262  {
263  TLOG(17) << "get_clear_next_expired_timeout - front " << tmospecs_[idx].tmo_tod_us << " NOT before ts_now " << tod_now_us << " - not clearing!";
264  return (-1);
265  }
266  }
267  return true;
268 } // get_clear_next_expired_timeout
269 
270 // this doesn't do anything (function undefined)
271 void Timeout::copy_in_timeout(const char* desc, uint64_t period_us, uint64_t start_us)
272 {
273  TLOG(18) << "copy_in_timeout desc=" + std::string(desc);
274  timeoutspec tos;
275  tos.desc = desc;
276  tos.tag = NULL;
277  tos.function = 0;
278  tos.period_us = period_us;
279  tos.tmo_tod_us = start_us;
280  tos.missed_periods = tos.check = 0;
281  copy_in_timeout(tos);
282 }
283 
284 void Timeout::copy_in_timeout(timeoutspec& tmo)
285 {
286  // check for at least one empty entry
287  assert(free_.size());
288 
289  // get/fill-in free tmospec
290  std::unique_lock<std::mutex> ulock(lock_mutex_);
291  size_t idx = free_.front();
292  free_.pop_front();
293  tmospecs_[idx] = tmo;
294  TLOG(20) << "copy_in_timeout timeoutspec desc=" + tmo.desc;
295  active_time_.insert(std::pair<uint64_t, size_t>(tmo.tmo_tod_us, idx));
296  active_desc_.insert(std::pair<std::string, size_t>(tmo.desc, idx));
297 }
298 
299 bool Timeout::cancel_timeout(void* tag, std::string desc)
300 {
301  bool retsts = false;
302  std::unordered_multimap<std::string, size_t>::iterator ii, ee;
303  std::unique_lock<std::mutex> ulock(lock_mutex_);
304  auto pairOfIters = active_desc_.equal_range(desc);
305  ii = pairOfIters.first;
306  ee = pairOfIters.second;
307  for (; ii != ee && ii->first.compare(desc) == 0; ++ii)
308  {
309  size_t idx = ii->second;
310  if (tmospecs_[idx].tag == tag)
311  {
312  // found a match
313  retsts = true;
314  uint64_t tmo_tod_us = tmospecs_[idx].tmo_tod_us;
315 
316  // now make sure to find the active_time_ with the same idx
317  std::multimap<uint64_t, size_t>::iterator i2;
318  i2 = active_time_.equal_range(tmo_tod_us).first;
319  while (1)
320  { // see also in get_clear_next_expired_timeout above
321  if (i2->second == idx)
322  break;
323  ++i2;
324  }
325 
326  active_desc_.erase(ii);
327  active_time_.erase(i2);
328  free_.push_front(idx);
329  break;
330  }
331  }
332  TLOG(22) << "cancel_timeout returning " << retsts;
333  return retsts;
334 } // cancel_timeout
335 
337 {
338  std::map<uint64_t, size_t>::iterator ii = active_time_.begin(), ee = active_time_.end();
339  for (; ii != ee; ++ii)
340  {
341  TLOG(TLVL_DEBUG) << "list_active_time " << (*ii).first << " desc=" << tmospecs_[(*ii).second].desc;
342  }
343 }
void copy_in_timeout(const char *desc, uint64_t period_us, uint64_t start_us=0)
Add a timeout with the given parameters.
Definition: Timeout.cc:271
void add_relative(const char *desc, void *tag, std::function< void()> &function, int rel_ms)
Add a periodic timeout to the Timeout container.
Definition: Timeout.cc:101
void * tag
could be file descriptor (fd)
Definition: Timeout.hh:36
std::function< void()> function
Function to execute at Timeout.
Definition: Timeout.hh:37
uint64_t tmo_tod_us
When the function should be executed (gettimeofday, microseconds)
Definition: Timeout.hh:38
int get_next_timeout_msdly()
Get the amount to wait for the next timeout to occur.
Definition: Timeout.cc:167
int check
Check the timeoutspec.
Definition: Timeout.hh:41
int missed_periods
Number of periods that passed while the function was executing.
Definition: Timeout.hh:40
void get_next_timeout_delay(int64_t *delay_us)
Get the amount to wait for the next timeout to occur.
Definition: Timeout.cc:147
uint64_t period_us
0 if not periodic
Definition: Timeout.hh:39
Specification for a Timeout function.
Definition: Timeout.hh:28
int get_next_expired_timeout(std::string &desc, void **tag, std::function< void()> &function, uint64_t *tmo_tod_us)
Get a timeout that has expired.
Definition: Timeout.cc:126
bool is_consistent()
Run a consistency check on all confiugured timeouts.
Definition: Timeout.cc:183
std::string desc
Description of the Timeout function.
Definition: Timeout.hh:35
bool cancel_timeout(void *tag, std::string desc)
Cancel the timeout having the given description and tag.
Definition: Timeout.cc:299
void add_periodic(const char *desc, void *tag, std::function< void()> &function, uint64_t period_us, uint64_t start_us=0)
Add a periodic timeout to the Timeout container.
Definition: Timeout.cc:64
Timeout(int max_tmos=100)
Construct a Timeout object.
Definition: Timeout.cc:57
void list_active_time()
TRACE all active timeouts.
Definition: Timeout.cc:336