Fix up and namespace clock_gettime and clock_getres for MacOS
[folly.git] / folly / portability / Time.cpp
old mode 100755 (executable)
new mode 100644 (file)
index 56bfb79..e54286f
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016 Facebook, Inc.
+ * Copyright 2017 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  */
 
 #include <folly/portability/Time.h>
+#include <folly/Likely.h>
 
-#if !FOLLY_HAVE_CLOCK_GETTIME
+#include <assert.h>
+
+#include <chrono>
+
+template <typename _Rep, typename _Period>
+static void duration_to_ts(
+    std::chrono::duration<_Rep, _Period> d,
+    struct timespec* ts) {
+  ts->tv_sec =
+      time_t(std::chrono::duration_cast<std::chrono::seconds>(d).count());
+  ts->tv_nsec = long(std::chrono::duration_cast<std::chrono::nanoseconds>(
+                         d % std::chrono::seconds(1))
+                         .count());
+}
+
+#if !FOLLY_HAVE_CLOCK_GETTIME || FOLLY_FORCE_CLOCK_GETTIME_DEFINITION
 #if __MACH__
 #include <errno.h>
+#include <mach/mach_init.h>
+#include <mach/mach_port.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;
+#include <mach/mach_types.h>
+#include <mach/task.h>
+#include <mach/thread_act.h>
+#include <mach/vm_map.h>
+
+namespace folly {
+namespace portability {
+namespace time {
+
+static std::chrono::nanoseconds time_value_to_ns(time_value_t t) {
+  return std::chrono::seconds(t.seconds) +
+      std::chrono::microseconds(t.microseconds);
 }
 
-int clock_gettime(clockid_t clk_id, struct timespec* ts) {
-  auto tb_info = tbInfo();
-  if (tb_info == nullptr) {
-    errno = EINVAL;
+static int clock_process_cputime(struct timespec* ts) {
+  // Get CPU usage for live threads.
+  task_thread_times_info thread_times_info;
+  mach_msg_type_number_t thread_times_info_count = TASK_THREAD_TIMES_INFO_COUNT;
+  kern_return_t kern_result = task_info(
+      mach_task_self(),
+      TASK_THREAD_TIMES_INFO,
+      (thread_info_t)&thread_times_info,
+      &thread_times_info_count);
+  if (UNLIKELY(kern_result != KERN_SUCCESS)) {
     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;
+  // Get CPU usage for terminated threads.
+  mach_task_basic_info task_basic_info;
+  mach_msg_type_number_t task_basic_info_count = MACH_TASK_BASIC_INFO_COUNT;
+  kern_result = task_info(
+      mach_task_self(),
+      MACH_TASK_BASIC_INFO,
+      (thread_info_t)&task_basic_info,
+      &task_basic_info_count);
+  if (UNLIKELY(kern_result != KERN_SUCCESS)) {
+    return -1;
+  }
 
+  auto cputime = time_value_to_ns(thread_times_info.user_time) +
+      time_value_to_ns(thread_times_info.system_time) +
+      time_value_to_ns(task_basic_info.user_time) +
+      time_value_to_ns(task_basic_info.system_time);
+  duration_to_ts(cputime, ts);
   return 0;
 }
 
+static int clock_thread_cputime(struct timespec* ts) {
+  mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT;
+  thread_basic_info_data_t thread_info_data;
+  thread_act_t thread = mach_thread_self();
+  kern_return_t kern_result = thread_info(
+      thread, THREAD_BASIC_INFO, (thread_info_t)&thread_info_data, &count);
+  mach_port_deallocate(mach_task_self(), thread);
+  if (UNLIKELY(kern_result != KERN_SUCCESS)) {
+    return -1;
+  }
+  auto cputime = time_value_to_ns(thread_info_data.system_time) +
+      time_value_to_ns(thread_info_data.user_time);
+  duration_to_ts(cputime, ts);
+  return 0;
+}
+
+int clock_gettime(clockid_t clk_id, struct timespec* ts) {
+  switch (clk_id) {
+    case CLOCK_REALTIME: {
+      auto now = std::chrono::system_clock::now().time_since_epoch();
+      duration_to_ts(now, ts);
+      return 0;
+    }
+    case CLOCK_MONOTONIC: {
+      auto now = std::chrono::steady_clock::now().time_since_epoch();
+      duration_to_ts(now, ts);
+      return 0;
+    }
+    case CLOCK_PROCESS_CPUTIME_ID:
+      return clock_process_cputime(ts);
+    case CLOCK_THREAD_CPUTIME_ID:
+      return clock_thread_cputime(ts);
+    default:
+      errno = EINVAL;
+      return -1;
+  }
+}
+
 int clock_getres(clockid_t clk_id, struct timespec* ts) {
-  auto tb_info = tbInfo();
-  if (tb_info == nullptr) {
-    errno = EINVAL;
+  if (clk_id != CLOCK_MONOTONIC) {
     return -1;
   }
 
+  static auto info = [] {
+    static mach_timebase_info_data_t info;
+    auto result = (mach_timebase_info(&info) == KERN_SUCCESS) ? &info : nullptr;
+    assert(result);
+    return result;
+  }();
+
   ts->tv_sec = 0;
-  ts->tv_nsec = tb_info->numer / tb_info->denom;
+  ts->tv_nsec = info->numer / info->denom;
 
   return 0;
 }
+
+} // namespace time
+} // namespace portability
+} // namespace folly
 #elif defined(_WIN32)
 #include <errno.h>
+#include <locale.h>
 #include <stdint.h>
 #include <stdlib.h>
 
 #include <folly/portability/Windows.h>
 
-static constexpr size_t kNsPerSec = 1000000000;
+namespace folly {
+namespace portability {
+namespace time {
+
+using unsigned_nanos = std::chrono::duration<uint64_t, std::nano>;
+
+static unsigned_nanos filetimeToUnsignedNanos(FILETIME ft) {
+  ULARGE_INTEGER i;
+  i.HighPart = ft.dwHighDateTime;
+  i.LowPart = ft.dwLowDateTime;
+
+  // FILETIMEs are in units of 100ns.
+  return unsigned_nanos(i.QuadPart * 100);
+};
+
+static LARGE_INTEGER performanceFrequency() {
+  static auto result = [] {
+    LARGE_INTEGER freq;
+    // On Windows XP or later, this will never fail.
+    BOOL res = QueryPerformanceFrequency(&freq);
+    assert(res);
+    return freq;
+  }();
+  return result;
+}
 
 extern "C" int clock_getres(clockid_t clock_id, struct timespec* res) {
   if (!res) {
@@ -71,24 +184,22 @@ extern "C" int clock_getres(clockid_t clock_id, struct timespec* res) {
     return -1;
   }
 
+  static constexpr size_t kNsPerSec = 1000000000;
   switch (clock_id) {
+    case CLOCK_REALTIME: {
+      constexpr auto perSec = double(std::chrono::system_clock::period::num) /
+          std::chrono::system_clock::period::den;
+      res->tv_sec = time_t(perSec);
+      res->tv_nsec = time_t(perSec * kNsPerSec);
+      return 0;
+    }
     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;
-      }
-
+      constexpr auto perSec = double(std::chrono::steady_clock::period::num) /
+          std::chrono::steady_clock::period::den;
+      res->tv_sec = time_t(perSec);
+      res->tv_nsec = time_t(perSec * kNsPerSec);
       return 0;
     }
-
-    case CLOCK_REALTIME:
     case CLOCK_PROCESS_CPUTIME_ID:
     case CLOCK_THREAD_CPUTIME_ID: {
       DWORD adj, timeIncrement;
@@ -99,7 +210,7 @@ extern "C" int clock_getres(clockid_t clock_id, struct timespec* res) {
       }
 
       res->tv_sec = 0;
-      res->tv_nsec = timeIncrement * 100;
+      res->tv_nsec = long(timeIncrement * 100);
       return 0;
     }
 
@@ -115,29 +226,25 @@ extern "C" int clock_gettime(clockid_t clock_id, struct timespec* tp) {
     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;
+  const auto unanosToTimespec = [](timespec* tp, unsigned_nanos t) -> int {
+    static constexpr unsigned_nanos one_sec{std::chrono::seconds(1)};
+    tp->tv_sec =
+        time_t(std::chrono::duration_cast<std::chrono::seconds>(t).count());
+    tp->tv_nsec = long((t % one_sec).count());
     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);
+      auto now = std::chrono::system_clock::now().time_since_epoch();
+      duration_to_ts(now, tp);
+      return 0;
+    }
+    case CLOCK_MONOTONIC: {
+      auto now = std::chrono::steady_clock::now().time_since_epoch();
+      duration_to_ts(now, tp);
+      return 0;
     }
     case CLOCK_PROCESS_CPUTIME_ID: {
       if (!GetProcessTimes(
@@ -150,7 +257,10 @@ extern "C" int clock_gettime(clockid_t clock_id, struct timespec* tp) {
         return -1;
       }
 
-      return timeToTimespec(tp, ftToUint(kernalTime) + ftToUint(userTime));
+      return unanosToTimespec(
+          tp,
+          filetimeToUnsignedNanos(kernalTime) +
+              filetimeToUnsignedNanos(userTime));
     }
     case CLOCK_THREAD_CPUTIME_ID: {
       if (!GetThreadTimes(
@@ -163,25 +273,10 @@ extern "C" int clock_gettime(clockid_t clock_id, struct timespec* tp) {
         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;
+      return unanosToTimespec(
+          tp,
+          filetimeToUnsignedNanos(kernalTime) +
+              filetimeToUnsignedNanos(userTime));
     }
 
     default:
@@ -189,6 +284,10 @@ extern "C" int clock_gettime(clockid_t clock_id, struct timespec* tp) {
       return -1;
   }
 }
+
+} // namespace time
+} // namespace portability
+} // namespace folly
 #else
 #error No clock_gettime(3) compatibility wrapper available for this platform.
 #endif
@@ -212,7 +311,7 @@ char* asctime_r(const tm* tm, char* buf) {
 
 char* ctime_r(const time_t* t, char* buf) {
   char tmpBuf[64];
-  if (ctime_s(tmpBuf, t)) {
+  if (ctime_s(tmpBuf, 64, t)) {
     return nullptr;
   }
   // Nothing we can do if the buff is to small :(
@@ -234,15 +333,18 @@ tm* localtime_r(const time_t* t, tm* o) {
 }
 
 int nanosleep(const struct timespec* request, struct timespec* remain) {
-  Sleep((DWORD)((request->tv_sec * 1000) + (request->tv_nsec / 1000000));
-  remain->tv_nsec = 0;
-  remain->tv_sec = 0;
+  Sleep((DWORD)((request->tv_sec * 1000) + (request->tv_nsec / 1000000)));
+  if (remain != nullptr) {
+    remain->tv_nsec = 0;
+    remain->tv_sec = 0;
+  }
   return 0;
 }
 
-char* strptime(const char* __restrict s,
-               const char* __restrict f,
-               struct tm* __restrict tm) {
+char* strptime(
+    const char* __restrict s,
+    const char* __restrict f,
+    struct tm* __restrict tm) {
   // Isn't the C++ standard lib nice? std::get_time is defined such that its
   // format parameters are the exact same as strptime. Of course, we have to
   // create a string stream first, and imbue it with the current C locale, and