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