artdaq  v3_09_00
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 <sys/time.h> /* struct timeval */
16 #include <cassert> /* assert */
17 #include <cstdio> // printf
18 #include <cstdlib> // exit
19 #include <cstring> /* strcmp */
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 != 0u ? 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 = nullptr;
94  tmo.function = nullptr;
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(const std::string& desc, int rel_ms)
114 {
115  timeoutspec tmo;
116  tmo.desc = desc;
117  tmo.tag = nullptr;
118  tmo.function = nullptr;
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  {
164  *delay_us = 0;
165  }
166  }
167 } // get_next_timeout_delay
168 
170 {
171  int64_t delay_us;
172  int tmo;
173  get_next_timeout_delay(&delay_us);
174  if (delay_us == -1)
175  {
176  tmo = -1;
177  }
178  else
179  { // NOTE THE + 1 b/c of integer division and/or system HZ resolution
180  tmo = delay_us / 1000;
181  }
182  return (tmo);
183 } // get_next_timeout_msdly
184 
186 {
187  std::map<uint64_t, size_t>::iterator itactive;
188  std::list<size_t>::iterator itfree;
189  for (auto& tmospec : tmospecs_)
190  {
191  tmospec.check = 1;
192  }
193  for (itactive = active_time_.begin(); itactive != active_time_.end(); ++itactive)
194  {
195  tmospecs_[(*itactive).second].check--;
196  }
197  for (itfree = free_.begin(); itfree != free_.end(); ++itfree)
198  {
199  tmospecs_[*itfree].check--;
200  }
201  for (auto& tmospec : tmospecs_)
202  {
203  if (tmospec.check != 0)
204  {
205  return false;
206  }
207  }
208  return (true);
209 }
210 
211 void Timeout::timeoutlist_init()
212 {
213  size_t list_sz = tmospecs_.size();
214  for (size_t ii = 0; ii < list_sz; ++ii)
215  { //bzero( &tmospecs_[list_sz], sizeof(tmospecs_[0]) );
216  free_.push_front(ii);
217  }
218 }
219 
220 int Timeout::get_clear_next_expired_timeout(timeoutspec& tmo, uint64_t tod_now_us)
221 {
222  int skipped = 0;
223  if (active_time_.empty())
224  {
225  TLOG(17) << "get_clear_next_expired_timeout - nothing to get/clear!";
226  return static_cast<int>(false);
227  }
228 
229  std::unique_lock<std::mutex> ulock(lock_mutex_);
230  auto itfront = active_time_.begin();
231  size_t idx = (*itfront).second;
232  if (tmospecs_[idx].tmo_tod_us < tod_now_us)
233  {
234  tmo = tmospecs_[idx];
235  TLOG(17) << "get_clear_next_expired_timeout - clearing tag=" << tmo.tag << " desc=" << tmo.desc << " period=" << tmo.period_us << " idx=" << idx;
236 
237  active_time_.erase(itfront);
238  // now, be effecient -- if periodic, add back at new time, else
239  // find/erase active_desc_ with same idx and free
240  if (tmo.period_us != 0u)
241  {
242  // PERIODIC
243  int64_t delta_us;
244  uint64_t period_us = tmo.period_us;
245  delta_us = tod_now_us - tmo.tmo_tod_us;
246  skipped = delta_us / period_us;
247  assert(skipped >= 0);
248  tmo.missed_periods += skipped;
249 
250  /* now fast forward over skipped */
251  period_us += period_us * skipped;
252  tmospecs_[idx].tmo_tod_us += period_us;
253  active_time_.insert(std::pair<uint64_t, size_t>(tmospecs_[idx].tmo_tod_us, idx));
254  TLOG(18) << "get_clear_next_expired_timeout - periodic timeout desc=" << tmo.desc
255  << " period_us=" << period_us << " delta_us=" << delta_us
256  << " skipped=" << skipped << " next tmo at:" << tmospecs_[idx].tmo_tod_us;
257  }
258  else
259  {
260  // find active_desc_ with same idx
261  std::unordered_multimap<std::string, size_t>::iterator i2;
262  i2 = active_desc_.equal_range(tmospecs_[idx].desc).first;
263  while (true)
264  { // see also in cancel_timeout below
265  if (i2->second == idx)
266  {
267  break;
268  }
269  ++i2;
270  }
271  active_desc_.erase(i2);
272  free_.push_front(idx);
273  }
274  }
275  else
276  {
277  TLOG(17) << "get_clear_next_expired_timeout - front " << tmospecs_[idx].tmo_tod_us << " NOT before ts_now " << tod_now_us << " - not clearing!";
278  return (-1);
279  }
280 
281  return 1;
282 } // get_clear_next_expired_timeout
283 
284 // this doesn't do anything (function undefined)
285 void Timeout::copy_in_timeout(const char* desc, uint64_t period_us, uint64_t start_us)
286 {
287  TLOG(18) << "copy_in_timeout desc=" + std::string(desc);
288  timeoutspec tos;
289  tos.desc = desc;
290  tos.tag = nullptr;
291  tos.function = nullptr;
292  tos.period_us = period_us;
293  tos.tmo_tod_us = start_us;
294  tos.missed_periods = tos.check = 0;
295  copy_in_timeout(tos);
296 }
297 
298 void Timeout::copy_in_timeout(timeoutspec& tmo)
299 {
300  // check for at least one empty entry
301  assert(free_.size());
302 
303  // get/fill-in free tmospec
304  std::unique_lock<std::mutex> ulock(lock_mutex_);
305  size_t idx = free_.front();
306  free_.pop_front();
307  tmospecs_[idx] = tmo;
308  TLOG(20) << "copy_in_timeout timeoutspec desc=" + tmo.desc;
309  active_time_.insert(std::pair<uint64_t, size_t>(tmo.tmo_tod_us, idx));
310  active_desc_.insert(std::pair<std::string, size_t>(tmo.desc, idx));
311 }
312 
313 bool Timeout::cancel_timeout(void* tag, const std::string& desc)
314 {
315  bool retsts = false;
316  std::unordered_multimap<std::string, size_t>::iterator ii, ee;
317  std::unique_lock<std::mutex> ulock(lock_mutex_);
318  auto pairOfIters = active_desc_.equal_range(desc);
319  ii = pairOfIters.first;
320  ee = pairOfIters.second;
321  for (; ii != ee && ii->first == desc; ++ii)
322  {
323  size_t idx = ii->second;
324  if (tmospecs_[idx].tag == tag)
325  {
326  // found a match
327  retsts = true;
328  uint64_t tmo_tod_us = tmospecs_[idx].tmo_tod_us;
329 
330  // now make sure to find the active_time_ with the same idx
331  std::multimap<uint64_t, size_t>::iterator i2;
332  i2 = active_time_.equal_range(tmo_tod_us).first;
333  while (true)
334  { // see also in get_clear_next_expired_timeout above
335  if (i2->second == idx)
336  {
337  break;
338  }
339  ++i2;
340  }
341 
342  active_desc_.erase(ii);
343  active_time_.erase(i2);
344  free_.push_front(idx);
345  break;
346  }
347  }
348  TLOG(22) << "cancel_timeout returning " << retsts;
349  return retsts;
350 } // cancel_timeout
351 
353 {
354  auto ii = active_time_.begin(), ee = active_time_.end();
355  for (; ii != ee; ++ii)
356  {
357  TLOG(TLVL_DEBUG) << "list_active_time " << (*ii).first << " desc=" << tmospecs_[(*ii).second].desc;
358  }
359 }
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:285
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:169
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
bool cancel_timeout(void *tag, const std::string &desc)
Cancel the timeout having the given description and tag.
Definition: Timeout.cc:313
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:185
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: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:352