From 5ab482b114b9872530b297e30bf1dde404848dbe Mon Sep 17 00:00:00 2001 From: Christopher Dykes Date: Tue, 19 Jul 2016 15:58:01 -0700 Subject: [PATCH] Wrappers folly::chrono::clock_gettime and clock_gettime_ns 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 | 89 +++++++++++++++++++++++++ folly/ClockGettimeWrappers.h | 29 ++++++++ folly/Makefile.am | 2 + folly/test/ClockGettimeWrappersTest.cpp | 54 +++++++++++++++ folly/test/Makefile.am | 4 ++ 5 files changed, 178 insertions(+) create mode 100644 folly/ClockGettimeWrappers.cpp create mode 100644 folly/ClockGettimeWrappers.h create mode 100644 folly/test/ClockGettimeWrappersTest.cpp diff --git a/folly/ClockGettimeWrappers.cpp b/folly/ClockGettimeWrappers.cpp new file mode 100644 index 00000000..24247d2d --- /dev/null +++ b/folly/ClockGettimeWrappers.cpp @@ -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 +#include +#include + +#include + +#include + +#ifndef _WIN32 +#define _GNU_SOURCE 1 +#include +#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 index 00000000..ee4eb06c --- /dev/null +++ b/folly/ClockGettimeWrappers.h @@ -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 + +#include + +namespace folly { +namespace chrono { + +extern int (*clock_gettime)(clockid_t, timespec* ts); +extern int64_t (*clock_gettime_ns)(clockid_t); +} +} diff --git a/folly/Makefile.am b/folly/Makefile.am index 1352e2be..3be134e0 100644 --- a/folly/Makefile.am +++ b/folly/Makefile.am @@ -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 index 00000000..8516d6d9 --- /dev/null +++ b/folly/test/ClockGettimeWrappersTest.cpp @@ -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 + +#include + +#include + +#include + +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 */ diff --git a/folly/test/Makefile.am b/folly/test/Makefile.am index d11b13d6..84ae12a5 100644 --- a/folly/test/Makefile.am +++ b/folly/test/Makefile.am @@ -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 -- 2.34.1