LogCabin
Core/Time.cc
Go to the documentation of this file.
00001 /* Copyright (c) 2012 Stanford University
00002  * Copyright (c) 2014-2015 Diego Ongaro
00003  *
00004  * Permission to use, copy, modify, and distribute this software for any
00005  * purpose with or without fee is hereby granted, provided that the above
00006  * copyright notice and this permission notice appear in all copies.
00007  *
00008  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR(S) DISCLAIM ALL WARRANTIES
00009  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
00010  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL AUTHORS BE LIABLE FOR
00011  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
00012  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
00013  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
00014  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
00015  */
00016 
00017 #include <algorithm>
00018 #include <cassert>
00019 #include <cstdlib>
00020 #include <cstring>
00021 #include <functional>
00022 #include <stdexcept>
00023 
00024 #include "Core/Debug.h"
00025 #include "Core/Time.h"
00026 
00027 namespace LogCabin {
00028 namespace Core {
00029 namespace Time {
00030 
00031 CSystemClock::time_point
00032 CSystemClock::now()
00033 {
00034     struct timespec now;
00035     int r = clock_gettime(CLOCK_REALTIME, &now);
00036     if (r != 0) {
00037         PANIC("clock_gettime(CLOCK_REALTIME) failed: %s",
00038               strerror(errno));
00039     }
00040     return time_point(std::chrono::nanoseconds(
00041                 int64_t(now.tv_sec) * 1000 * 1000 * 1000 +
00042                 now.tv_nsec));
00043 }
00044 
00045 CSteadyClock::time_point
00046 CSteadyClock::now()
00047 {
00048     struct timespec now;
00049     int r = clock_gettime(STEADY_CLOCK_ID, &now);
00050     if (r != 0) {
00051         PANIC("clock_gettime(STEADY_CLOCK_ID=%d) failed: %s",
00052               STEADY_CLOCK_ID, strerror(errno));
00053     }
00054     return time_point(std::chrono::nanoseconds(
00055                 int64_t(now.tv_sec) * 1000 * 1000 * 1000 +
00056                 now.tv_nsec));
00057 }
00058 
00059 int64_t
00060 parseSignedDuration(const std::string& description)
00061 {
00062     const char* start = description.c_str();
00063     char* end = NULL;
00064     errno = 0;
00065     int64_t r = strtol(start, &end, 10);
00066     if (errno == ERANGE) {
00067         // pass
00068     } else if (errno != 0 || start == end) {
00069         throw std::runtime_error(
00070             std::string("Invalid time description: "
00071                         "could not parse number from ") + description);
00072     }
00073 
00074     std::string units = end;
00075     // The black magic is from https://stackoverflow.com/a/217605
00076     // trim whitespace at end of string
00077     units.erase(std::find_if(units.rbegin(), units.rend(),
00078                          std::not1(std::ptr_fun<int, int>(std::isspace)))
00079                 .base(),
00080             units.end());
00081     // trim whitespace at beginning of string
00082     units.erase(units.begin(),
00083             std::find_if(units.begin(), units.end(),
00084                          std::not1(std::ptr_fun<int, int>(std::isspace))));
00085 
00086     int64_t overflow;
00087     if (r < 0L)
00088        overflow = std::numeric_limits<int64_t>::min();
00089     else
00090        overflow = std::numeric_limits<int64_t>::max();
00091 
00092     if (units == "ns" ||
00093         units == "nanosecond" ||
00094         units == "nanoseconds") {
00095         // pass
00096     } else if (units == "us" ||
00097                units == "microsecond" ||
00098                units == "microseconds") {
00099         if (std::abs(r) <= 9223372036854775L)
00100             r *= 1000L;
00101         else
00102             r = overflow;
00103     } else if (units == "ms" ||
00104                units == "millisecond" ||
00105                units == "milliseconds") {
00106         if (std::abs(r) <= 9223372036854L)
00107             r *= 1000000L;
00108         else
00109             r = overflow;
00110     } else if (units == "s" ||
00111                units == "second" ||
00112                units == "seconds" ||
00113                units == "") {
00114         if (std::abs(r) <= 9223372036L)
00115             r *= 1000000000L;
00116         else
00117             r = overflow;
00118     } else if (units == "min" ||
00119                units == "minute" ||
00120                units == "minutes") {
00121         if (std::abs(r) <= 153722867L)
00122             r *= 1000000000L * 60L;
00123         else
00124             r = overflow;
00125     } else if (units == "h" ||
00126                units == "hr" ||
00127                units == "hour" ||
00128                units == "hours") {
00129         if (std::abs(r) <= 2562047L)
00130             r *= 1000000000L * 60L * 60L;
00131         else
00132             r = overflow;
00133     } else if (units == "d" ||
00134                units == "day" ||
00135                units == "days") {
00136         if (std::abs(r) <= 106751L)
00137             r *= 1000000000L * 60L * 60L * 24L;
00138         else
00139             r = overflow;
00140     } else if (units == "w" ||
00141                units == "wk" ||
00142                units == "week" ||
00143                units == "weeks") {
00144         if (std::abs(r) <= 15250L)
00145             r *= 1000000000L * 60L * 60L * 24L * 7L;
00146         else
00147             r = overflow;
00148     } else if (units == "mo" ||
00149                units == "month" ||
00150                units == "months") {
00151         // Months vary in length, so this is the average number of seconds in a
00152         // month. If someone is specifying durations in such large units, they
00153         // probably won't care.
00154         if (std::abs(r) <= 3507L)
00155             r *= 1000000000L * 2629800L;
00156         else
00157             r = overflow;
00158     } else if (units == "y" ||
00159                units == "yr" ||
00160                units == "year" ||
00161                units == "years") {
00162         // Years vary in length due to leap years, so this is the number of
00163         // seconds in a 365.25-day year. If someone is specifying durations in
00164         // such large units, they probably won't care.
00165         if (std::abs(r) <= 292L)
00166             r *= 1000000000L * 31557600L;
00167         else
00168             r = overflow;
00169     } else {
00170         throw std::runtime_error(
00171             std::string("Invalid time description: "
00172                         "could not parse units from ") + description);
00173     }
00174     return r;
00175 }
00176 
00177 uint64_t
00178 parseNonNegativeDuration(const std::string& description)
00179 {
00180     int64_t r = parseSignedDuration(description);
00181     if (r < 0) {
00182         throw std::runtime_error(Core::StringUtil::format(
00183                 "Invalid time description: '%s' is negative",
00184                 description.c_str()));
00185     }
00186     return static_cast<uint64_t>(r);
00187 }
00188 
00189 void
00190 sleep(SteadyClock::time_point wake)
00191 {
00192     struct timespec wakeSpec = makeTimeSpec(wake);
00193     if (wakeSpec.tv_sec < 0)
00194         return;
00195     int r = clock_nanosleep(STEADY_CLOCK_ID,
00196                             TIMER_ABSTIME,
00197                             &wakeSpec,
00198                             NULL);
00199     if (r != 0) {
00200         PANIC("clock_nanosleep(STEADY_CLOCK_ID=%d, %s) failed: %s",
00201               STEADY_CLOCK_ID,
00202               Core::StringUtil::toString(wake).c_str(),
00203               strerror(r));
00204     }
00205 }
00206 
00207 void
00208 sleep(std::chrono::nanoseconds duration)
00209 {
00210     typedef SteadyClock::time_point TimePoint;
00211     if (duration <= std::chrono::nanoseconds::zero())
00212         return;
00213     TimePoint now = SteadyClock::now();
00214     TimePoint wake = now + duration;
00215     if (wake < now) { // overflow
00216         wake = TimePoint::max();
00217     }
00218     Core::Time::sleep(wake);
00219 }
00220 
00221 SteadyTimeConverter::SteadyTimeConverter()
00222     : steadyNow(SteadyClock::now())
00223     , systemNow(SystemClock::now())
00224 {
00225 }
00226 
00227 SystemClock::time_point
00228 SteadyTimeConverter::convert(SteadyClock::time_point when)
00229 {
00230     // Note that this relies on signed integer wrapping, so -fwrapv or
00231     // -fno-strict-overflow must be on for correctness under optimizing
00232     // comiplers. The unit tests are pretty good at catching when this isn't
00233     // the case.
00234     std::chrono::nanoseconds diff = when - steadyNow;
00235     SystemClock::time_point then = systemNow + diff;
00236     if (when > steadyNow && then < systemNow) // overflow
00237         return SystemClock::time_point::max();
00238     return then;
00239 }
00240 
00241 int64_t
00242 SteadyTimeConverter::unixNanos(SteadyClock::time_point when)
00243 {
00244     return std::chrono::nanoseconds(
00245         convert(when).time_since_epoch()).count();
00246 }
00247 
00248 } // namespace LogCabin::Core::Time
00249 } // namespace LogCabin::Core
00250 } // namespace LogCabin
00251 
00252 namespace std {
00253 
00254 namespace {
00255 
00256 std::string
00257 padFraction(int64_t fraction, uint64_t digits)
00258 {
00259     if (fraction == 0)
00260         return "";
00261     if (fraction < 0)
00262         fraction *= -1;
00263     while (fraction % 1000 == 0 && digits >= 3) {
00264         digits -= 3;
00265         fraction /= 1000;
00266     }
00267     char ret[digits + 2];
00268     ret[0] = '.';
00269     ret[digits + 1] = '\0';
00270     for (uint64_t i = digits; i > 0; --i) {
00271         ret[i] = char('0' + (fraction % 10));
00272         fraction /= 10;
00273     }
00274     return ret;
00275 }
00276 
00277 } // namespace std::<anonymous>
00278 
00279 std::ostream&
00280 operator<<(std::ostream& os,
00281            const std::chrono::nanoseconds& duration)
00282 {
00283     int64_t nanos = duration.count();
00284     if (nanos / 1000000000L != 0) {
00285         int64_t whole    = nanos / 1000000000L;
00286         int64_t fraction = nanos % 1000000000L;
00287         os << whole << padFraction(fraction, 9) << " s";
00288     } else if (nanos / 1000000L != 0) {
00289         int64_t whole    = nanos / 1000000L;
00290         int64_t fraction = nanos % 1000000L;
00291         os << whole << padFraction(fraction, 6) << " ms";
00292     } else if (nanos / 1000L != 0) {
00293         int64_t whole    = nanos / 1000L;
00294         int64_t fraction = nanos % 1000L;
00295         os << whole << padFraction(fraction, 3) << " us";
00296     } else {
00297         os << nanos << " ns";
00298     }
00299     return os;
00300 }
00301 
00302 std::ostream&
00303 operator<<(std::ostream& os,
00304            const std::chrono::microseconds& duration)
00305 {
00306     return os << chrono::duration_cast<chrono::nanoseconds>(duration);
00307 }
00308 
00309 std::ostream&
00310 operator<<(std::ostream& os,
00311            const std::chrono::milliseconds& duration)
00312 {
00313     return os << chrono::duration_cast<chrono::nanoseconds>(duration);
00314 }
00315 
00316 std::ostream&
00317 operator<<(std::ostream& os,
00318            const std::chrono::seconds& duration)
00319 {
00320     return os << chrono::duration_cast<chrono::nanoseconds>(duration);
00321 }
00322 
00323 std::ostream&
00324 operator<<(std::ostream& os,
00325            const std::chrono::minutes& duration)
00326 {
00327     return os << chrono::duration_cast<chrono::nanoseconds>(duration);
00328 }
00329 
00330 std::ostream&
00331 operator<<(std::ostream& os,
00332            const std::chrono::hours& duration)
00333 {
00334     return os << chrono::duration_cast<chrono::nanoseconds>(duration);
00335 }
00336 
00337 } // namespace std
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines