capnproto

FORK: Cap'n Proto serialization/RPC system - core tools and C++ library
git clone https://git.neptards.moe/neptards/capnproto.git
Log | Files | Refs | README | LICENSE

time.c++ (9609B)


      1 // Copyright (c) 2014 Google Inc. (contributed by Remy Blank <rblank@google.com>)
      2 // Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
      3 // Licensed under the MIT License:
      4 //
      5 // Permission is hereby granted, free of charge, to any person obtaining a copy
      6 // of this software and associated documentation files (the "Software"), to deal
      7 // in the Software without restriction, including without limitation the rights
      8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      9 // copies of the Software, and to permit persons to whom the Software is
     10 // furnished to do so, subject to the following conditions:
     11 //
     12 // The above copyright notice and this permission notice shall be included in
     13 // all copies or substantial portions of the Software.
     14 //
     15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     21 // THE SOFTWARE.
     22 
     23 #if _WIN32
     24 #include "win32-api-version.h"
     25 #endif
     26 
     27 #include "time.h"
     28 #include "debug.h"
     29 #include <set>
     30 
     31 #if _WIN32
     32 #include <windows.h>
     33 #else
     34 #include <time.h>
     35 #endif
     36 
     37 namespace kj {
     38 
     39 const Clock& nullClock() {
     40   class NullClock final: public Clock {
     41   public:
     42     Date now() const override { return UNIX_EPOCH; }
     43   };
     44   static KJ_CONSTEXPR(const) NullClock NULL_CLOCK = NullClock();
     45   return NULL_CLOCK;
     46 }
     47 
     48 #if _WIN32
     49 
     50 namespace {
     51 
     52 static constexpr int64_t WIN32_EPOCH_OFFSET = 116444736000000000ull;
     53 // Number of 100ns intervals from Jan 1, 1601 to Jan 1, 1970.
     54 
     55 static Date toKjDate(FILETIME t) {
     56   int64_t value = (static_cast<uint64_t>(t.dwHighDateTime) << 32) | t.dwLowDateTime;
     57   return (value - WIN32_EPOCH_OFFSET) * (100 * kj::NANOSECONDS) + UNIX_EPOCH;
     58 }
     59 
     60 class Win32CoarseClock: public Clock {
     61 public:
     62   Date now() const override {
     63     FILETIME ft;
     64     GetSystemTimeAsFileTime(&ft);
     65     return toKjDate(ft);
     66   }
     67 };
     68 
     69 class Win32PreciseClock: public Clock {
     70   typedef VOID WINAPI GetSystemTimePreciseAsFileTimeFunc(LPFILETIME);
     71 public:
     72   Date now() const override {
     73     static GetSystemTimePreciseAsFileTimeFunc* const getSystemTimePreciseAsFileTimePtr =
     74         getGetSystemTimePreciseAsFileTime();
     75     FILETIME ft;
     76     if (getSystemTimePreciseAsFileTimePtr == nullptr) {
     77       // We can't use QueryPerformanceCounter() to get any more precision because we have no way
     78       // of knowing when the calendar clock jumps. So I guess we're stuck.
     79       GetSystemTimeAsFileTime(&ft);
     80     } else {
     81       getSystemTimePreciseAsFileTimePtr(&ft);
     82     }
     83     return toKjDate(ft);
     84   }
     85 
     86 private:
     87   static GetSystemTimePreciseAsFileTimeFunc* getGetSystemTimePreciseAsFileTime() {
     88     // Dynamically look up the function GetSystemTimePreciseAsFileTimeFunc(). This was only
     89     // introduced as of Windows 8, so it might be missing.
     90 #if __GNUC__ && !__clang__ && __GNUC__ >= 8
     91 // GCC 8 warns that our reinterpret_cast of a function pointer below is casting between
     92 // incompatible types. Yes, GCC, we know that. This is the nature of GetProcAddress(); it returns
     93 // everything as `long long int (*)()` and we have to cast to the actual type.
     94 #pragma GCC diagnostic push
     95 #pragma GCC diagnostic ignored "-Wcast-function-type"
     96 #endif
     97     return reinterpret_cast<GetSystemTimePreciseAsFileTimeFunc*>(GetProcAddress(
     98       GetModuleHandleA("kernel32.dll"),
     99       "GetSystemTimePreciseAsFileTime"));
    100   }
    101 };
    102 
    103 class Win32CoarseMonotonicClock: public MonotonicClock {
    104 public:
    105   TimePoint now() const override {
    106     return kj::origin<TimePoint>() + GetTickCount64() * kj::MILLISECONDS;
    107   }
    108 };
    109 
    110 class Win32PreciseMonotonicClock: public MonotonicClock {
    111   // Precise clock implemented using QueryPerformanceCounter().
    112   //
    113   // TODO(someday): Windows 10 has QueryUnbiasedInterruptTime() and
    114   //   QueryUnbiasedInterruptTimePrecise(), a new API for monotonic timing that isn't as difficult.
    115   //   Is there any benefit to dynamically checking for these and using them if available?
    116 
    117 public:
    118   TimePoint now() const override {
    119     static const QpcProperties props;
    120 
    121     LARGE_INTEGER now;
    122     QueryPerformanceCounter(&now);
    123     uint64_t adjusted = now.QuadPart - props.origin;
    124     uint64_t ns = mulDiv64(adjusted, 1'000'000'000, props.frequency);
    125     return kj::origin<TimePoint>() + ns * kj::NANOSECONDS;
    126   }
    127 
    128 private:
    129   struct QpcProperties {
    130     uint64_t origin;
    131     // What QueryPerformanceCounter() would have returned at the time when GetTickCount64() returned
    132     // zero. Used to ensure that the coarse and precise timers return similar values.
    133 
    134     uint64_t frequency;
    135     // From QueryPerformanceFrequency().
    136 
    137     QpcProperties() {
    138       LARGE_INTEGER now, freqLi;
    139       uint64_t ticks = GetTickCount64();
    140       QueryPerformanceCounter(&now);
    141 
    142       QueryPerformanceFrequency(&freqLi);
    143       frequency = freqLi.QuadPart;
    144 
    145       // Convert the millisecond tick count into performance counter ticks.
    146       uint64_t ticksAsQpc = mulDiv64(ticks, freqLi.QuadPart, 1000);
    147 
    148       origin = now.QuadPart - ticksAsQpc;
    149     }
    150   };
    151 
    152   static inline uint64_t mulDiv64(uint64_t value, uint64_t numer, uint64_t denom) {
    153     // Inspired by:
    154     //   https://github.com/rust-lang/rust/pull/22788/files#diff-24f054cd23f65af3b574c6ce8aa5a837R54
    155     // Computes (value*numer)/denom without overflow, as long as both
    156     // (numer*denom) and the overall result fit into 64 bits.
    157     uint64_t q = value / denom;
    158     uint64_t r = value % denom;
    159     return q * numer + r * numer / denom;
    160   }
    161 };
    162 
    163 }  // namespace
    164 
    165 const Clock& systemCoarseCalendarClock() {
    166   static constexpr Win32CoarseClock clock;
    167   return clock;
    168 }
    169 const Clock& systemPreciseCalendarClock() {
    170   static constexpr Win32PreciseClock clock;
    171   return clock;
    172 }
    173 
    174 const MonotonicClock& systemCoarseMonotonicClock() {
    175   static constexpr Win32CoarseMonotonicClock clock;
    176   return clock;
    177 }
    178 const MonotonicClock& systemPreciseMonotonicClock() {
    179   static constexpr Win32PreciseMonotonicClock clock;
    180   return clock;
    181 }
    182 
    183 #else
    184 
    185 namespace {
    186 
    187 class PosixClock: public Clock {
    188 public:
    189   constexpr PosixClock(clockid_t clockId): clockId(clockId) {}
    190 
    191   Date now() const override {
    192     struct timespec ts;
    193     KJ_SYSCALL(clock_gettime(clockId, &ts));
    194     return UNIX_EPOCH + ts.tv_sec * kj::SECONDS + ts.tv_nsec * kj::NANOSECONDS;
    195   }
    196 
    197 private:
    198   clockid_t clockId;
    199 };
    200 
    201 class PosixMonotonicClock: public MonotonicClock {
    202 public:
    203   constexpr PosixMonotonicClock(clockid_t clockId): clockId(clockId) {}
    204 
    205   TimePoint now() const override {
    206     struct timespec ts;
    207     KJ_SYSCALL(clock_gettime(clockId, &ts));
    208     return kj::origin<TimePoint>() + ts.tv_sec * kj::SECONDS + ts.tv_nsec * kj::NANOSECONDS;
    209   }
    210 
    211 private:
    212   clockid_t clockId;
    213 };
    214 
    215 }  // namespace
    216 
    217 // FreeBSD has "_PRECISE", but Linux just defaults to precise.
    218 #ifndef CLOCK_REALTIME_PRECISE
    219 #define CLOCK_REALTIME_PRECISE CLOCK_REALTIME
    220 #endif
    221 
    222 #ifndef CLOCK_MONOTONIC_PRECISE
    223 #define CLOCK_MONOTONIC_PRECISE CLOCK_MONOTONIC
    224 #endif
    225 
    226 // FreeBSD has "_FAST", Linux has "_COARSE".
    227 // MacOS has an "_APPROX" but only for CLOCK_MONOTONIC_RAW, which isn't helpful.
    228 #ifndef CLOCK_REALTIME_COARSE
    229 #ifdef CLOCK_REALTIME_FAST
    230 #define CLOCK_REALTIME_COARSE CLOCK_REALTIME_FAST
    231 #else
    232 #define CLOCK_REALTIME_COARSE CLOCK_REALTIME
    233 #endif
    234 #endif
    235 
    236 #ifndef CLOCK_MONOTONIC_COARSE
    237 #ifdef CLOCK_MONOTONIC_FAST
    238 #define CLOCK_MONOTONIC_COARSE CLOCK_MONOTONIC_FAST
    239 #else
    240 #define CLOCK_MONOTONIC_COARSE CLOCK_MONOTONIC
    241 #endif
    242 #endif
    243 
    244 const Clock& systemCoarseCalendarClock() {
    245   static constexpr PosixClock clock(CLOCK_REALTIME_COARSE);
    246   return clock;
    247 }
    248 const Clock& systemPreciseCalendarClock() {
    249   static constexpr PosixClock clock(CLOCK_REALTIME_PRECISE);
    250   return clock;
    251 }
    252 
    253 const MonotonicClock& systemCoarseMonotonicClock() {
    254   static constexpr PosixMonotonicClock clock(CLOCK_MONOTONIC_COARSE);
    255   return clock;
    256 }
    257 const MonotonicClock& systemPreciseMonotonicClock() {
    258   static constexpr PosixMonotonicClock clock(CLOCK_MONOTONIC_PRECISE);
    259   return clock;
    260 }
    261 
    262 #endif
    263 
    264 CappedArray<char, sizeof(int64_t) * 3 + 2 + 4> KJ_STRINGIFY(TimePoint t) {
    265   return kj::toCharSequence(t - kj::origin<TimePoint>());
    266 }
    267 CappedArray<char, sizeof(int64_t) * 3 + 2 + 4> KJ_STRINGIFY(Date d) {
    268   return kj::toCharSequence(d - UNIX_EPOCH);
    269 }
    270 CappedArray<char, sizeof(int64_t) * 3 + 2 + 4> KJ_STRINGIFY(Duration d) {
    271   auto digits = kj::toCharSequence(d / kj::NANOSECONDS);
    272   ArrayPtr<char> arr = digits;
    273 
    274   size_t point;
    275   kj::StringPtr suffix;
    276   kj::Duration unit;
    277   if (digits.size() > 9) {
    278     point = arr.size() - 9;
    279     suffix = "s";
    280     unit = kj::SECONDS;
    281   } else if (digits.size() > 6) {
    282     point = arr.size() - 6;
    283     suffix = "ms";
    284     unit = kj::MILLISECONDS;
    285   } else if (digits.size() > 3) {
    286     point = arr.size() - 3;
    287     suffix = "μs";
    288     unit = kj::MICROSECONDS;
    289   } else {
    290     point = arr.size();
    291     suffix = "ns";
    292     unit = kj::NANOSECONDS;
    293   }
    294 
    295   CappedArray<char, sizeof(int64_t) * 3 + 2 + 4> result;
    296   char *end;
    297   if (d % unit == 0 * kj::NANOSECONDS) {
    298     end = _::fillLimited(result.begin(), result.end(), arr.slice(0, point), suffix);
    299   } else {
    300     while (arr.back() == '0') {
    301       arr = arr.slice(0, arr.size() - 1);
    302     }
    303     KJ_DASSERT(arr.size() > point);
    304     end = _::fillLimited(result.begin(), result.end(), arr.slice(0, point), "."_kj,
    305         arr.slice(point, arr.size()), suffix);
    306   }
    307   result.setSize(end - result.begin());
    308   return result;
    309 }
    310 
    311 }  // namespace kj