blocxx
TimeoutTimer.cpp
Go to the documentation of this file.
1 /*******************************************************************************
2 * Copyright (C) 2005, Vintela, Inc. All rights reserved.
3 * Copyright (C) 2006, Novell, Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of
14 * Vintela, Inc.,
15 * nor Novell, Inc.,
16 * nor the names of its contributors or employees may be used to
17 * endorse or promote products derived from this software without
18 * specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 *******************************************************************************/
32 
33 
38 #include "blocxx/BLOCXX_config.h"
39 #include "blocxx/TimeoutTimer.hpp"
40 #include "blocxx/DateTime.hpp"
41 #include "blocxx/Infinity.hpp"
42 
43 #include <limits>
44 
45 namespace BLOCXX_NAMESPACE
46 {
47 
49  : m_timeout(x)
50  , m_start(getCurrentTime())
51  , m_loopTime(m_start)
52 {
53 }
54 
56 {
57 }
58 
59 void
61 {
63 }
64 
65 void
67 {
68  loop();
70  {
72  }
73 }
74 
75 void
77 {
79 }
80 
81 namespace
82 {
83 
84 template <typename T, typename U>
85 void safeAssign(T& x, U y)
86 {
87  if (y < (std::numeric_limits<T>::min)())
88  {
89  x = (std::numeric_limits<T>::min)();
90  }
91  else if (y > (std::numeric_limits<T>::max)())
92  {
93  x = (std::numeric_limits<T>::max)();
94  }
95  else
96  {
97  x = static_cast<T>(y + 0.5); // we add 0.5 so the behavior is like rounding, instead of truncation.
98  }
99 }
100 
101 bool compareInterval(const DateTime& first, const DateTime& timeToTest, double seconds)
102 {
103  // Return first + seconds < timeToTest <==> timeToTest - first >= seconds.
104  // The latter form avoids overflow problems.
105  DateTime diff = timeToTest - first;
106  double diff1 = diff.get() + diff.getMicrosecond() * 1e-6;
107  return diff1 >= seconds;
108 }
109 
110 } // end unnamed namespace
111 
112 bool
114 {
115  if (infinite())
116  {
117  return false;
118  }
119 
120  switch (m_timeout.getType())
121  {
122  case Timeout::E_ABSOLUTE:
123  return m_loopTime >= m_timeout.getAbsolute();
124 
125  case Timeout::E_RELATIVE:
127  return compareInterval(m_start, m_loopTime, m_timeout.getRelative());
128  }
129  return false;
130 }
131 
132 bool
134 {
135  return m_timeout == Timeout::infinite;
136 }
137 
138 #ifdef BLOCXX_HAVE_STRUCT_TIMEVAL
139 // return 0 for infinite, otherwise a pointer to tv
140 ::timeval*
141 TimeoutTimer::asTimeval(::timeval& tv, double maxSeconds) const
142 {
143  if (infinite() && maxSeconds == INFINITY)
144  {
145  return 0;
146  }
147 
148  asTimeval(tv);
149 
150  // check & enforce maxSeconds parameter
151  ::timeval temp;
152  safeAssign(temp.tv_sec, maxSeconds);
153  double dummy;
154  safeAssign(temp.tv_usec, modf(maxSeconds, &dummy) * 1e6);
155  if (infinite() || temp.tv_sec < tv.tv_sec)
156  {
157  tv = temp;
158  }
159  else if (temp.tv_sec == tv.tv_sec && temp.tv_usec < tv.tv_usec)
160  {
161  tv.tv_usec = temp.tv_usec;
162  }
163 
164  return &tv;
165 }
166 
167 // return 0 for infinite, otherwise a pointer to tv
168 ::timeval*
169 TimeoutTimer::asTimeval(::timeval& tv) const
170 {
171  if (infinite())
172  {
173  return 0;
174  }
175 
177  {
178  // convert the difference between the last loop and the absolute timeout into a timeval
179  DateTime timeoutTime = m_timeout.getAbsolute();
180  if (timeoutTime > m_loopTime)
181  {
182  DateTime diff = m_timeout.getAbsolute() - m_loopTime;
183  tv.tv_sec = diff.get();
184  tv.tv_usec = diff.getMicrosecond();
185  }
186  else
187  {
188  // somehow we got past the timeout, so just return a 0 length timeval.
189  tv.tv_sec = 0;
190  tv.tv_usec = 0;
191  }
192  }
193  else // it's relative
194  {
195  double timeTillExpiration = calcSeconds();
196  safeAssign(tv.tv_sec, timeTillExpiration);
197  double dummy;
198  safeAssign(tv.tv_usec, modf(timeTillExpiration, &dummy) * 1e6);
199  }
200 
201 
202  return &tv;
203 }
204 #endif
205 
206 Timeout
208 {
209  return m_timeout;
210 }
211 
212 double
214 {
215  // return the number of seconds until the timeout expires.
216  double seconds;
218  {
220  seconds = static_cast<double>(diff.get()) + (static_cast<double>(diff.getMicrosecond()) / 1e6);
221  }
222  else
223  {
224  // seconds = (m_start + m_timeout.getRelative()) - m_loopTime
225  double start = static_cast<double>(m_start.get()) + (static_cast<double>(m_start.getMicrosecond()) / 1e6);
226  double loop = static_cast<double>(m_loopTime.get()) + (static_cast<double>(m_loopTime.getMicrosecond()) / 1e6);
227  seconds = start + m_timeout.getRelative() - loop;
228  }
229  return seconds;
230 }
231 
232 double
233 TimeoutTimer::calcSeconds(double maxSeconds) const
234 {
235  double seconds = calcSeconds();
236  seconds = seconds > maxSeconds ? maxSeconds : seconds;
237  return seconds;
238 }
239 
240 Timeout
242 {
243  return Timeout::relative(calcSeconds());
244 }
245 
246 Timeout
247 TimeoutTimer::asRelativeTimeout(double maxSeconds) const
248 {
249  return Timeout::relative(calcSeconds(maxSeconds));
250 }
251 
252 Timeout
254 {
255  // an infinite timeout cannot be converted to absolute, it stays infinite
256  if (infinite())
257  {
258  return m_timeout;
259  }
261  {
262  return m_timeout;
263  }
264  else
265  {
266  const long MICROSECONDS_PER_SECOND = 1000000;
267  DateTime endTime(m_start);
268  time_t secs;
269  safeAssign(secs, m_start.get() + static_cast<double>(m_timeout.getRelative()));
270  UInt32 microSecs;
271  double dummy;
272  safeAssign(microSecs,
274  modf(m_timeout.getRelative(), &dummy) * MICROSECONDS_PER_SECOND);
275 
276  // handle any overflow
277  secs += microSecs / MICROSECONDS_PER_SECOND;
278  microSecs = microSecs % MICROSECONDS_PER_SECOND;
279 
280  return Timeout::absolute(DateTime(secs, microSecs));
281  }
282 }
283 
284 #ifdef BLOCXX_WIN32
285 DWORD
286 TimeoutTimer::asDWORDMs() const
287 {
288  if (infinite())
289  {
290  return INFINITE;
291  }
292 
293  DWORD rval;
294  safeAssign(rval, calcSeconds() * 1000.0);
295  return rval;
296 }
297 #endif
298 
299 int
301 {
302  if (infinite())
303  {
304  return -1;
305  }
306 
307  int rval;
308  safeAssign(rval, calcSeconds() * 1000.0);
309  return rval;
310 }
311 
312 int
313 TimeoutTimer::asIntMs(double maxSeconds) const
314 {
315  if (infinite() && maxSeconds == INFINITY)
316  {
317  return -1;
318  }
319 
320  int rval;
321  safeAssign(rval, calcSeconds(maxSeconds) * 1000.0);
322  return rval;
323 }
324 
325 #ifdef BLOCXX_HAVE_STRUCT_TIMESPEC
326 ::timespec*
327 TimeoutTimer::asTimespec(::timespec& ts) const
328 {
329  const long NANOSECONDS_PER_MICROSECOND = 1000;
330  const long NANOSECONDS_PER_MILLISECOND = 1000000;
331  const long NANOSECONDS_PER_SECOND = 1000000000;
332 
334  {
335  // convert the timeout to a timespec
336  DateTime timeoutTime = m_timeout.getAbsolute();
337  if (timeoutTime > m_loopTime)
338  {
339  ts.tv_sec = timeoutTime.get();
340  ts.tv_nsec = timeoutTime.getMicrosecond() * NANOSECONDS_PER_MICROSECOND;
341  }
342  else
343  {
344  // somehow we got past the timeout (a time warp?), so just return a 0 timespec.
345  ts.tv_sec = 0;
346  ts.tv_nsec = 0;
347  }
348  }
349  else // relative
350  {
351  safeAssign(ts.tv_sec, m_start.get() + static_cast<double>(m_timeout.getRelative()));
352  double dummy;
353  safeAssign(ts.tv_nsec,
354  static_cast<double>(m_start.getMicrosecond()) * NANOSECONDS_PER_MICROSECOND +
355  modf(m_timeout.getRelative(), &dummy) * NANOSECONDS_PER_SECOND);
356 
357  // handle any overflow because ts.tv_nsec has to be in the correct range.
358  ts.tv_sec += ts.tv_nsec / NANOSECONDS_PER_SECOND;
359  ts.tv_nsec = ts.tv_nsec % NANOSECONDS_PER_SECOND;
360  }
361 
362  return &ts;
363 }
364 #endif
365 
366 DateTime
368 {
369  return DateTime::getCurrent();
370 }
371 
372 } // end namespace BLOCXX_NAMESPACE
373 
374 
375 
376