Commit dec14727 authored by Christopher Dykes's avatar Christopher Dykes Committed by Facebook Github Bot 2

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
parent 67859615
......@@ -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
......
......@@ -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 <time.h>
#else
#include <folly/detail/Clock.h>
#endif
// Provide our own std::__throw_* wrappers for platforms that don't have them
#if FOLLY_HAVE_BITS_FUNCTEXCEPT_H
#include <bits/functexcept.h>
......
/*
* 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/detail/Clock.h>
#if __MACH__
#include <errno.h>
#include <mach/mach_time.h>
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 <songdongsheng@live.cn>
//
// 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 <WinSock2.h>
#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
// <pthreads_time.h> has clock_gettime and friends
// make sure to include <pthread.h> as well for typedefs of timespec/etc
#else
#error No clock_gettime(2) compatibility wrapper available for this platform.
#endif
/*
* 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 <ctime>
#include <cstdint>
#include <folly/Portability.h>
#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 <pthread.h>
#include <pthread_time.h>
#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_ */
......@@ -16,11 +16,189 @@
#include <folly/portability/Time.h>
#if !FOLLY_HAVE_CLOCK_GETTIME
#if __MACH__
#include <errno.h>
#include <mach/mach_time.h>
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 <errno.h>
#include <stdint.h>
#include <stdlib.h>
#include <folly/portability/Windows.h>
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 <iomanip>
#include <sstream>
#include <Windows.h>
#include <folly/portability/Windows.h>
extern "C" {
char* asctime_r(const tm* tm, char* buf) {
......
......@@ -18,6 +18,25 @@
#include <time.h>
#include <folly/portability/Config.h>
// 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)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment