Wrappers folly::chrono::clock_gettime and clock_gettime_ns
authorChristopher Dykes <cdykes@fb.com>
Tue, 19 Jul 2016 22:58:01 +0000 (15:58 -0700)
committerFacebook Github Bot 8 <facebook-github-bot-8-bot@fb.com>
Tue, 19 Jul 2016 23:08:29 +0000 (16:08 -0700)
Summary:
On Linux hosts, the fast path to get the current time in
nanoseconds without doing a syscall to the kernel is available via the
VDSO kernel-runtime interface.

In this diff, I:

1. Expose portability wrappers `folly::chrono::clock_gettime()` and
`folly::chrono::clock_gettime_ns()`
2. Implement a VDSO wrapper on Linux hosts to implement those without
a round-trip to the kernel

Depends On D3418054

Reviewed By: bmaurer

Differential Revision: D3418087

fbshipit-source-id: 3fb99f0dd946f19ba29d0d52a1038dad3556bafd

folly/ClockGettimeWrappers.cpp [new file with mode: 0644]
folly/ClockGettimeWrappers.h [new file with mode: 0644]
folly/Makefile.am
folly/test/ClockGettimeWrappersTest.cpp [new file with mode: 0644]
folly/test/Makefile.am

diff --git a/folly/ClockGettimeWrappers.cpp b/folly/ClockGettimeWrappers.cpp
new file mode 100644 (file)
index 0000000..24247d2
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <folly/ClockGettimeWrappers.h>
+#include <folly/Likely.h>
+#include <folly/portability/Time.h>
+
+#include <chrono>
+
+#include <time.h>
+
+#ifndef _WIN32
+#define _GNU_SOURCE 1
+#include <dlfcn.h>
+#endif
+
+namespace folly {
+namespace chrono {
+
+static int64_t clock_gettime_ns_fallback(clockid_t clock) {
+  struct timespec ts;
+  int r = clock_gettime(clock, &ts);
+  if (UNLIKELY(r != 0)) {
+    // Mimic what __clock_gettime_ns does (even though this can be a legit
+    // value).
+    return -1;
+  }
+  std::chrono::nanoseconds result =
+      std::chrono::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec);
+  return result.count();
+}
+
+// Initialize with default behavior, which we might override on Linux hosts
+// with VDSO support.
+int (*clock_gettime)(clockid_t, timespec* ts) = &::clock_gettime;
+int64_t (*clock_gettime_ns)(clockid_t) = &clock_gettime_ns_fallback;
+
+#ifdef __linux__
+
+namespace {
+
+struct VdsoInitializer {
+  VdsoInitializer() {
+    m_handle = dlopen("linux-vdso.so.1", RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD);
+    if (!m_handle) {
+      return;
+    }
+
+    void* p = dlsym(m_handle, "__vdso_clock_gettime");
+    if (p) {
+      folly::chrono::clock_gettime = (int (*)(clockid_t, timespec*))p;
+    }
+    p = dlsym(m_handle, "__vdso_clock_gettime_ns");
+    if (p) {
+      folly::chrono::clock_gettime_ns = (int64_t(*)(clockid_t))p;
+    }
+  }
+
+  ~VdsoInitializer() {
+    if (m_handle) {
+      clock_gettime = &::clock_gettime;
+      clock_gettime_ns = &clock_gettime_ns_fallback;
+      dlclose(m_handle);
+    }
+  }
+
+ private:
+  void* m_handle;
+};
+
+static const VdsoInitializer vdso_initializer;
+}
+
+#endif
+}
+}
diff --git a/folly/ClockGettimeWrappers.h b/folly/ClockGettimeWrappers.h
new file mode 100644 (file)
index 0000000..ee4eb06
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <folly/portability/Time.h>
+
+#include <time.h>
+
+namespace folly {
+namespace chrono {
+
+extern int (*clock_gettime)(clockid_t, timespec* ts);
+extern int64_t (*clock_gettime_ns)(clockid_t);
+}
+}
index 1352e2bef8b0e6ccf1a022f22a89e07b7ee26169..3be134e04d9306a8f143cc10ce02c4b3ba4cb0a9 100644 (file)
@@ -41,6 +41,7 @@ nobase_follyinclude_HEADERS = \
        Bits.h \
        CallOnce.h \
        Checksum.h \
+       ClockGettimeWrappers.h \
        ConcurrentSkipList.h \
        ConcurrentSkipList-inl.h \
        ConditionallyExistent.h \
@@ -368,6 +369,7 @@ libfollybase_la_SOURCES = \
 libfolly_la_SOURCES = \
        Bits.cpp \
        Checksum.cpp \
+       ClockGettimeWrappers.cpp \
        detail/CacheLocality.cpp \
        detail/IPAddress.cpp \
        dynamic.cpp \
diff --git a/folly/test/ClockGettimeWrappersTest.cpp b/folly/test/ClockGettimeWrappersTest.cpp
new file mode 100644 (file)
index 0000000..8516d6d
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <folly/ClockGettimeWrappers.h>
+
+#include <folly/test/TestUtils.h>
+
+#include <chrono>
+
+#include <gtest/gtest.h>
+
+static constexpr auto kAcceptableDeltaSecs = std::chrono::seconds(120);
+
+using folly::test::AreWithinSecs;
+
+#ifdef CLOCK_REALTIME
+
+TEST(ClockGettimeWrappers, clockGettimeWrapperIsWithin120SecsOfSystemClock) {
+  struct timespec ts;
+  auto ret = folly::chrono::clock_gettime(CLOCK_REALTIME, &ts);
+  ASSERT_EQ(0, ret);
+
+  auto gettimeResult =
+      std::chrono::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec);
+  auto stdChronoSystemClockNow =
+      std::chrono::system_clock::now().time_since_epoch();
+  ASSERT_TRUE(AreWithinSecs(
+      gettimeResult, stdChronoSystemClockNow, kAcceptableDeltaSecs));
+}
+
+TEST(ClockGettimeWrappers, clockGettimeNsWrapperIsWithin120SecsOfSystemClock) {
+  auto now_ns = folly::chrono::clock_gettime_ns(CLOCK_REALTIME);
+  auto stdChronoSystemClockNow =
+      std::chrono::system_clock::now().time_since_epoch();
+  ASSERT_TRUE(AreWithinSecs(
+      std::chrono::nanoseconds(now_ns),
+      stdChronoSystemClockNow,
+      kAcceptableDeltaSecs));
+}
+
+#endif /* CLOCK_REALTIME */
index d11b13d68b09551473f00f6c981d98a3cedf4094..84ae12a53be1b7f1150b0b313927564593df91a8 100644 (file)
@@ -229,6 +229,10 @@ indestructible_test_SOURCES = IndestructibleTest.cpp
 indestructible_test_LDADD = libfollytestmain.la
 TESTS += indestructible_test
 
+portability_clock_gettime_wrappers_test_SOURCES = ClockGettimeWrappersTest.cpp
+portability_clock_gettime_wrappers_test_LDADD = libfollytestmain.la
+TESTS += portability_clock_gettime_wrappers_test
+
 portability_time_test_SOURCES = ../portability/test/TimeTest.cpp
 portability_time_test_LDADD = libfollytestmain.la
 TESTS += portability_time_test