Move the clock details over to the Time.h portability header
[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
19 #if !FOLLY_HAVE_CLOCK_GETTIME
20 #if __MACH__
21 #include <errno.h>
22 #include <mach/mach_time.h>
23
24 static const mach_timebase_info_data_t* tbInfo() {
25   static auto info = [] {
26     static mach_timebase_info_data_t info;
27     return (mach_timebase_info(&info) == KERN_SUCCESS) ? &info : nullptr;
28   }();
29   return info;
30 }
31
32 int clock_gettime(clockid_t clk_id, struct timespec* ts) {
33   auto tb_info = tbInfo();
34   if (tb_info == nullptr) {
35     errno = EINVAL;
36     return -1;
37   }
38
39   uint64_t now_ticks = mach_absolute_time();
40   uint64_t now_ns = (now_ticks * tb_info->numer) / tb_info->denom;
41   ts->tv_sec = now_ns / 1000000000;
42   ts->tv_nsec = now_ns % 1000000000;
43
44   return 0;
45 }
46
47 int clock_getres(clockid_t clk_id, struct timespec* ts) {
48   auto tb_info = tbInfo();
49   if (tb_info == nullptr) {
50     errno = EINVAL;
51     return -1;
52   }
53
54   ts->tv_sec = 0;
55   ts->tv_nsec = tb_info->numer / tb_info->denom;
56
57   return 0;
58 }
59 #elif defined(_WIN32)
60 #include <errno.h>
61 #include <stdint.h>
62 #include <stdlib.h>
63
64 #include <folly/portability/Windows.h>
65
66 static constexpr size_t kNsPerSec = 1000000000;
67
68 extern "C" int clock_getres(clockid_t clock_id, struct timespec* res) {
69   if (!res) {
70     errno = EFAULT;
71     return -1;
72   }
73
74   switch (clock_id) {
75     case CLOCK_MONOTONIC: {
76       LARGE_INTEGER freq;
77       if (!QueryPerformanceFrequency(&freq)) {
78         errno = EINVAL;
79         return -1;
80       }
81
82       res->tv_sec = 0;
83       res->tv_nsec = (long)((kNsPerSec + (freq.QuadPart >> 1)) / freq.QuadPart);
84       if (res->tv_nsec < 1) {
85         res->tv_nsec = 1;
86       }
87
88       return 0;
89     }
90
91     case CLOCK_REALTIME:
92     case CLOCK_PROCESS_CPUTIME_ID:
93     case CLOCK_THREAD_CPUTIME_ID: {
94       DWORD adj, timeIncrement;
95       BOOL adjDisabled;
96       if (!GetSystemTimeAdjustment(&adj, &timeIncrement, &adjDisabled)) {
97         errno = EINVAL;
98         return -1;
99       }
100
101       res->tv_sec = 0;
102       res->tv_nsec = timeIncrement * 100;
103       return 0;
104     }
105
106     default:
107       errno = EINVAL;
108       return -1;
109   }
110 }
111
112 extern "C" int clock_gettime(clockid_t clock_id, struct timespec* tp) {
113   if (!tp) {
114     errno = EFAULT;
115     return -1;
116   }
117
118   const auto ftToUint = [](FILETIME ft) -> uint64_t {
119     ULARGE_INTEGER i;
120     i.HighPart = ft.dwHighDateTime;
121     i.LowPart = ft.dwLowDateTime;
122     return i.QuadPart;
123   };
124   const auto timeToTimespec = [](timespec* tp, uint64_t t) -> int {
125     constexpr size_t k100NsPerSec = kNsPerSec / 100;
126
127     // The filetimes t is based on are represented in
128     // 100ns's. (ie. a value of 4 is 400ns)
129     tp->tv_sec = t / k100NsPerSec;
130     tp->tv_nsec = ((long)(t % k100NsPerSec)) * 100;
131     return 0;
132   };
133
134   FILETIME createTime, exitTime, kernalTime, userTime;
135   switch (clock_id) {
136     case CLOCK_REALTIME: {
137       constexpr size_t kDeltaEpochIn100NS = 116444736000000000ULL;
138
139       GetSystemTimeAsFileTime(&createTime);
140       return timeToTimespec(tp, ftToUint(createTime) - kDeltaEpochIn100NS);
141     }
142     case CLOCK_PROCESS_CPUTIME_ID: {
143       if (!GetProcessTimes(
144               GetCurrentProcess(),
145               &createTime,
146               &exitTime,
147               &kernalTime,
148               &userTime)) {
149         errno = EINVAL;
150         return -1;
151       }
152
153       return timeToTimespec(tp, ftToUint(kernalTime) + ftToUint(userTime));
154     }
155     case CLOCK_THREAD_CPUTIME_ID: {
156       if (!GetThreadTimes(
157               GetCurrentThread(),
158               &createTime,
159               &exitTime,
160               &kernalTime,
161               &userTime)) {
162         errno = EINVAL;
163         return -1;
164       }
165
166       return timeToTimespec(tp, ftToUint(kernalTime) + ftToUint(userTime));
167     }
168     case CLOCK_MONOTONIC: {
169       LARGE_INTEGER fl, cl;
170       if (!QueryPerformanceFrequency(&fl) || !QueryPerformanceCounter(&cl)) {
171         errno = EINVAL;
172         return -1;
173       }
174
175       int64_t freq = fl.QuadPart;
176       int64_t counter = cl.QuadPart;
177       tp->tv_sec = counter / freq;
178       tp->tv_nsec = (long)(((counter % freq) * kNsPerSec + (freq >> 1)) / freq);
179       if (tp->tv_nsec >= kNsPerSec) {
180         tp->tv_sec++;
181         tp->tv_nsec -= kNsPerSec;
182       }
183
184       return 0;
185     }
186
187     default:
188       errno = EINVAL;
189       return -1;
190   }
191 }
192 #else
193 #error No clock_gettime(3) compatibility wrapper available for this platform.
194 #endif
195 #endif
196
197 #ifdef _WIN32
198 #include <iomanip>
199 #include <sstream>
200
201 #include <folly/portability/Windows.h>
202
203 extern "C" {
204 char* asctime_r(const tm* tm, char* buf) {
205   char tmpBuf[64];
206   if (asctime_s(tmpBuf, tm)) {
207     return nullptr;
208   }
209   // Nothing we can do if the buff is to small :(
210   return strcpy(buf, tmpBuf);
211 }
212
213 char* ctime_r(const time_t* t, char* buf) {
214   char tmpBuf[64];
215   if (ctime_s(tmpBuf, t)) {
216     return nullptr;
217   }
218   // Nothing we can do if the buff is to small :(
219   return strcpy(buf, tmpBuf);
220 }
221
222 tm* gmtime_r(const time_t* t, tm* res) {
223   if (!gmtime_s(res, t)) {
224     return res;
225   }
226   return nullptr;
227 }
228
229 tm* localtime_r(const time_t* t, tm* o) {
230   if (!localtime_s(o, t)) {
231     return o;
232   }
233   return nullptr;
234 }
235
236 int nanosleep(const struct timespec* request, struct timespec* remain) {
237   Sleep((DWORD)((request->tv_sec * 1000) + (request->tv_nsec / 1000000));
238   remain->tv_nsec = 0;
239   remain->tv_sec = 0;
240   return 0;
241 }
242
243 char* strptime(const char* __restrict s,
244                const char* __restrict f,
245                struct tm* __restrict tm) {
246   // Isn't the C++ standard lib nice? std::get_time is defined such that its
247   // format parameters are the exact same as strptime. Of course, we have to
248   // create a string stream first, and imbue it with the current C locale, and
249   // we also have to make sure we return the right things if it fails, or
250   // if it succeeds, but this is still far simpler an implementation than any
251   // of the versions in any of the C standard libraries.
252   std::istringstream input(s);
253   input.imbue(std::locale(setlocale(LC_ALL, nullptr)));
254   input >> std::get_time(tm, f);
255   if (input.fail()) {
256     return nullptr;
257   }
258   return const_cast<char*>(s + input.tellg());
259 }
260 }
261 #endif