Fix up and namespace clock_gettime and clock_getres for MacOS
[folly.git] / folly / portability / Time.cpp
1 /*
2  * Copyright 2017 Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <folly/portability/Time.h>
18 #include <folly/Likely.h>
19
20 #include <assert.h>
21
22 #include <chrono>
23
24 template <typename _Rep, typename _Period>
25 static void duration_to_ts(
26     std::chrono::duration<_Rep, _Period> d,
27     struct timespec* ts) {
28   ts->tv_sec =
29       time_t(std::chrono::duration_cast<std::chrono::seconds>(d).count());
30   ts->tv_nsec = long(std::chrono::duration_cast<std::chrono::nanoseconds>(
31                          d % std::chrono::seconds(1))
32                          .count());
33 }
34
35 #if !FOLLY_HAVE_CLOCK_GETTIME || FOLLY_FORCE_CLOCK_GETTIME_DEFINITION
36 #if __MACH__
37 #include <errno.h>
38 #include <mach/mach_init.h>
39 #include <mach/mach_port.h>
40 #include <mach/mach_time.h>
41 #include <mach/mach_types.h>
42 #include <mach/task.h>
43 #include <mach/thread_act.h>
44 #include <mach/vm_map.h>
45
46 namespace folly {
47 namespace portability {
48 namespace time {
49
50 static std::chrono::nanoseconds time_value_to_ns(time_value_t t) {
51   return std::chrono::seconds(t.seconds) +
52       std::chrono::microseconds(t.microseconds);
53 }
54
55 static int clock_process_cputime(struct timespec* ts) {
56   // Get CPU usage for live threads.
57   task_thread_times_info thread_times_info;
58   mach_msg_type_number_t thread_times_info_count = TASK_THREAD_TIMES_INFO_COUNT;
59   kern_return_t kern_result = task_info(
60       mach_task_self(),
61       TASK_THREAD_TIMES_INFO,
62       (thread_info_t)&thread_times_info,
63       &thread_times_info_count);
64   if (UNLIKELY(kern_result != KERN_SUCCESS)) {
65     return -1;
66   }
67
68   // Get CPU usage for terminated threads.
69   mach_task_basic_info task_basic_info;
70   mach_msg_type_number_t task_basic_info_count = MACH_TASK_BASIC_INFO_COUNT;
71   kern_result = task_info(
72       mach_task_self(),
73       MACH_TASK_BASIC_INFO,
74       (thread_info_t)&task_basic_info,
75       &task_basic_info_count);
76   if (UNLIKELY(kern_result != KERN_SUCCESS)) {
77     return -1;
78   }
79
80   auto cputime = time_value_to_ns(thread_times_info.user_time) +
81       time_value_to_ns(thread_times_info.system_time) +
82       time_value_to_ns(task_basic_info.user_time) +
83       time_value_to_ns(task_basic_info.system_time);
84   duration_to_ts(cputime, ts);
85   return 0;
86 }
87
88 static int clock_thread_cputime(struct timespec* ts) {
89   mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT;
90   thread_basic_info_data_t thread_info_data;
91   thread_act_t thread = mach_thread_self();
92   kern_return_t kern_result = thread_info(
93       thread, THREAD_BASIC_INFO, (thread_info_t)&thread_info_data, &count);
94   mach_port_deallocate(mach_task_self(), thread);
95   if (UNLIKELY(kern_result != KERN_SUCCESS)) {
96     return -1;
97   }
98   auto cputime = time_value_to_ns(thread_info_data.system_time) +
99       time_value_to_ns(thread_info_data.user_time);
100   duration_to_ts(cputime, ts);
101   return 0;
102 }
103
104 int clock_gettime(clockid_t clk_id, struct timespec* ts) {
105   switch (clk_id) {
106     case CLOCK_REALTIME: {
107       auto now = std::chrono::system_clock::now().time_since_epoch();
108       duration_to_ts(now, ts);
109       return 0;
110     }
111     case CLOCK_MONOTONIC: {
112       auto now = std::chrono::steady_clock::now().time_since_epoch();
113       duration_to_ts(now, ts);
114       return 0;
115     }
116     case CLOCK_PROCESS_CPUTIME_ID:
117       return clock_process_cputime(ts);
118     case CLOCK_THREAD_CPUTIME_ID:
119       return clock_thread_cputime(ts);
120     default:
121       errno = EINVAL;
122       return -1;
123   }
124 }
125
126 int clock_getres(clockid_t clk_id, struct timespec* ts) {
127   if (clk_id != CLOCK_MONOTONIC) {
128     return -1;
129   }
130
131   static auto info = [] {
132     static mach_timebase_info_data_t info;
133     auto result = (mach_timebase_info(&info) == KERN_SUCCESS) ? &info : nullptr;
134     assert(result);
135     return result;
136   }();
137
138   ts->tv_sec = 0;
139   ts->tv_nsec = info->numer / info->denom;
140
141   return 0;
142 }
143
144 } // namespace time
145 } // namespace portability
146 } // namespace folly
147 #elif defined(_WIN32)
148 #include <errno.h>
149 #include <locale.h>
150 #include <stdint.h>
151 #include <stdlib.h>
152
153 #include <folly/portability/Windows.h>
154
155 namespace folly {
156 namespace portability {
157 namespace time {
158
159 using unsigned_nanos = std::chrono::duration<uint64_t, std::nano>;
160
161 static unsigned_nanos filetimeToUnsignedNanos(FILETIME ft) {
162   ULARGE_INTEGER i;
163   i.HighPart = ft.dwHighDateTime;
164   i.LowPart = ft.dwLowDateTime;
165
166   // FILETIMEs are in units of 100ns.
167   return unsigned_nanos(i.QuadPart * 100);
168 };
169
170 static LARGE_INTEGER performanceFrequency() {
171   static auto result = [] {
172     LARGE_INTEGER freq;
173     // On Windows XP or later, this will never fail.
174     BOOL res = QueryPerformanceFrequency(&freq);
175     assert(res);
176     return freq;
177   }();
178   return result;
179 }
180
181 extern "C" int clock_getres(clockid_t clock_id, struct timespec* res) {
182   if (!res) {
183     errno = EFAULT;
184     return -1;
185   }
186
187   static constexpr size_t kNsPerSec = 1000000000;
188   switch (clock_id) {
189     case CLOCK_REALTIME: {
190       constexpr auto perSec = double(std::chrono::system_clock::period::num) /
191           std::chrono::system_clock::period::den;
192       res->tv_sec = time_t(perSec);
193       res->tv_nsec = time_t(perSec * kNsPerSec);
194       return 0;
195     }
196     case CLOCK_MONOTONIC: {
197       constexpr auto perSec = double(std::chrono::steady_clock::period::num) /
198           std::chrono::steady_clock::period::den;
199       res->tv_sec = time_t(perSec);
200       res->tv_nsec = time_t(perSec * kNsPerSec);
201       return 0;
202     }
203     case CLOCK_PROCESS_CPUTIME_ID:
204     case CLOCK_THREAD_CPUTIME_ID: {
205       DWORD adj, timeIncrement;
206       BOOL adjDisabled;
207       if (!GetSystemTimeAdjustment(&adj, &timeIncrement, &adjDisabled)) {
208         errno = EINVAL;
209         return -1;
210       }
211
212       res->tv_sec = 0;
213       res->tv_nsec = long(timeIncrement * 100);
214       return 0;
215     }
216
217     default:
218       errno = EINVAL;
219       return -1;
220   }
221 }
222
223 extern "C" int clock_gettime(clockid_t clock_id, struct timespec* tp) {
224   if (!tp) {
225     errno = EFAULT;
226     return -1;
227   }
228
229   const auto unanosToTimespec = [](timespec* tp, unsigned_nanos t) -> int {
230     static constexpr unsigned_nanos one_sec{std::chrono::seconds(1)};
231     tp->tv_sec =
232         time_t(std::chrono::duration_cast<std::chrono::seconds>(t).count());
233     tp->tv_nsec = long((t % one_sec).count());
234     return 0;
235   };
236
237   FILETIME createTime, exitTime, kernalTime, userTime;
238   switch (clock_id) {
239     case CLOCK_REALTIME: {
240       auto now = std::chrono::system_clock::now().time_since_epoch();
241       duration_to_ts(now, tp);
242       return 0;
243     }
244     case CLOCK_MONOTONIC: {
245       auto now = std::chrono::steady_clock::now().time_since_epoch();
246       duration_to_ts(now, tp);
247       return 0;
248     }
249     case CLOCK_PROCESS_CPUTIME_ID: {
250       if (!GetProcessTimes(
251               GetCurrentProcess(),
252               &createTime,
253               &exitTime,
254               &kernalTime,
255               &userTime)) {
256         errno = EINVAL;
257         return -1;
258       }
259
260       return unanosToTimespec(
261           tp,
262           filetimeToUnsignedNanos(kernalTime) +
263               filetimeToUnsignedNanos(userTime));
264     }
265     case CLOCK_THREAD_CPUTIME_ID: {
266       if (!GetThreadTimes(
267               GetCurrentThread(),
268               &createTime,
269               &exitTime,
270               &kernalTime,
271               &userTime)) {
272         errno = EINVAL;
273         return -1;
274       }
275
276       return unanosToTimespec(
277           tp,
278           filetimeToUnsignedNanos(kernalTime) +
279               filetimeToUnsignedNanos(userTime));
280     }
281
282     default:
283       errno = EINVAL;
284       return -1;
285   }
286 }
287
288 } // namespace time
289 } // namespace portability
290 } // namespace folly
291 #else
292 #error No clock_gettime(3) compatibility wrapper available for this platform.
293 #endif
294 #endif
295
296 #ifdef _WIN32
297 #include <iomanip>
298 #include <sstream>
299
300 #include <folly/portability/Windows.h>
301
302 extern "C" {
303 char* asctime_r(const tm* tm, char* buf) {
304   char tmpBuf[64];
305   if (asctime_s(tmpBuf, tm)) {
306     return nullptr;
307   }
308   // Nothing we can do if the buff is to small :(
309   return strcpy(buf, tmpBuf);
310 }
311
312 char* ctime_r(const time_t* t, char* buf) {
313   char tmpBuf[64];
314   if (ctime_s(tmpBuf, 64, t)) {
315     return nullptr;
316   }
317   // Nothing we can do if the buff is to small :(
318   return strcpy(buf, tmpBuf);
319 }
320
321 tm* gmtime_r(const time_t* t, tm* res) {
322   if (!gmtime_s(res, t)) {
323     return res;
324   }
325   return nullptr;
326 }
327
328 tm* localtime_r(const time_t* t, tm* o) {
329   if (!localtime_s(o, t)) {
330     return o;
331   }
332   return nullptr;
333 }
334
335 int nanosleep(const struct timespec* request, struct timespec* remain) {
336   Sleep((DWORD)((request->tv_sec * 1000) + (request->tv_nsec / 1000000)));
337   if (remain != nullptr) {
338     remain->tv_nsec = 0;
339     remain->tv_sec = 0;
340   }
341   return 0;
342 }
343
344 char* strptime(
345     const char* __restrict s,
346     const char* __restrict f,
347     struct tm* __restrict tm) {
348   // Isn't the C++ standard lib nice? std::get_time is defined such that its
349   // format parameters are the exact same as strptime. Of course, we have to
350   // create a string stream first, and imbue it with the current C locale, and
351   // we also have to make sure we return the right things if it fails, or
352   // if it succeeds, but this is still far simpler an implementation than any
353   // of the versions in any of the C standard libraries.
354   std::istringstream input(s);
355   input.imbue(std::locale(setlocale(LC_ALL, nullptr)));
356   input >> std::get_time(tm, f);
357   if (input.fail()) {
358     return nullptr;
359   }
360   return const_cast<char*>(s + input.tellg());
361 }
362 }
363 #endif