From: Christopher Dykes Date: Wed, 23 Mar 2016 17:10:36 +0000 (-0700) Subject: Move the clock details over to the Time.h portability header X-Git-Tag: 2016.07.26~416 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=dec1472761d208dd236d547777cba59ec27bdde0;p=folly.git Move the clock details over to the Time.h portability header Summary: Because things belong places. Reviewed By: mzlee Differential Revision: D3077737 fb-gh-sync-id: 966f37df722ae737481d776ff1c9b0d5132b7a58 shipit-source-id: 966f37df722ae737481d776ff1c9b0d5132b7a58 --- diff --git a/folly/Makefile.am b/folly/Makefile.am index f9906987..83d623a8 100644 --- a/folly/Makefile.am +++ b/folly/Makefile.am @@ -53,7 +53,6 @@ nobase_follyinclude_HEADERS = \ detail/BitsDetail.h \ detail/CacheLocality.h \ detail/ChecksumDetail.h \ - detail/Clock.h \ detail/DiscriminatedPtrDetail.h \ detail/ExceptionWrapper.h \ detail/FileUtilDetail.h \ @@ -456,10 +455,6 @@ libfolly_la_SOURCES += \ experimental/io/HugePages.cpp endif -if !HAVE_LINUX -libfollybase_la_SOURCES += detail/Clock.cpp -endif - if !HAVE_WEAK_SYMBOLS libfollybase_la_SOURCES += detail/MallocImpl.cpp endif diff --git a/folly/Portability.h b/folly/Portability.h index 363fe304..1da837e8 100644 --- a/folly/Portability.h +++ b/folly/Portability.h @@ -246,14 +246,6 @@ namespace std { typedef ::max_align_t max_align_t; } # define FOLLY_GLIBCXX_NAMESPACE_CXX11_END #endif -// Some platforms lack clock_gettime(2) and clock_getres(2). Inject our own -// versions of these into the global namespace. -#if FOLLY_HAVE_CLOCK_GETTIME -#include -#else -#include -#endif - // Provide our own std::__throw_* wrappers for platforms that don't have them #if FOLLY_HAVE_BITS_FUNCTEXCEPT_H #include diff --git a/folly/detail/Clock.cpp b/folly/detail/Clock.cpp deleted file mode 100644 index d861d88f..00000000 --- a/folly/detail/Clock.cpp +++ /dev/null @@ -1,197 +0,0 @@ -/* - * 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 - -#if __MACH__ -#include -#include - -namespace { - -const mach_timebase_info_data_t* tbInfo() { - static auto info = [] { - static mach_timebase_info_data_t info; - return (mach_timebase_info(&info) == KERN_SUCCESS) ? &info : nullptr; - }(); - return info; -}; - -} // anonymous namespace - -int clock_gettime(clockid_t clk_id, struct timespec* ts) { - auto tb_info = tbInfo(); - if (tb_info == nullptr) { - errno = EINVAL; - return -1; - } - - uint64_t now_ticks = mach_absolute_time(); - uint64_t now_ns = (now_ticks * tb_info->numer) / tb_info->denom; - ts->tv_sec = now_ns / 1000000000; - ts->tv_nsec = now_ns % 1000000000; - - return 0; -} - -int clock_getres(clockid_t clk_id, struct timespec* ts) { - auto tb_info = tbInfo(); - if (tb_info == nullptr) { - errno = EINVAL; - return -1; - } - - ts->tv_sec = 0; - ts->tv_nsec = tb_info->numer / tb_info->denom; - - return 0; -} -#elif defined(_MSC_VER) -// The MSVC version has been extracted from the pthreads implemenation here: -// https://github.com/songdongsheng/libpthread -// Copyright(c) 2011, Dongsheng Song -// -// It is under the Apache License Version 2.0, just as the rest of the file is. -// It has been mostly stripped down to what we have. - -#include - -#define DELTA_EPOCH_IN_100NS INT64_C(116444736000000000) -#define POW10_7 INT64_C(10000000) -#define POW10_9 INT64_C(1000000000) - -int clock_getres(clockid_t clock_id, struct timespec *res) -{ - switch (clock_id) { - case CLOCK_MONOTONIC: - { - LARGE_INTEGER pf; - - if (QueryPerformanceFrequency(&pf) == 0) - return -1; - - res->tv_sec = 0; - res->tv_nsec = (int)((POW10_9 + (pf.QuadPart >> 1)) / pf.QuadPart); - if (res->tv_nsec < 1) - res->tv_nsec = 1; - - return 0; - } - - case CLOCK_REALTIME: - case CLOCK_PROCESS_CPUTIME_ID: - case CLOCK_THREAD_CPUTIME_ID: - { - DWORD timeAdjustment, timeIncrement; - BOOL isTimeAdjustmentDisabled; - - (void)GetSystemTimeAdjustment( - &timeAdjustment, - &timeIncrement, - &isTimeAdjustmentDisabled - ); - res->tv_sec = 0; - res->tv_nsec = timeIncrement * 100; - - return 0; - } - default: - break; - } - - return -1; -} - -int clock_gettime(clockid_t clock_id, struct timespec *tp) -{ - unsigned __int64 t; - LARGE_INTEGER pf, pc; - union { - unsigned __int64 u64; - FILETIME ft; - } ct, et, kt, ut; - - switch (clock_id) { - case CLOCK_REALTIME: - { - GetSystemTimeAsFileTime(&ct.ft); - t = ct.u64 - DELTA_EPOCH_IN_100NS; - tp->tv_sec = t / POW10_7; - tp->tv_nsec = ((int)(t % POW10_7)) * 100; - - return 0; - } - - case CLOCK_MONOTONIC: - { - if (QueryPerformanceFrequency(&pf) == 0) - return -1; - - if (QueryPerformanceCounter(&pc) == 0) - return -1; - - tp->tv_sec = pc.QuadPart / pf.QuadPart; - tp->tv_nsec = (int)( - ((pc.QuadPart % pf.QuadPart) * POW10_9 + (pf.QuadPart >> 1)) / - pf.QuadPart - ); - if (tp->tv_nsec >= POW10_9) { - tp->tv_sec++; - tp->tv_nsec -= POW10_9; - } - - return 0; - } - - case CLOCK_PROCESS_CPUTIME_ID: - { - if (0 == GetProcessTimes(GetCurrentProcess(), - &ct.ft, &et.ft, &kt.ft, &ut.ft)) { - return -1; - } - t = kt.u64 + ut.u64; - tp->tv_sec = t / POW10_7; - tp->tv_nsec = ((int)(t % POW10_7)) * 100; - - return 0; - } - - case CLOCK_THREAD_CPUTIME_ID: - { - if (0 == GetThreadTimes(GetCurrentThread(), - &ct.ft, &et.ft, &kt.ft, &ut.ft)) { - return -1; - } - t = kt.u64 + ut.u64; - tp->tv_sec = t / POW10_7; - tp->tv_nsec = ((int)(t % POW10_7)) * 100; - - return 0; - } - - default: - break; - } - - return -1; -} -#elif defined(__CYGWIN__) || defined(__MINGW__) -// using winpthreads from mingw-w64 -// has clock_gettime and friends -// make sure to include as well for typedefs of timespec/etc -#else -#error No clock_gettime(2) compatibility wrapper available for this platform. -#endif diff --git a/folly/detail/Clock.h b/folly/detail/Clock.h deleted file mode 100644 index d7da926e..00000000 --- a/folly/detail/Clock.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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. - */ - -#ifndef FOLLY_DETAIL_CLOCK_H_ -#define FOLLY_DETAIL_CLOCK_H_ - -#include -#include - -#include - -#if FOLLY_HAVE_CLOCK_GETTIME -#error This should only be used as a workaround for platforms \ - that do not support clock_gettime(2). -#endif - -/* For windows, we'll use pthread's time implementations */ -#if defined(__CYGWIN__) || defined(__MINGW__) -#include -#include -#else -typedef uint8_t clockid_t; -#define CLOCK_REALTIME 0 -#ifdef _MSC_VER -#define CLOCK_MONOTONIC 1 -#define CLOCK_PROCESS_CPUTIME_ID 2 -#define CLOCK_THREAD_CPUTIME_ID 3 -#endif - -int clock_gettime(clockid_t clk_id, struct timespec* ts); -int clock_getres(clockid_t clk_id, struct timespec* ts); -#endif - -#endif /* FOLLY_DETAIL_CLOCK_H_ */ diff --git a/folly/portability/Time.cpp b/folly/portability/Time.cpp index 0a06abdb..56bfb79a 100755 --- a/folly/portability/Time.cpp +++ b/folly/portability/Time.cpp @@ -16,11 +16,189 @@ #include +#if !FOLLY_HAVE_CLOCK_GETTIME +#if __MACH__ +#include +#include + +static const mach_timebase_info_data_t* tbInfo() { + static auto info = [] { + static mach_timebase_info_data_t info; + return (mach_timebase_info(&info) == KERN_SUCCESS) ? &info : nullptr; + }(); + return info; +} + +int clock_gettime(clockid_t clk_id, struct timespec* ts) { + auto tb_info = tbInfo(); + if (tb_info == nullptr) { + errno = EINVAL; + return -1; + } + + uint64_t now_ticks = mach_absolute_time(); + uint64_t now_ns = (now_ticks * tb_info->numer) / tb_info->denom; + ts->tv_sec = now_ns / 1000000000; + ts->tv_nsec = now_ns % 1000000000; + + return 0; +} + +int clock_getres(clockid_t clk_id, struct timespec* ts) { + auto tb_info = tbInfo(); + if (tb_info == nullptr) { + errno = EINVAL; + return -1; + } + + ts->tv_sec = 0; + ts->tv_nsec = tb_info->numer / tb_info->denom; + + return 0; +} +#elif defined(_WIN32) +#include +#include +#include + +#include + +static constexpr size_t kNsPerSec = 1000000000; + +extern "C" int clock_getres(clockid_t clock_id, struct timespec* res) { + if (!res) { + errno = EFAULT; + return -1; + } + + switch (clock_id) { + case CLOCK_MONOTONIC: { + LARGE_INTEGER freq; + if (!QueryPerformanceFrequency(&freq)) { + errno = EINVAL; + return -1; + } + + res->tv_sec = 0; + res->tv_nsec = (long)((kNsPerSec + (freq.QuadPart >> 1)) / freq.QuadPart); + if (res->tv_nsec < 1) { + res->tv_nsec = 1; + } + + return 0; + } + + case CLOCK_REALTIME: + case CLOCK_PROCESS_CPUTIME_ID: + case CLOCK_THREAD_CPUTIME_ID: { + DWORD adj, timeIncrement; + BOOL adjDisabled; + if (!GetSystemTimeAdjustment(&adj, &timeIncrement, &adjDisabled)) { + errno = EINVAL; + return -1; + } + + res->tv_sec = 0; + res->tv_nsec = timeIncrement * 100; + return 0; + } + + default: + errno = EINVAL; + return -1; + } +} + +extern "C" int clock_gettime(clockid_t clock_id, struct timespec* tp) { + if (!tp) { + errno = EFAULT; + return -1; + } + + const auto ftToUint = [](FILETIME ft) -> uint64_t { + ULARGE_INTEGER i; + i.HighPart = ft.dwHighDateTime; + i.LowPart = ft.dwLowDateTime; + return i.QuadPart; + }; + const auto timeToTimespec = [](timespec* tp, uint64_t t) -> int { + constexpr size_t k100NsPerSec = kNsPerSec / 100; + + // The filetimes t is based on are represented in + // 100ns's. (ie. a value of 4 is 400ns) + tp->tv_sec = t / k100NsPerSec; + tp->tv_nsec = ((long)(t % k100NsPerSec)) * 100; + return 0; + }; + + FILETIME createTime, exitTime, kernalTime, userTime; + switch (clock_id) { + case CLOCK_REALTIME: { + constexpr size_t kDeltaEpochIn100NS = 116444736000000000ULL; + + GetSystemTimeAsFileTime(&createTime); + return timeToTimespec(tp, ftToUint(createTime) - kDeltaEpochIn100NS); + } + case CLOCK_PROCESS_CPUTIME_ID: { + if (!GetProcessTimes( + GetCurrentProcess(), + &createTime, + &exitTime, + &kernalTime, + &userTime)) { + errno = EINVAL; + return -1; + } + + return timeToTimespec(tp, ftToUint(kernalTime) + ftToUint(userTime)); + } + case CLOCK_THREAD_CPUTIME_ID: { + if (!GetThreadTimes( + GetCurrentThread(), + &createTime, + &exitTime, + &kernalTime, + &userTime)) { + errno = EINVAL; + return -1; + } + + return timeToTimespec(tp, ftToUint(kernalTime) + ftToUint(userTime)); + } + case CLOCK_MONOTONIC: { + LARGE_INTEGER fl, cl; + if (!QueryPerformanceFrequency(&fl) || !QueryPerformanceCounter(&cl)) { + errno = EINVAL; + return -1; + } + + int64_t freq = fl.QuadPart; + int64_t counter = cl.QuadPart; + tp->tv_sec = counter / freq; + tp->tv_nsec = (long)(((counter % freq) * kNsPerSec + (freq >> 1)) / freq); + if (tp->tv_nsec >= kNsPerSec) { + tp->tv_sec++; + tp->tv_nsec -= kNsPerSec; + } + + return 0; + } + + default: + errno = EINVAL; + return -1; + } +} +#else +#error No clock_gettime(3) compatibility wrapper available for this platform. +#endif +#endif + #ifdef _WIN32 #include #include -#include +#include extern "C" { char* asctime_r(const tm* tm, char* buf) { diff --git a/folly/portability/Time.h b/folly/portability/Time.h index 0a35b23f..1980377f 100755 --- a/folly/portability/Time.h +++ b/folly/portability/Time.h @@ -18,6 +18,25 @@ #include +#include + +// These aren't generic implementations, so we can only declare them on +// platforms we support. +#if !FOLLY_HAVE_CLOCK_GETTIME && (defined(__MACH__) || defined(_WIN32)) +#define CLOCK_REALTIME 0 +// The Windows implementation supports a few other +// clock types as well. +#ifdef _WIN32 +# define CLOCK_MONOTONIC 1 +# define CLOCK_PROCESS_CPUTIME_ID 2 +# define CLOCK_THREAD_CPUTIME_ID 3 +#endif + +typedef uint8_t clockid_t; +extern "C" int clock_gettime(clockid_t clk_id, struct timespec* ts); +extern "C" int clock_getres(clockid_t clk_id, struct timespec* ts); +#endif + #ifdef _WIN32 #define TM_YEAR_BASE (1900)