Work around a compiler crash in VS 2017.3 Preview 1
[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 static std::chrono::nanoseconds time_value_to_ns(time_value_t t) {
47   return std::chrono::seconds(t.seconds) +
48       std::chrono::microseconds(t.microseconds);
49 }
50
51 static int clock_process_cputime(struct timespec* ts) {
52   // Get CPU usage for live threads.
53   task_thread_times_info thread_times_info;
54   mach_msg_type_number_t thread_times_info_count = TASK_THREAD_TIMES_INFO_COUNT;
55   kern_return_t kern_result = task_info(
56       mach_task_self(),
57       TASK_THREAD_TIMES_INFO,
58       (thread_info_t)&thread_times_info,
59       &thread_times_info_count);
60   if (UNLIKELY(kern_result != KERN_SUCCESS)) {
61     return -1;
62   }
63
64   // Get CPU usage for terminated threads.
65   mach_task_basic_info task_basic_info;
66   mach_msg_type_number_t task_basic_info_count = MACH_TASK_BASIC_INFO_COUNT;
67   kern_result = task_info(
68       mach_task_self(),
69       MACH_TASK_BASIC_INFO,
70       (thread_info_t)&task_basic_info,
71       &task_basic_info_count);
72   if (UNLIKELY(kern_result != KERN_SUCCESS)) {
73     return -1;
74   }
75
76   auto cputime = time_value_to_ns(thread_times_info.user_time) +
77       time_value_to_ns(thread_times_info.system_time) +
78       time_value_to_ns(task_basic_info.user_time) +
79       time_value_to_ns(task_basic_info.system_time);
80   duration_to_ts(cputime, ts);
81   return 0;
82 }
83
84 static int clock_thread_cputime(struct timespec* ts) {
85   mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT;
86   thread_basic_info_data_t thread_info_data;
87   thread_act_t thread = mach_thread_self();
88   kern_return_t kern_result = thread_info(
89       thread, THREAD_BASIC_INFO, (thread_info_t)&thread_info_data, &count);
90   mach_port_deallocate(mach_task_self(), thread);
91   if (UNLIKELY(kern_result != KERN_SUCCESS)) {
92     return -1;
93   }
94   auto cputime = time_value_to_ns(thread_info_data.system_time) +
95       time_value_to_ns(thread_info_data.user_time);
96   duration_to_ts(cputime, ts);
97   return 0;
98 }
99
100 int clock_gettime(clockid_t clk_id, struct timespec* ts) {
101   switch (clk_id) {
102     case CLOCK_REALTIME: {
103       auto now = std::chrono::system_clock::now().time_since_epoch();
104       duration_to_ts(now, ts);
105       return 0;
106     }
107     case CLOCK_MONOTONIC: {
108       auto now = std::chrono::steady_clock::now().time_since_epoch();
109       duration_to_ts(now, ts);
110       return 0;
111     }
112     case CLOCK_PROCESS_CPUTIME_ID:
113       return clock_process_cputime(ts);
114     case CLOCK_THREAD_CPUTIME_ID:
115       return clock_thread_cputime(ts);
116     default:
117       errno = EINVAL;
118       return -1;
119   }
120 }
121
122 int clock_getres(clockid_t clk_id, struct timespec* ts) {
123   if (clk_id != CLOCK_MONOTONIC) {
124     return -1;
125   }
126
127   static auto info = [] {
128     static mach_timebase_info_data_t info;
129     auto result = (mach_timebase_info(&info) == KERN_SUCCESS) ? &info : nullptr;
130     assert(result);
131     return result;
132   }();
133
134   ts->tv_sec = 0;
135   ts->tv_nsec = info->numer / info->denom;
136
137   return 0;
138 }
139 #elif defined(_WIN32)
140 #include <errno.h>
141 #include <locale.h>
142 #include <stdint.h>
143 #include <stdlib.h>
144
145 #include <folly/portability/Windows.h>
146
147 using unsigned_nanos = std::chrono::duration<uint64_t, std::nano>;
148
149 static unsigned_nanos filetimeToUnsignedNanos(FILETIME ft) {
150   ULARGE_INTEGER i;
151   i.HighPart = ft.dwHighDateTime;
152   i.LowPart = ft.dwLowDateTime;
153
154   // FILETIMEs are in units of 100ns.
155   return unsigned_nanos(i.QuadPart * 100);
156 };
157
158 static LARGE_INTEGER performanceFrequency() {
159   static auto result = [] {
160     LARGE_INTEGER freq;
161     // On Windows XP or later, this will never fail.
162     BOOL res = QueryPerformanceFrequency(&freq);
163     assert(res);
164     return freq;
165   }();
166   return result;
167 }
168
169 extern "C" int clock_getres(clockid_t clock_id, struct timespec* res) {
170   if (!res) {
171     errno = EFAULT;
172     return -1;
173   }
174
175   static constexpr size_t kNsPerSec = 1000000000;
176   switch (clock_id) {
177     case CLOCK_REALTIME: {
178       constexpr auto perSec = double(std::chrono::system_clock::period::num) /
179           std::chrono::system_clock::period::den;
180       res->tv_sec = time_t(perSec);
181       res->tv_nsec = time_t(perSec * kNsPerSec);
182       return 0;
183     }
184     case CLOCK_MONOTONIC: {
185       constexpr auto perSec = double(std::chrono::steady_clock::period::num) /
186           std::chrono::steady_clock::period::den;
187       res->tv_sec = time_t(perSec);
188       res->tv_nsec = time_t(perSec * kNsPerSec);
189       return 0;
190     }
191     case CLOCK_PROCESS_CPUTIME_ID:
192     case CLOCK_THREAD_CPUTIME_ID: {
193       DWORD adj, timeIncrement;
194       BOOL adjDisabled;
195       if (!GetSystemTimeAdjustment(&adj, &timeIncrement, &adjDisabled)) {
196         errno = EINVAL;
197         return -1;
198       }
199
200       res->tv_sec = 0;
201       res->tv_nsec = long(timeIncrement * 100);
202       return 0;
203     }
204
205     default:
206       errno = EINVAL;
207       return -1;
208   }
209 }
210
211 extern "C" int clock_gettime(clockid_t clock_id, struct timespec* tp) {
212   if (!tp) {
213     errno = EFAULT;
214     return -1;
215   }
216
217   const auto unanosToTimespec = [](timespec* tp, unsigned_nanos t) -> int {
218     static constexpr unsigned_nanos one_sec{std::chrono::seconds(1)};
219     tp->tv_sec =
220         time_t(std::chrono::duration_cast<std::chrono::seconds>(t).count());
221     tp->tv_nsec = long((t % one_sec).count());
222     return 0;
223   };
224
225   FILETIME createTime, exitTime, kernalTime, userTime;
226   switch (clock_id) {
227     case CLOCK_REALTIME: {
228       auto now = std::chrono::system_clock::now().time_since_epoch();
229       duration_to_ts(now, tp);
230       return 0;
231     }
232     case CLOCK_MONOTONIC: {
233       auto now = std::chrono::steady_clock::now().time_since_epoch();
234       duration_to_ts(now, tp);
235       return 0;
236     }
237     case CLOCK_PROCESS_CPUTIME_ID: {
238       if (!GetProcessTimes(
239               GetCurrentProcess(),
240               &createTime,
241               &exitTime,
242               &kernalTime,
243               &userTime)) {
244         errno = EINVAL;
245         return -1;
246       }
247
248       return unanosToTimespec(
249           tp,
250           filetimeToUnsignedNanos(kernalTime) +
251               filetimeToUnsignedNanos(userTime));
252     }
253     case CLOCK_THREAD_CPUTIME_ID: {
254       if (!GetThreadTimes(
255               GetCurrentThread(),
256               &createTime,
257               &exitTime,
258               &kernalTime,
259               &userTime)) {
260         errno = EINVAL;
261         return -1;
262       }
263
264       return unanosToTimespec(
265           tp,
266           filetimeToUnsignedNanos(kernalTime) +
267               filetimeToUnsignedNanos(userTime));
268     }
269
270     default:
271       errno = EINVAL;
272       return -1;
273   }
274 }
275 #else
276 #error No clock_gettime(3) compatibility wrapper available for this platform.
277 #endif
278 #endif
279
280 #ifdef _WIN32
281 #include <iomanip>
282 #include <sstream>
283
284 #include <folly/portability/Windows.h>
285
286 extern "C" {
287 char* asctime_r(const tm* tm, char* buf) {
288   char tmpBuf[64];
289   if (asctime_s(tmpBuf, tm)) {
290     return nullptr;
291   }
292   // Nothing we can do if the buff is to small :(
293   return strcpy(buf, tmpBuf);
294 }
295
296 char* ctime_r(const time_t* t, char* buf) {
297   char tmpBuf[64];
298   if (ctime_s(tmpBuf, 64, t)) {
299     return nullptr;
300   }
301   // Nothing we can do if the buff is to small :(
302   return strcpy(buf, tmpBuf);
303 }
304
305 tm* gmtime_r(const time_t* t, tm* res) {
306   if (!gmtime_s(res, t)) {
307     return res;
308   }
309   return nullptr;
310 }
311
312 tm* localtime_r(const time_t* t, tm* o) {
313   if (!localtime_s(o, t)) {
314     return o;
315   }
316   return nullptr;
317 }
318
319 int nanosleep(const struct timespec* request, struct timespec* remain) {
320   Sleep((DWORD)((request->tv_sec * 1000) + (request->tv_nsec / 1000000)));
321   if (remain != nullptr) {
322     remain->tv_nsec = 0;
323     remain->tv_sec = 0;
324   }
325   return 0;
326 }
327
328 char* strptime(const char* __restrict s,
329                const char* __restrict f,
330                struct tm* __restrict tm) {
331   // Isn't the C++ standard lib nice? std::get_time is defined such that its
332   // format parameters are the exact same as strptime. Of course, we have to
333   // create a string stream first, and imbue it with the current C locale, and
334   // we also have to make sure we return the right things if it fails, or
335   // if it succeeds, but this is still far simpler an implementation than any
336   // of the versions in any of the C standard libraries.
337   std::istringstream input(s);
338   input.imbue(std::locale(setlocale(LC_ALL, nullptr)));
339   input >> std::get_time(tm, f);
340   if (input.fail()) {
341     return nullptr;
342   }
343   return const_cast<char*>(s + input.tellg());
344 }
345 }
346 #endif