da44db618eb9d46b2a35b957cc484d102c8af58b
[folly.git] / folly / portability / Time.cpp
1 /*
2  * Copyright 2016 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 = std::chrono::duration_cast<std::chrono::seconds>(d).count();
29   ts->tv_nsec = std::chrono::duration_cast<std::chrono::nanoseconds>(
30                     d % std::chrono::seconds(1))
31                     .count();
32 }
33
34 #if !FOLLY_HAVE_CLOCK_GETTIME
35 #if __MACH__
36 #include <errno.h>
37 #include <mach/mach_init.h>
38 #include <mach/mach_port.h>
39 #include <mach/mach_time.h>
40 #include <mach/mach_types.h>
41 #include <mach/task.h>
42 #include <mach/thread_act.h>
43 #include <mach/vm_map.h>
44
45 static std::chrono::nanoseconds time_value_to_ns(time_value_t t) {
46   return std::chrono::seconds(t.seconds) +
47       std::chrono::microseconds(t.microseconds);
48 }
49
50 static int clock_process_cputime(struct timespec* ts) {
51   // Get CPU usage for live threads.
52   task_thread_times_info thread_times_info;
53   mach_msg_type_number_t thread_times_info_count;
54   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   switch (clock_id) {
176     case CLOCK_MONOTONIC: {
177       LARGE_INTEGER freq = performanceFrequency();
178       if (freq.QuadPart == -1) {
179         errno = EINVAL;
180         return -1;
181       }
182
183       static constexpr size_t kNsPerSec = 1000000000;
184
185       res->tv_sec = 0;
186       res->tv_nsec = (long)((kNsPerSec + (freq.QuadPart >> 1)) / freq.QuadPart);
187       if (res->tv_nsec < 1) {
188         res->tv_nsec = 1;
189       }
190
191       return 0;
192     }
193
194     case CLOCK_REALTIME:
195     case CLOCK_PROCESS_CPUTIME_ID:
196     case CLOCK_THREAD_CPUTIME_ID: {
197       DWORD adj, timeIncrement;
198       BOOL adjDisabled;
199       if (!GetSystemTimeAdjustment(&adj, &timeIncrement, &adjDisabled)) {
200         errno = EINVAL;
201         return -1;
202       }
203
204       res->tv_sec = 0;
205       res->tv_nsec = timeIncrement * 100;
206       return 0;
207     }
208
209     default:
210       errno = EINVAL;
211       return -1;
212   }
213 }
214
215 extern "C" int clock_gettime(clockid_t clock_id, struct timespec* tp) {
216   if (!tp) {
217     errno = EFAULT;
218     return -1;
219   }
220
221   const auto unanosToTimespec = [](timespec* tp, unsigned_nanos t) -> int {
222     static constexpr unsigned_nanos one_sec(std::chrono::seconds(1));
223     tp->tv_sec = std::chrono::duration_cast<std::chrono::seconds>(t).count();
224     tp->tv_nsec = (t % one_sec).count();
225     return 0;
226   };
227
228   FILETIME createTime, exitTime, kernalTime, userTime;
229   switch (clock_id) {
230     case CLOCK_REALTIME: {
231       auto now = std::chrono::system_clock::now().time_since_epoch();
232       duration_to_ts(now, tp);
233       return 0;
234     }
235     case CLOCK_MONOTONIC: {
236       auto now = std::chrono::steady_clock::now().time_since_epoch();
237       duration_to_ts(now, tp);
238       return 0;
239     }
240     case CLOCK_PROCESS_CPUTIME_ID: {
241       if (!GetProcessTimes(
242               GetCurrentProcess(),
243               &createTime,
244               &exitTime,
245               &kernalTime,
246               &userTime)) {
247         errno = EINVAL;
248         return -1;
249       }
250
251       return unanosToTimespec(
252           tp,
253           filetimeToUnsignedNanos(kernalTime) +
254               filetimeToUnsignedNanos(userTime));
255     }
256     case CLOCK_THREAD_CPUTIME_ID: {
257       if (!GetThreadTimes(
258               GetCurrentThread(),
259               &createTime,
260               &exitTime,
261               &kernalTime,
262               &userTime)) {
263         errno = EINVAL;
264         return -1;
265       }
266
267       return unanosToTimespec(
268           tp,
269           filetimeToUnsignedNanos(kernalTime) +
270               filetimeToUnsignedNanos(userTime));
271     }
272
273     default:
274       errno = EINVAL;
275       return -1;
276   }
277 }
278 #else
279 #error No clock_gettime(3) compatibility wrapper available for this platform.
280 #endif
281 #endif
282
283 #ifdef _WIN32
284 #include <iomanip>
285 #include <sstream>
286
287 #include <folly/portability/Windows.h>
288
289 extern "C" {
290 char* asctime_r(const tm* tm, char* buf) {
291   char tmpBuf[64];
292   if (asctime_s(tmpBuf, tm)) {
293     return nullptr;
294   }
295   // Nothing we can do if the buff is to small :(
296   return strcpy(buf, tmpBuf);
297 }
298
299 char* ctime_r(const time_t* t, char* buf) {
300   char tmpBuf[64];
301   if (ctime_s(tmpBuf, 64, t)) {
302     return nullptr;
303   }
304   // Nothing we can do if the buff is to small :(
305   return strcpy(buf, tmpBuf);
306 }
307
308 tm* gmtime_r(const time_t* t, tm* res) {
309   if (!gmtime_s(res, t)) {
310     return res;
311   }
312   return nullptr;
313 }
314
315 tm* localtime_r(const time_t* t, tm* o) {
316   if (!localtime_s(o, t)) {
317     return o;
318   }
319   return nullptr;
320 }
321
322 int nanosleep(const struct timespec* request, struct timespec* remain) {
323   Sleep((DWORD)((request->tv_sec * 1000) + (request->tv_nsec / 1000000)));
324   remain->tv_nsec = 0;
325   remain->tv_sec = 0;
326   return 0;
327 }
328
329 char* strptime(const char* __restrict s,
330                const char* __restrict f,
331                struct tm* __restrict tm) {
332   // Isn't the C++ standard lib nice? std::get_time is defined such that its
333   // format parameters are the exact same as strptime. Of course, we have to
334   // create a string stream first, and imbue it with the current C locale, and
335   // we also have to make sure we return the right things if it fails, or
336   // if it succeeds, but this is still far simpler an implementation than any
337   // of the versions in any of the C standard libraries.
338   std::istringstream input(s);
339   input.imbue(std::locale(setlocale(LC_ALL, nullptr)));
340   input >> std::get_time(tm, f);
341   if (input.fail()) {
342     return nullptr;
343   }
344   return const_cast<char*>(s + input.tellg());
345 }
346 }
347 #endif