diff --git a/lib/Basics/HybridLogicalClock.cpp b/lib/Basics/HybridLogicalClock.cpp index 4cb082a2a9..3c9566440b 100644 --- a/lib/Basics/HybridLogicalClock.cpp +++ b/lib/Basics/HybridLogicalClock.cpp @@ -23,23 +23,96 @@ #include "Basics/HybridLogicalClock.h" -char arangodb::basics::HybridLogicalClock::encodeTable[65] - = "-_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; +namespace { +template +constexpr DurationT maxDuration() noexcept { + return DurationT{std::numeric_limits::max()}; +} -signed char arangodb::basics::HybridLogicalClock::decodeTable[256] - = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 0 - 15 - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 16 - 31 - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 0,-1,-1, // 32 - 47 - 54,55,56,57,58,59,60,61,62,63,-1,-1,-1,-1,-1,-1, // 48 - 63 - -1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16, // 64 - 79 - 17,18,19,20,21,22,23,24,25,26,27,-1,-1,-1,-1, 1, // 80 - 95 - -1,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42, // 96 - 111 - 43,44,45,46,47,48,49,50,51,52,53,-1,-1,-1,-1,-1, // 112 - 127 - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 128 - 143 - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 144 - 159 - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 160 - 175 - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 176 - 191 - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 192 - 207 - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 208 - 223 - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 224 - 239 - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}; // 240 - 255 +template +constexpr DurationT absDuration(const DurationT d) noexcept { + return DurationT{(d.count() < 0) ? -d.count() : d.count()}; +} + +template +DstDurationT clockOffset( + const SrcDurationT tolerance = + std::chrono::duration_cast(std::chrono::nanoseconds{300}), + const int limit = 10000) { + if (std::is_same::value) { + return DstDurationT{}; + } + + auto itercnt = 0; + auto src_now = SrcTimePointT{}; + auto dst_now = DstTimePointT{}; + auto epsilon = maxDuration(); + do { + const auto src_before = SrcClockT::now(); + const auto dst_between = DstClockT::now(); + const auto src_after = SrcClockT::now(); + const auto src_diff = src_after - src_before; + const auto delta = absDuration(src_diff); + if (delta < epsilon) { + src_now = src_before + src_diff / 2; + dst_now = dst_between; + epsilon = delta; + } + if (++itercnt >= limit) { + break; + } + } while (epsilon > tolerance); + + auto diff1970 = SrcClockT::from_time_t(0) - src_now; + + return (dst_now + diff1970).time_since_epoch(); +} +} + +char arangodb::basics::HybridLogicalClock::encodeTable[65] = + "-_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + +signed char arangodb::basics::HybridLogicalClock::decodeTable[256] = { + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, // 0 - 15 + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, // 16 - 31 + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 0, -1, -1, // 32 - 47 + 54, 55, 56, 57, 58, 59, 60, 61, + 62, 63, -1, -1, -1, -1, -1, -1, // 48 - 63 + -1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16, // 64 - 79 + 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, -1, -1, -1, -1, 1, // 80 - 95 + -1, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, // 96 - 111 + 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, -1, -1, -1, -1, -1, // 112 - 127 + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, // 128 - 143 + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, // 144 - 159 + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, // 160 - 175 + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, // 176 - 191 + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, // 192 - 207 + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, // 208 - 223 + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, // 224 - 239 + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1}; // 240 - 255 + +uint64_t arangodb::basics::HybridLogicalClock::computeOffset1970() { + auto diff = clockOffset(); + + return std::chrono::duration_cast(diff).count(); +} diff --git a/lib/Basics/HybridLogicalClock.h b/lib/Basics/HybridLogicalClock.h index 022d8ad558..e4f99e3dbf 100644 --- a/lib/Basics/HybridLogicalClock.h +++ b/lib/Basics/HybridLogicalClock.h @@ -32,12 +32,16 @@ namespace arangodb { namespace basics { class HybridLogicalClock { - std::chrono::high_resolution_clock clock; - std::atomic lastTimeStamp; + public: + typedef std::chrono::high_resolution_clock ClockT; + + private: + ClockT _clock; + std::atomic _lastTimeStamp; + uint64_t _offset1970; public: - HybridLogicalClock() : lastTimeStamp(0) { - } + HybridLogicalClock() : _lastTimeStamp(0), _offset1970(computeOffset1970()) {} HybridLogicalClock(HybridLogicalClock const& other) = delete; HybridLogicalClock(HybridLogicalClock&& other) = delete; HybridLogicalClock& operator=(HybridLogicalClock const& other) = delete; @@ -48,13 +52,15 @@ class HybridLogicalClock { uint64_t newTimeStamp; do { uint64_t physical = getPhysicalTime(); - oldTimeStamp = lastTimeStamp.load(std::memory_order_relaxed); + oldTimeStamp = _lastTimeStamp.load(std::memory_order_relaxed); uint64_t oldTime = extractTime(oldTimeStamp); - newTimeStamp = (physical <= oldTime) ? - assembleTimeStamp(oldTime, extractCount(oldTimeStamp)+1) : - assembleTimeStamp(physical, 0); - } while (!lastTimeStamp.compare_exchange_weak(oldTimeStamp, newTimeStamp, - std::memory_order_release, std::memory_order_relaxed)); + newTimeStamp = + (physical <= oldTime) + ? assembleTimeStamp(oldTime, extractCount(oldTimeStamp) + 1) + : assembleTimeStamp(physical, 0); + } while (!_lastTimeStamp.compare_exchange_weak(oldTimeStamp, newTimeStamp, + std::memory_order_release, + std::memory_order_relaxed)); return newTimeStamp; } @@ -64,17 +70,18 @@ class HybridLogicalClock { uint64_t newTimeStamp; do { uint64_t physical = getPhysicalTime(); - oldTimeStamp = lastTimeStamp.load(std::memory_order_relaxed); + oldTimeStamp = _lastTimeStamp.load(std::memory_order_relaxed); uint64_t oldTime = extractTime(oldTimeStamp); uint64_t recTime = extractTime(receivedTimeStamp); - uint64_t newTime = (std::max)((std::max)(oldTime, physical),recTime); + uint64_t newTime = (std::max)((std::max)(oldTime, physical), recTime); // Note that this implies newTime >= oldTime and newTime >= recTime uint64_t newCount; if (newTime == oldTime) { if (newTime == recTime) { // all three identical newCount = (std::max)(extractCount(oldTimeStamp), - extractCount(receivedTimeStamp))+1; + extractCount(receivedTimeStamp)) + + 1; } else { // this means recTime < newTime newCount = extractCount(oldTimeStamp) + 1; @@ -88,8 +95,9 @@ class HybridLogicalClock { } } newTimeStamp = assembleTimeStamp(newTime, newCount); - } while (!lastTimeStamp.compare_exchange_weak(oldTimeStamp, newTimeStamp, - std::memory_order_release, std::memory_order_relaxed)); + } while (!_lastTimeStamp.compare_exchange_weak(oldTimeStamp, newTimeStamp, + std::memory_order_release, + std::memory_order_relaxed)); return newTimeStamp; } @@ -100,7 +108,7 @@ class HybridLogicalClock { r[--pos] = encodeTable[static_cast(t & 0x3ful)]; t >>= 6; } - return r.substr(pos, 11-pos); + return r.substr(pos, 11 - pos); } static uint64_t decodeTimeStamp(std::string const& s) { @@ -137,21 +145,22 @@ class HybridLogicalClock { } private: - // Helper to get the physical time in milliseconds since the epoch: + // helper to compute the offset between epoch and 1970 + uint64_t computeOffset1970(); + + // helper to get the physical time in milliseconds since the epoch: uint64_t getPhysicalTime() { - auto now = clock.now(); + auto now = _clock.now(); uint64_t ms = std::chrono::duration_cast( - now.time_since_epoch()).count(); + now.time_since_epoch()) + .count() - + _offset1970; return ms; } - static uint64_t extractTime(uint64_t t) { - return t >> 20; - } + static uint64_t extractTime(uint64_t t) { return t >> 20; } - static uint64_t extractCount(uint64_t t) { - return t & 0xfffffUL; - } + static uint64_t extractCount(uint64_t t) { return t & 0xfffffUL; } static uint64_t assembleTimeStamp(uint64_t time, uint64_t count) { return (time << 20) + count; @@ -160,10 +169,8 @@ class HybridLogicalClock { static char encodeTable[65]; static signed char decodeTable[256]; - +}; +}; }; -}; // namespace basics -}; // namespace arangodb - #endif