blocxx

TimeoutTimer.cpp

Go to the documentation of this file.
00001 /*******************************************************************************
00002 * Copyright (C) 2005, Vintela, Inc. All rights reserved.
00003 * Copyright (C) 2006, Novell, Inc. All rights reserved.
00004 * 
00005 * Redistribution and use in source and binary forms, with or without
00006 * modification, are permitted provided that the following conditions are met:
00007 * 
00008 *     * Redistributions of source code must retain the above copyright notice,
00009 *       this list of conditions and the following disclaimer.
00010 *     * Redistributions in binary form must reproduce the above copyright
00011 *       notice, this list of conditions and the following disclaimer in the
00012 *       documentation and/or other materials provided with the distribution.
00013 *     * Neither the name of 
00014 *       Vintela, Inc., 
00015 *       nor Novell, Inc., 
00016 *       nor the names of its contributors or employees may be used to 
00017 *       endorse or promote products derived from this software without 
00018 *       specific prior written permission.
00019 * 
00020 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00021 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00022 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00023 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00024 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00025 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00026 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00027 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00028 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00029 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00030 * POSSIBILITY OF SUCH DAMAGE.
00031 *******************************************************************************/
00032 
00033 
00038 #include "blocxx/BLOCXX_config.h"
00039 #include "blocxx/TimeoutTimer.hpp"
00040 #include "blocxx/DateTime.hpp"
00041 #include "blocxx/Infinity.hpp"
00042 
00043 #include <limits>
00044 
00045 namespace BLOCXX_NAMESPACE
00046 {
00047 
00048 TimeoutTimer::TimeoutTimer(const Timeout& x)
00049    : m_timeout(x)
00050    , m_start(getCurrentTime())
00051    , m_loopTime(m_start)
00052 {
00053 }
00054 
00055 TimeoutTimer::~TimeoutTimer()
00056 {
00057 }
00058 
00059 void
00060 TimeoutTimer::start()
00061 {
00062    m_start = m_loopTime = getCurrentTime();
00063 }
00064 
00065 void
00066 TimeoutTimer::resetOnLoop()
00067 {
00068    loop();
00069    if (m_timeout.getType() == Timeout::E_RELATIVE_WITH_RESET)
00070    {
00071       m_start = m_loopTime;
00072    }
00073 }
00074 
00075 void
00076 TimeoutTimer::loop()
00077 {
00078    m_loopTime = getCurrentTime();
00079 }
00080 
00081 namespace
00082 {
00083 
00084 template <typename T, typename U>
00085 void safeAssign(T& x, U y)
00086 {
00087    if (y < (std::numeric_limits<T>::min)())
00088    {
00089       x = (std::numeric_limits<T>::min)();
00090    }
00091    else if (y > (std::numeric_limits<T>::max)())
00092    {
00093       x = (std::numeric_limits<T>::max)();
00094    }
00095    else
00096    {
00097       x = static_cast<T>(y + 0.5); // we add 0.5 so the behavior is like rounding, instead of truncation.
00098    }
00099 }
00100 
00101 bool compareInterval(const DateTime& first, const DateTime& timeToTest, double seconds)
00102 {
00103    // Return first + seconds < timeToTest <==> timeToTest - first >= seconds.
00104    // The latter form avoids overflow problems.
00105    DateTime diff = timeToTest - first;
00106    double diff1 = diff.get() + diff.getMicrosecond() * 1e-6;
00107    return diff1 >= seconds;
00108 }
00109 
00110 } // end unnamed namespace
00111 
00112 bool 
00113 TimeoutTimer::expired() const
00114 {
00115    if (infinite())
00116    {
00117       return false;
00118    }
00119 
00120    switch (m_timeout.getType())
00121    {
00122       case Timeout::E_ABSOLUTE:
00123          return m_loopTime >= m_timeout.getAbsolute();
00124 
00125       case Timeout::E_RELATIVE:
00126       case Timeout::E_RELATIVE_WITH_RESET:
00127          return compareInterval(m_start, m_loopTime, m_timeout.getRelative());
00128    }
00129    return false;
00130 }
00131 
00132 bool
00133 TimeoutTimer::infinite() const
00134 {
00135    return m_timeout == Timeout::infinite;
00136 }
00137 
00138 #ifdef BLOCXX_HAVE_STRUCT_TIMEVAL
00139 // return 0 for infinite, otherwise a pointer to tv
00140 ::timeval* 
00141 TimeoutTimer::asTimeval(::timeval& tv, double maxSeconds) const
00142 {
00143    if (infinite() && maxSeconds == INFINITY)
00144    {
00145       return 0;
00146    }
00147 
00148    asTimeval(tv);
00149 
00150    // check & enforce maxSeconds parameter
00151    ::timeval temp;
00152    safeAssign(temp.tv_sec, maxSeconds);
00153    double dummy;
00154    safeAssign(temp.tv_usec, modf(maxSeconds, &dummy) * 1e6);
00155    if (infinite() || temp.tv_sec < tv.tv_sec)
00156    {
00157       tv = temp;
00158    }
00159    else if (temp.tv_sec == tv.tv_sec && temp.tv_usec < tv.tv_usec)
00160    {
00161       tv.tv_usec = temp.tv_usec;
00162    }
00163 
00164    return &tv;
00165 }
00166 
00167 // return 0 for infinite, otherwise a pointer to tv
00168 ::timeval* 
00169 TimeoutTimer::asTimeval(::timeval& tv) const
00170 {
00171    if (infinite())
00172    {
00173       return 0;
00174    }
00175 
00176    if (m_timeout.getType() == Timeout::E_ABSOLUTE)
00177    {
00178       // convert the difference between the last loop and the absolute timeout into a timeval
00179       DateTime timeoutTime = m_timeout.getAbsolute();
00180       if (timeoutTime > m_loopTime)
00181       {
00182          DateTime diff = m_timeout.getAbsolute() - m_loopTime;
00183          tv.tv_sec = diff.get();
00184          tv.tv_usec = diff.getMicrosecond();
00185       }
00186       else
00187       {
00188          // somehow we got past the timeout, so just return a 0 length timeval.
00189          tv.tv_sec = 0;
00190          tv.tv_usec = 0;
00191       }
00192    }
00193    else // it's relative
00194    {
00195       double timeTillExpiration = calcSeconds();
00196       safeAssign(tv.tv_sec, timeTillExpiration);
00197       double dummy;
00198       safeAssign(tv.tv_usec, modf(timeTillExpiration, &dummy) * 1e6);
00199    }
00200 
00201 
00202    return &tv;
00203 }
00204 #endif
00205 
00206 Timeout
00207 TimeoutTimer::asTimeout() const
00208 {
00209    return m_timeout;
00210 }
00211 
00212 double
00213 TimeoutTimer::calcSeconds() const
00214 {
00215    // return the number of seconds until the timeout expires.
00216    double seconds;
00217    if (m_timeout.getType() == Timeout::E_ABSOLUTE)
00218    {
00219       DateTime diff = m_timeout.getAbsolute() - m_loopTime;
00220       seconds = static_cast<double>(diff.get()) + (static_cast<double>(diff.getMicrosecond()) / 1e6);
00221    }
00222    else
00223    {
00224       // seconds = (m_start + m_timeout.getRelative()) - m_loopTime
00225       double start = static_cast<double>(m_start.get()) + (static_cast<double>(m_start.getMicrosecond()) / 1e6);
00226       double loop = static_cast<double>(m_loopTime.get()) + (static_cast<double>(m_loopTime.getMicrosecond()) / 1e6);
00227       seconds = start + m_timeout.getRelative() - loop;
00228    }
00229    return seconds;
00230 }
00231 
00232 double
00233 TimeoutTimer::calcSeconds(double maxSeconds) const
00234 {
00235    double seconds = calcSeconds();
00236    seconds = seconds > maxSeconds ? maxSeconds : seconds;
00237    return seconds;
00238 }
00239 
00240 Timeout 
00241 TimeoutTimer::asRelativeTimeout() const
00242 {
00243    return Timeout::relative(calcSeconds());
00244 }
00245 
00246 Timeout 
00247 TimeoutTimer::asRelativeTimeout(double maxSeconds) const
00248 {
00249    return Timeout::relative(calcSeconds(maxSeconds));
00250 }
00251 
00252 Timeout 
00253 TimeoutTimer::asAbsoluteTimeout() const
00254 {
00255    // an infinite timeout cannot be converted to absolute, it stays infinite
00256    if (infinite())
00257    {
00258       return m_timeout;
00259    }
00260    if (m_timeout.getType() == Timeout::E_ABSOLUTE)
00261    {
00262       return m_timeout;
00263    }
00264    else
00265    {
00266       const long MICROSECONDS_PER_SECOND = 1000000;
00267       DateTime endTime(m_start);
00268       time_t secs;
00269       safeAssign(secs, m_start.get() + static_cast<double>(m_timeout.getRelative()));
00270       UInt32 microSecs;
00271       double dummy;
00272       safeAssign(microSecs, 
00273          m_start.getMicrosecond() +
00274          modf(m_timeout.getRelative(), &dummy) * MICROSECONDS_PER_SECOND);
00275 
00276       // handle any overflow
00277       secs += microSecs / MICROSECONDS_PER_SECOND;
00278       microSecs = microSecs % MICROSECONDS_PER_SECOND;
00279 
00280       return Timeout::absolute(DateTime(secs, microSecs));
00281    }
00282 }
00283 
00284 #ifdef BLOCXX_WIN32
00285 DWORD 
00286 TimeoutTimer::asDWORDMs() const
00287 {
00288    if (infinite())
00289    {
00290       return INFINITE;
00291    }
00292 
00293    DWORD rval;
00294    safeAssign(rval, calcSeconds() * 1000.0);
00295    return rval;
00296 }
00297 #endif
00298 
00299 int 
00300 TimeoutTimer::asIntMs() const
00301 {
00302    if (infinite())
00303    {
00304       return -1;
00305    }
00306 
00307    int rval;
00308    safeAssign(rval, calcSeconds() * 1000.0);
00309    return rval;
00310 }
00311 
00312 int
00313 TimeoutTimer::asIntMs(double maxSeconds) const
00314 {
00315    if (infinite() && maxSeconds == INFINITY)
00316    {
00317       return -1;
00318    }
00319 
00320    int rval;
00321    safeAssign(rval, calcSeconds(maxSeconds) * 1000.0);
00322    return rval;
00323 }
00324 
00325 #ifdef BLOCXX_HAVE_STRUCT_TIMESPEC
00326 ::timespec* 
00327 TimeoutTimer::asTimespec(::timespec& ts) const
00328 {
00329    const long NANOSECONDS_PER_MICROSECOND = 1000;
00330    const long NANOSECONDS_PER_MILLISECOND = 1000000;
00331    const long NANOSECONDS_PER_SECOND      = 1000000000;
00332 
00333    if (m_timeout.getType() == Timeout::E_ABSOLUTE)
00334    {
00335       // convert the timeout to a timespec
00336       DateTime timeoutTime = m_timeout.getAbsolute();
00337       if (timeoutTime > m_loopTime)
00338       {
00339          ts.tv_sec = timeoutTime.get();
00340          ts.tv_nsec = timeoutTime.getMicrosecond() * NANOSECONDS_PER_MICROSECOND;
00341       }
00342       else
00343       {
00344          // somehow we got past the timeout (a time warp?), so just return a 0 timespec.
00345          ts.tv_sec = 0;
00346          ts.tv_nsec = 0;
00347       }
00348    }
00349    else // relative
00350    {
00351       safeAssign(ts.tv_sec, m_start.get() + static_cast<double>(m_timeout.getRelative()));
00352       double dummy;
00353       safeAssign(ts.tv_nsec, 
00354          static_cast<double>(m_start.getMicrosecond()) * NANOSECONDS_PER_MICROSECOND +
00355          modf(m_timeout.getRelative(), &dummy) * NANOSECONDS_PER_SECOND);
00356 
00357       // handle any overflow because ts.tv_nsec has to be in the correct range.
00358       ts.tv_sec += ts.tv_nsec / NANOSECONDS_PER_SECOND;
00359       ts.tv_nsec = ts.tv_nsec % NANOSECONDS_PER_SECOND;
00360    }
00361 
00362    return &ts;
00363 }
00364 #endif
00365 
00366 DateTime
00367 TimeoutTimer::getCurrentTime() const
00368 {
00369    return DateTime::getCurrent();
00370 }
00371 
00372 } // end namespace BLOCXX_NAMESPACE
00373 
00374 
00375 
00376