blocxx
|
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