summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBilly Donahue <billy.donahue@mongodb.com>2022-10-31 17:35:36 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2022-10-31 19:04:41 +0000
commit3fffc18136febd871ef8be886c6a4d961a98ec43 (patch)
tree4095ccbd57f9efe5774c8d58bfc9603db06c744d
parentd0c05f39f0b73283bf909bdb2c6f0a0f55d223de (diff)
downloadmongo-3fffc18136febd871ef8be886c6a4d961a98ec43.tar.gz
SERVER-70941 system TickSource via std::chrono::steady_clock
-rw-r--r--src/mongo/db/curop.cpp2
-rw-r--r--src/mongo/db/operation_context.cpp2
-rw-r--r--src/mongo/db/prepare_conflict_tracker.cpp2
-rw-r--r--src/mongo/db/service_context.cpp2
-rw-r--r--src/mongo/util/concurrency/ticketholder.cpp4
-rw-r--r--src/mongo/util/system_tick_source.cpp136
-rw-r--r--src/mongo/util/system_tick_source.h23
-rw-r--r--src/mongo/util/tick_source.h13
-rw-r--r--src/mongo/util/tick_source_test.cpp72
-rw-r--r--src/mongo/util/timer.cpp2
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),