artdaq  v3_02_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 <stdio.h> // printf
16 #include <sys/time.h> /* struct timeval */
17 #include <assert.h> /* assert */
18 #include <string.h> /* strcmp */
19 #include <stdlib.h> // exit
20 #include <list>
21 using std::list;
22 #include "artdaq/TransferPlugins/detail/Timeout.hh"
23 #include "artdaq/DAQdata/Globals.hh" // TRACE
24 #include "artdaq-core/Utilities/TimeUtils.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 
58 Timeout::Timeout(int max_tmos)
59  : tmospecs_(max_tmos)
60 {
61  TLOG(16) << "Timeout ctor";
62  timeoutlist_init();
63 }
64 
65 
66 void
67 Timeout::add_periodic(const char* desc, void* tag, std::function<void()>& function
68  , uint64_t period_us
69  , uint64_t start_us)
70 {
71  timeoutspec tmo;
72  tmo.desc = desc;
73  tmo.tag = tag;
74  tmo.function = function;
75  tmo.tmo_tod_us = start_us ? start_us : artdaq::TimeUtils::gettimeofday_us() + period_us;
76  tmo.period_us = period_us;
77  tmo.check = tmo.missed_periods = 0;
78  copy_in_timeout(tmo);
79 } // add_periodic
80 
81 void
82 Timeout::add_periodic(const char* desc, void* tag, std::function<void()>& function
83  , int rel_ms)
84 {
85  timeoutspec tmo;
86  tmo.desc = desc;
87  tmo.tag = tag;
88  tmo.function = function;
89  tmo.period_us = rel_ms * 1000;
90  tmo.tmo_tod_us = artdaq::TimeUtils::gettimeofday_us() + tmo.period_us;
91  tmo.check = tmo.missed_periods = 0;
92  copy_in_timeout(tmo);
93 } // add_periodic
94 
95 void
96 Timeout::add_periodic(const char* desc
97  , uint64_t period_us
98  , uint64_t start_us)
99 {
100  TLOG(19) << "add_periodic - desc=" << desc << " period_us=" << period_us << " start_us=" << start_us;
101  timeoutspec tmo;
102  tmo.desc = desc;
103  tmo.tag = 0;
104  tmo.function = 0;
105  tmo.tmo_tod_us = start_us;
106  tmo.period_us = period_us;
107  tmo.missed_periods = tmo.check = 0;
108  copy_in_timeout(tmo);
109 } // add_periodic
110 
111 void
112 Timeout::add_relative(const char* desc, void* tag, std::function<void()>& function
113  , int rel_ms)
114 {
115  timeoutspec tmo;
116  tmo.desc = desc;
117  tmo.tag = tag;
118  tmo.function = function;
119  tmo.tmo_tod_us = artdaq::TimeUtils::gettimeofday_us() + rel_ms * 1000;
120  tmo.period_us = 0;
121  tmo.missed_periods = tmo.check = 0;
122  copy_in_timeout(tmo);
123 } // add_periodic
124 
125 void
126 Timeout::add_relative(std::string desc
127  , int rel_ms)
128 {
129  timeoutspec tmo;
130  tmo.desc = desc.c_str();
131  tmo.tag = 0;
132  tmo.function = 0;
133  tmo.period_us = 0;
134  tmo.tmo_tod_us = artdaq::TimeUtils::gettimeofday_us() + rel_ms * 1000;
135  tmo.missed_periods = tmo.check = 0;
136  copy_in_timeout(tmo);
137 } // add_periodic
138 
139 int // tmo_tod_us is an output
140 Timeout::get_next_expired_timeout(std::string& desc, void** tag, std::function<void()>& function
141  , uint64_t* tmo_tod_us)
142 {
143  int skipped = 0;
144  timeoutspec tmo;
145  TLOG(15) << "get_next_expired_timeout b4 get_clear_next_expired_timeout";
146  skipped = get_clear_next_expired_timeout(tmo, artdaq::TimeUtils::gettimeofday_us());
147  if (skipped == -1)
148  {
149  TLOG(18) << "get_next_expired_timeout - get_clear_next_expired_timeout returned false";
150  desc = std::string(""); // 2 ways to check for none timed out
151  }
152  else
153  {
154  desc = tmo.desc;
155  *tag = tmo.tag;
156  function = tmo.function;
157  *tmo_tod_us = tmo.tmo_tod_us;
158  }
159  return (skipped);
160 } // get_next_expired_timeout
161 
162 void
164 {
165  std::unique_lock<std::mutex> ulock(lock_mutex_);
166  size_t active_time_size = active_time_.size();
167  if (active_time_size == 0)
168  {
169  TLOG(17) << "get_next_timeout_delay active_.size() == 0";
170  *delay_us = -1; // usually means a very very long time
171  }
172  else
173  {
174  TLOG(17) << "get_next_timeout_delay active_.size() != 0: " << active_time_size;
175  uint64_t tod_us = artdaq::TimeUtils::gettimeofday_us();
176  timeoutspec* tmo = &tmospecs_[(*(active_time_.begin())).second];
177  *delay_us = tmo->tmo_tod_us - tod_us;
178  if (*delay_us < 0)
179  *delay_us = 0;
180  }
181 } // get_next_timeout_delay
182 
183 int
185 {
186  int64_t delay_us;
187  int tmo;
188  get_next_timeout_delay(&delay_us);
189  if (delay_us == -1)
190  {
191  tmo = -1;
192  }
193  else
194  { // NOTE THE + 1 b/c of integer division and/or system HZ resolution
195  tmo = delay_us / 1000;
196  }
197  return (tmo);
198 } // get_next_timeout_msdly
199 
200 
201 bool
203 {
204  std::map<uint64_t, size_t>::iterator itactive;
205  std::list<size_t>::iterator itfree;
206  for (unsigned ii = 0; ii < tmospecs_.size(); ++ii)
207  tmospecs_[ii].check = 1;
208  for (itactive = active_time_.begin(); itactive != active_time_.end(); ++itactive)
209  tmospecs_[(*itactive).second].check--;
210  for (itfree = free_.begin(); itfree != free_.end(); ++itfree)
211  tmospecs_[*itfree].check--;
212  for (unsigned ii = 0; ii < tmospecs_.size(); ++ii)
213  if (tmospecs_[ii].check != 0) return false;
214  return (true);
215 }
216 
217 void
218 Timeout::timeoutlist_init()
219 {
220  size_t list_sz = tmospecs_.size();
221  for (size_t ii = 0; ii < list_sz; ++ii)
222  { //bzero( &tmospecs_[list_sz], sizeof(tmospecs_[0]) );
223  free_.push_front(ii);
224  }
225 }
226 
227 
228 int Timeout::get_clear_next_expired_timeout(timeoutspec& tmo
229  , uint64_t tod_now_us)
230 {
231  int skipped = 0;
232  if (active_time_.size() == 0)
233  {
234  TLOG(17) << "get_clear_next_expired_timeout - nothing to get/clear!";
235  return (false);
236  }
237  else
238  {
239  std::unique_lock<std::mutex> ulock(lock_mutex_);
240  std::multimap<uint64_t, size_t>::iterator itfront = active_time_.begin();
241  size_t idx = (*itfront).second;
242  if (tmospecs_[idx].tmo_tod_us < tod_now_us)
243  {
244  tmo = tmospecs_[idx];
245  TLOG(17) << "get_clear_next_expired_timeout - clearing tag=" << tmo.tag << " desc=" << tmo.desc << " period=" << tmo.period_us << " idx=" << idx;
246 
247  active_time_.erase(itfront);
248  // now, be effecient -- if periodic, add back at new time, else
249  // find/erase active_desc_ with same idx and free
250  if (tmo.period_us)
251  {
252  // PERIODIC
253  int64_t delta_us;
254  uint64_t period_us = tmo.period_us;
255  delta_us = tod_now_us - tmo.tmo_tod_us;
256  skipped = delta_us / period_us;
257  assert(skipped >= 0);
258  tmo.missed_periods += skipped;
259 
260  /* now fast forward over skipped */
261  period_us += period_us * skipped;
262  tmospecs_[idx].tmo_tod_us += period_us;
263  active_time_.insert(std::pair<uint64_t, size_t>(tmospecs_[idx].tmo_tod_us, idx));
264  TLOG(18) << "get_clear_next_expired_timeout - periodic timeout desc=" << tmo.desc
265  << " period_us=" << period_us << " delta_us=" << delta_us
266  << " skipped=" << skipped << " next tmo at:" << tmospecs_[idx].tmo_tod_us;
267  }
268  else
269  {
270  // find active_desc_ with same idx
271  std::unordered_multimap<std::string, size_t>::iterator i2;
272  i2 = active_desc_.equal_range(tmospecs_[idx].desc).first;
273  while (1)
274  { // see also in cancel_timeout below
275  if (i2->second == idx)
276  break;
277  ++i2;
278  }
279  active_desc_.erase(i2);
280  free_.push_front(idx);
281  }
282  }
283  else
284  {
285  TLOG(17) << "get_clear_next_expired_timeout - front " << tmospecs_[idx].tmo_tod_us << " NOT before ts_now " << tod_now_us << " - not clearing!";
286  return (-1);
287  }
288  }
289  return true;
290 } // get_clear_next_expired_timeout
291 
292 // this doesn't do anything (function undefined)
293 void Timeout::copy_in_timeout(const char* desc, uint64_t period_us, uint64_t start_us)
294 {
295  TLOG(18) << "copy_in_timeout desc=" + std::string(desc);
296  timeoutspec tos;
297  tos.desc = desc;
298  tos.tag = NULL;
299  tos.function = 0;
300  tos.period_us = period_us;
301  tos.tmo_tod_us = start_us;
302  tos.missed_periods = tos.check = 0;
303  copy_in_timeout(tos);
304 }
305 
306 void
307 Timeout::copy_in_timeout(timeoutspec& tmo)
308 {
309  // check for at least one empty entry
310  assert(free_.size());
311 
312  // get/fill-in free tmospec
313  std::unique_lock<std::mutex> ulock(lock_mutex_);
314  size_t idx = free_.front();
315  free_.pop_front();
316  tmospecs_[idx] = tmo;
317  TLOG(20) << "copy_in_timeout timeoutspec desc=" + tmo.desc;
318  active_time_.insert(std::pair<uint64_t, size_t>(tmo.tmo_tod_us, idx));
319  active_desc_.insert(std::pair<std::string, size_t>(tmo.desc, idx));
320 }
321 
322 bool
324  , std::string desc)
325 {
326  bool retsts = false;
327  std::unordered_multimap<std::string, size_t>::iterator ii, ee;
328  std::unique_lock<std::mutex> ulock(lock_mutex_);
329  auto pairOfIters = active_desc_.equal_range(desc);
330  ii = pairOfIters.first;
331  ee = pairOfIters.second;
332  for (; ii != ee && ii->first.compare(desc) == 0; ++ii)
333  {
334  size_t idx = ii->second;
335  if (tmospecs_[idx].tag == tag)
336  {
337  // found a match
338  retsts = true;
339  uint64_t tmo_tod_us = tmospecs_[idx].tmo_tod_us;
340 
341  // now make sure to find the active_time_ with the same idx
342  std::multimap<uint64_t, size_t>::iterator i2;
343  i2 = active_time_.equal_range(tmo_tod_us).first;
344  while (1)
345  { // see also in get_clear_next_expired_timeout above
346  if (i2->second == idx)
347  break;
348  ++i2;
349  }
350 
351  active_desc_.erase(ii);
352  active_time_.erase(i2);
353  free_.push_front(idx);
354  break;
355  }
356  }
357  TLOG(22) << "cancel_timeout returning " << retsts;
358  return retsts;
359 } // cancel_timeout
360 
361 void
363 {
364  std::map<uint64_t, size_t>::iterator ii = active_time_.begin(), ee = active_time_.end();
365  for (; ii != ee; ++ii)
366  {
367  TLOG(TLVL_DEBUG) << "list_active_time " << (*ii).first << " desc=" << tmospecs_[(*ii).second].desc;
368  }
369 }
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:293
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:112
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:184
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:163
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:140
bool is_consistent()
Run a consistency check on all confiugured timeouts.
Definition: Timeout.cc:202
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:323
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:67
Timeout(int max_tmos=100)
Construct a Timeout object.
Definition: Timeout.cc:58
void list_active_time()
TRACE all active timeouts.
Definition: Timeout.cc:362