diff options
-rw-r--r-- | src/mongo/db/curop.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/operation_context.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/prepare_conflict_tracker.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/service_context.cpp | 2 | ||||
-rw-r--r-- | src/mongo/util/concurrency/ticketholder.cpp | 4 | ||||
-rw-r--r-- | src/mongo/util/system_tick_source.cpp | 136 | ||||
-rw-r--r-- | src/mongo/util/system_tick_source.h | 23 | ||||
-rw-r--r-- | src/mongo/util/tick_source.h | 13 | ||||
-rw-r--r-- | src/mongo/util/tick_source_test.cpp | 72 | ||||
-rw-r--r-- | src/mongo/util/timer.cpp | 2 |
10 files changed, 76 insertions, 182 deletions
diff --git a/src/mongo/db/curop.cpp b/src/mongo/db/curop.cpp index 73ddcb6827e..9e6b89c1af4 100644 --- a/src/mongo/db/curop.cpp +++ b/src/mongo/db/curop.cpp @@ -278,7 +278,7 @@ void CurOp::setGenericCursor_inlock(GenericCursor gc) { void CurOp::_finishInit(OperationContext* opCtx, CurOpStack* stack) { _stack = stack; - _tickSource = SystemTickSource::get(); + _tickSource = globalSystemTickSource(); if (opCtx) { _stack->push(opCtx, this); diff --git a/src/mongo/db/operation_context.cpp b/src/mongo/db/operation_context.cpp index c17d9c2d7b3..24c92e19f6b 100644 --- a/src/mongo/db/operation_context.cpp +++ b/src/mongo/db/operation_context.cpp @@ -80,7 +80,7 @@ OperationContext::OperationContext(Client* client, OperationIdSlot&& opIdSlot) : _client(client), _opId(std::move(opIdSlot)), _elapsedTime(client ? client->getServiceContext()->getTickSource() - : SystemTickSource::get()) {} + : globalSystemTickSource()) {} OperationContext::~OperationContext() { releaseOperationKey(); diff --git a/src/mongo/db/prepare_conflict_tracker.cpp b/src/mongo/db/prepare_conflict_tracker.cpp index e8e031a1405..ac9c1b495fc 100644 --- a/src/mongo/db/prepare_conflict_tracker.cpp +++ b/src/mongo/db/prepare_conflict_tracker.cpp @@ -52,7 +52,7 @@ void PrepareConflictTracker::endPrepareConflict(OperationContext* opCtx) { auto curTick = tickSource->getTicks(); auto curConflictDuration = - tickSource->spanTo<Microseconds>(_prepareConflictStartTime, curTick); + tickSource->ticksTo<Microseconds>(curTick - _prepareConflictStartTime); _prepareConflictDuration.store(_prepareConflictDuration.load() + curConflictDuration); _prepareConflictStartTime = 0; diff --git a/src/mongo/db/service_context.cpp b/src/mongo/db/service_context.cpp index 013e3209f95..61e20dc82e2 100644 --- a/src/mongo/db/service_context.cpp +++ b/src/mongo/db/service_context.cpp @@ -100,7 +100,7 @@ void setGlobalServiceContext(ServiceContext::UniqueServiceContext&& serviceConte ServiceContext::ServiceContext() : _opIdRegistry(UniqueOperationIdRegistry::create()), - _tickSource(std::make_unique<SystemTickSource>()), + _tickSource(makeSystemTickSource()), _fastClockSource(std::make_unique<SystemClockSource>()), _preciseClockSource(std::make_unique<SystemClockSource>()) {} diff --git a/src/mongo/util/concurrency/ticketholder.cpp b/src/mongo/util/concurrency/ticketholder.cpp index a9c44467536..2e9e90eeec1 100644 --- a/src/mongo/util/concurrency/ticketholder.cpp +++ b/src/mongo/util/concurrency/ticketholder.cpp @@ -51,7 +51,7 @@ void updateQueueStatsOnRelease(ServiceContext* serviceContext, queueStats.totalFinishedProcessing.fetchAndAddRelaxed(1); auto startTime = admCtx->getStartProcessingTime(); auto tickSource = serviceContext->getTickSource(); - auto delta = tickSource->spanTo<Microseconds>(startTime, tickSource->getTicks()); + auto delta = tickSource->ticksTo<Microseconds>(tickSource->getTicks() - startTime); queueStats.totalTimeProcessingMicros.fetchAndAddRelaxed(delta.count()); } @@ -163,7 +163,7 @@ boost::optional<Ticket> TicketHolderWithQueueingStats::waitForTicketUntil(Operat auto currentWaitTime = tickSource->getTicks(); auto updateQueuedTime = [&]() { auto oldWaitTime = std::exchange(currentWaitTime, tickSource->getTicks()); - auto waitDelta = tickSource->spanTo<Microseconds>(oldWaitTime, currentWaitTime).count(); + auto waitDelta = tickSource->ticksTo<Microseconds>(currentWaitTime - oldWaitTime).count(); queueStats.totalTimeQueuedMicros.fetchAndAddRelaxed(waitDelta); }; queueStats.totalAddedQueue.fetchAndAddRelaxed(1); diff --git a/src/mongo/util/system_tick_source.cpp b/src/mongo/util/system_tick_source.cpp index ee5f24d4e83..293b072f079 100644 --- a/src/mongo/util/system_tick_source.cpp +++ b/src/mongo/util/system_tick_source.cpp @@ -27,132 +27,32 @@ * it in the license file. */ -#include "mongo/platform/basic.h" - #include "mongo/util/system_tick_source.h" -#include "mongo/config.h" - -#include <ctime> -#include <limits> -#if defined(MONGO_CONFIG_HAVE_HEADER_UNISTD_H) -#include <unistd.h> -#endif +#include <chrono> // NOLINT #include <memory> -#include "mongo/base/init.h" -#include "mongo/util/assert_util.h" #include "mongo/util/tick_source.h" -#include "mongo/util/time_support.h" namespace mongo { -namespace { - -const int64_t kMillisPerSecond = 1000; -const int64_t kMicrosPerSecond = 1000 * kMillisPerSecond; -const int64_t kNanosPerSecond = 1000 * kMicrosPerSecond; - -/** - * Internally, the timer counts platform-dependent ticks of some sort, and - * must then convert those ticks to microseconds and their ilk. This field - * stores the frequency of the platform-dependent counter. - * - * Define ticksPerSecond before init to ensure correct relative sequencing - * regardless of how it is initialized (static or dynamic). - */ -TickSource::Tick ticksPerSecond = kMicrosPerSecond; - -// "Generic" implementation for _timerNow. -TickSource::Tick _timerNowGeneric() { - return curTimeMicros64(); -} - -// Function pointer to timer implementation. -// Overridden in initTickSource() with better implementation where available. -TickSource::Tick (*_timerNow)() = &_timerNowGeneric; - -#if defined(_WIN32) - -/** - * Windows-specific implementation of the - * Timer class. Windows selects the best available timer, in its estimation, for - * measuring time at high resolution. This may be the HPET of the TSC on x86 systems, - * but is promised to be synchronized across processors, barring BIOS errors. - */ -TickSource::Tick timerNowWindows() { - LARGE_INTEGER i; - fassert(16161, QueryPerformanceCounter(&i)); - return i.QuadPart; -} - -void initTickSource() { - LARGE_INTEGER x; - bool ok = QueryPerformanceFrequency(&x); - verify(ok); - ticksPerSecond = x.QuadPart; - _timerNow = &timerNowWindows; -} - -#elif defined(MONGO_CONFIG_HAVE_POSIX_MONOTONIC_CLOCK) - -/** - * Implementation for timer on systems that support the - * POSIX clock API and CLOCK_MONOTONIC clock. - */ -TickSource::Tick timerNowPosixMonotonicClock() { - timespec the_time; - long long result; - - fassert(16160, !clock_gettime(CLOCK_MONOTONIC, &the_time)); - - // Safe for 292 years after the clock epoch, even if we switch to a signed time value. - // On Linux, the monotonic clock's epoch is the UNIX epoch. - result = static_cast<long long>(the_time.tv_sec); - result *= kNanosPerSecond; - result += static_cast<long long>(the_time.tv_nsec); - return result; -} - -void initTickSource() { - // If the monotonic clock is not available at runtime (sysconf() returns 0 or -1), - // do not override the generic implementation or modify ticksPerSecond. - if (sysconf(_SC_MONOTONIC_CLOCK) <= 0) { - return; - } - - ticksPerSecond = kNanosPerSecond; - _timerNow = &timerNowPosixMonotonicClock; - - // Make sure that the current time relative to the (unspecified) epoch isn't already too - // big to represent as a 64-bit count of nanoseconds. - long long maxSecs = std::numeric_limits<long long>::max() / kNanosPerSecond; - timespec the_time; - fassert(16162, !clock_gettime(CLOCK_MONOTONIC, &the_time)); - fassert(16163, static_cast<long long>(the_time.tv_sec) < maxSecs); -} -#else -void initTickSource() {} -#endif - -} // unnamed namespace - -MONGO_INITIALIZER(SystemTickSourceInit)(InitializerContext* context) { - initTickSource(); - SystemTickSource::get(); -} - -TickSource::Tick SystemTickSource::getTicks() { - return _timerNow(); -} - -TickSource::Tick SystemTickSource::getTicksPerSecond() { - return ticksPerSecond; -} - -SystemTickSource* SystemTickSource::get() { - static const auto globalSystemTickSource = std::make_unique<SystemTickSource>(); - return globalSystemTickSource.get(); +std::unique_ptr<TickSource> makeSystemTickSource() { + class Steady : public TickSource { + using C = std::chrono::steady_clock; // NOLINT + Tick getTicksPerSecond() override { + static_assert(C::period::num == 1, "Fractional frequency disallowed"); + return C::period::den; + } + Tick getTicks() override { + return C::now().time_since_epoch().count(); + } + }; + return std::make_unique<Steady>(); +} + +TickSource* globalSystemTickSource() { + static const auto p = makeSystemTickSource().release(); + return p; } } // namespace mongo diff --git a/src/mongo/util/system_tick_source.h b/src/mongo/util/system_tick_source.h index df59541fcee..360b38616f0 100644 --- a/src/mongo/util/system_tick_source.h +++ b/src/mongo/util/system_tick_source.h @@ -31,25 +31,14 @@ #include "mongo/util/tick_source.h" +#include <memory> + namespace mongo { -/** - * Tick source based on platform specific clock ticks. Should be of reasonably high - * performance. The maximum span measurable by the counter and convertible to microseconds - * is about 10 trillion ticks. As long as there are fewer than 100 ticks per nanosecond, - * timer durations of 2.5 years will be supported. Since a typical tick duration will be - * under 10 per nanosecond, if not below 1 per nanosecond, this should not be an issue. - */ -class SystemTickSource final : public TickSource { -public: - TickSource::Tick getTicks() override; +/** Tick source based on `std:::chrono::steady_clock`. Monotonic, cheap, and high-precision. */ +std::unique_ptr<TickSource> makeSystemTickSource(); - TickSource::Tick getTicksPerSecond() override; +/** Accesses a singleton instance made by `makeSystemTickSource`. Safe to call at any time. */ +TickSource* globalSystemTickSource(); - /** - * Gets the singleton instance of SystemTickSource. Should not be called before - * the global initializers are done. - */ - static SystemTickSource* get(); -}; } // namespace mongo diff --git a/src/mongo/util/tick_source.h b/src/mongo/util/tick_source.h index 5b0abc88aca..759a6354ed1 100644 --- a/src/mongo/util/tick_source.h +++ b/src/mongo/util/tick_source.h @@ -65,18 +65,5 @@ public: static_cast<double>(getTicksPerSecond()) * D::period::num / D::period::den; return D(static_cast<int64_t>(ticks / ticksPerD)); } - - /** - * Measures the length of the span from the start tick to the end tick and returns the result - * using duration type D. - * If the start tick is after (greater than) the end tick, returns a duration equivalent to 0 - * ticks. - * - * e.g. tickSource->spanTo<Milliseconds>(start, end); - */ - template <typename D> - D spanTo(Tick start, Tick end) { - return ticksTo<D>(std::max((end - start), Tick{0})); - } }; } // namespace mongo diff --git a/src/mongo/util/tick_source_test.cpp b/src/mongo/util/tick_source_test.cpp index 9cfd907677a..b0fd6e43cb3 100644 --- a/src/mongo/util/tick_source_test.cpp +++ b/src/mongo/util/tick_source_test.cpp @@ -27,10 +27,10 @@ * it in the license file. */ -#include "mongo/platform/basic.h" - +#include "mongo/unittest/assert_that.h" #include "mongo/unittest/unittest.h" -#include "mongo/util/clock_source_mock.h" +#include "mongo/util/duration.h" +#include "mongo/util/system_tick_source.h" #include "mongo/util/tick_source.h" #include "mongo/util/tick_source_mock.h" #include "mongo/util/time_support.h" @@ -38,35 +38,53 @@ namespace mongo { namespace { +namespace m = unittest::match; + +template <typename SourceDuration, typename OutDuration> +auto tickToDuration(int ticks) { + TickSourceMock<SourceDuration> ts; + ts.reset(ticks); + return ts.template ticksTo<OutDuration>(ts.getTicks()).count(); +} + TEST(TickSourceTest, TicksToDurationConversion) { - TickSourceMock<Seconds> tsSecs; - tsSecs.reset(1); - ASSERT_EQ(tsSecs.ticksTo<Seconds>(tsSecs.getTicks()).count(), 1); - ASSERT_EQ(tsSecs.ticksTo<Milliseconds>(tsSecs.getTicks()).count(), 1000); - ASSERT_EQ(tsSecs.ticksTo<Microseconds>(tsSecs.getTicks()).count(), 1000 * 1000); + ASSERT_EQ((tickToDuration<Seconds, Seconds>(1)), 1); + ASSERT_EQ((tickToDuration<Seconds, Milliseconds>(1)), 1'000); + ASSERT_EQ((tickToDuration<Seconds, Microseconds>(1)), 1'000'000); + ASSERT_EQ((tickToDuration<Milliseconds, Milliseconds>(1)), 1); + ASSERT_EQ((tickToDuration<Milliseconds, Microseconds>(1)), 1'000); + ASSERT_EQ((tickToDuration<Microseconds, Microseconds>(1)), 1); +} - TickSourceMock<Milliseconds> tsMillis; - tsMillis.reset(1); - ASSERT_EQ(tsMillis.ticksTo<Milliseconds>(tsMillis.getTicks()).count(), 1); - ASSERT_EQ(tsMillis.ticksTo<Microseconds>(tsMillis.getTicks()).count(), 1000); +TEST(SystemTickSourceTest, TicksPerSecond) { + ASSERT_EQ(makeSystemTickSource()->getTicksPerSecond(), 1'000'000'000); +} - TickSourceMock<Microseconds> tsMicros; - tsMicros.reset(1); - ASSERT_EQ(tsMicros.ticksTo<Microseconds>(tsMicros.getTicks()).count(), 1); +TEST(SystemTickSourceTest, GetTicks) { + auto ts = makeSystemTickSource(); + auto t0 = ts->getTicks(); + for (int reps = 20; reps--;) { + static constexpr Milliseconds delay{200}; + static constexpr Milliseconds err{20}; + sleepFor(delay); + auto t1 = ts->getTicks(); + ASSERT_THAT(ts->ticksTo<Milliseconds>(t1 - t0), + m::AllOf(m::Ge(delay - err), m::Le(delay + err))); + t0 = t1; + } } -TEST(TickSourceTest, SpansToDurationConversion) { - TickSourceMock<Seconds> tsSecs; - tsSecs.reset(0); - TickSource::Tick zero = tsSecs.getTicks(); - tsSecs.reset(10); - TickSource::Tick ten = tsSecs.getTicks(); - ASSERT_EQ(tsSecs.spanTo<Seconds>(zero, ten).count(), 10); - ASSERT_EQ(tsSecs.spanTo<Seconds>(ten, zero).count(), 0); - ASSERT_EQ(tsSecs.spanTo<Milliseconds>(zero, ten).count(), 10 * 1000); - ASSERT_EQ(tsSecs.spanTo<Milliseconds>(ten, zero).count(), 0); - ASSERT_EQ(tsSecs.spanTo<Microseconds>(zero, ten).count(), 10 * 1000 * 1000); - ASSERT_EQ(tsSecs.spanTo<Microseconds>(ten, zero).count(), 0); +TEST(SystemTickSourceTest, Monotonic) { + auto ts = makeSystemTickSource(); + auto t0 = ts->getTicks(); + std::vector<TickSource::Tick> samples; + samples.reserve(1'000'000); + do { + samples.clear(); + for (size_t i = 0; i < 1'000'000; ++i) + samples.push_back(ts->getTicks()); + ASSERT_TRUE(std::is_sorted(samples.begin(), samples.end())); + } while (ts->ticksTo<Milliseconds>(ts->getTicks() - t0) < Seconds{5}); } } // namespace } // namespace mongo diff --git a/src/mongo/util/timer.cpp b/src/mongo/util/timer.cpp index f69bfc4cbb8..7992cd0788c 100644 --- a/src/mongo/util/timer.cpp +++ b/src/mongo/util/timer.cpp @@ -42,7 +42,7 @@ const int64_t kMicrosPerSecond = 1000 * 1000; } // unnamed namespace -Timer::Timer() : Timer(SystemTickSource::get()) {} +Timer::Timer() : Timer(globalSystemTickSource()) {} Timer::Timer(TickSource* tickSource) : _tickSource(tickSource), |