folly copyright 2015 -> copyright 2016
[folly.git] / folly / detail / Clock.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/detail/Clock.h>
18
19 #if __MACH__
20 #include <errno.h>
21 #include <mach/mach_time.h>
22
23 namespace {
24
25 const mach_timebase_info_data_t* tbInfo() {
26   static auto info = [] {
27     static mach_timebase_info_data_t info;
28     return (mach_timebase_info(&info) == KERN_SUCCESS) ? &info : nullptr;
29   }();
30   return info;
31 };
32
33 }  // anonymous namespace
34
35 int clock_gettime(clockid_t clk_id, struct timespec* ts) {
36   auto tb_info = tbInfo();
37   if (tb_info == nullptr) {
38     errno = EINVAL;
39     return -1;
40   }
41
42   uint64_t now_ticks = mach_absolute_time();
43   uint64_t now_ns = (now_ticks * tb_info->numer) / tb_info->denom;
44   ts->tv_sec = now_ns / 1000000000;
45   ts->tv_nsec = now_ns % 1000000000;
46
47   return 0;
48 }
49
50 int clock_getres(clockid_t clk_id, struct timespec* ts) {
51   auto tb_info = tbInfo();
52   if (tb_info == nullptr) {
53     errno = EINVAL;
54     return -1;
55   }
56
57   ts->tv_sec = 0;
58   ts->tv_nsec = tb_info->numer / tb_info->denom;
59
60   return 0;
61 }
62 #elif defined(_MSC_VER)
63 // The MSVC version has been extracted from the pthreads implemenation here:
64 // https://github.com/songdongsheng/libpthread
65 // Copyright(c) 2011, Dongsheng Song <songdongsheng@live.cn>
66 //
67 // It is under the Apache License Version 2.0, just as the rest of the file is.
68 // It has been mostly stripped down to what we have.
69
70 #include <WinSock2.h>
71
72 #define DELTA_EPOCH_IN_100NS    INT64_C(116444736000000000)
73 #define POW10_7     INT64_C(10000000)
74 #define POW10_9     INT64_C(1000000000)
75
76 int clock_getres(clockid_t clock_id, struct timespec *res)
77 {
78   switch (clock_id) {
79     case CLOCK_MONOTONIC:
80     {
81       LARGE_INTEGER pf;
82
83       if (QueryPerformanceFrequency(&pf) == 0)
84         return -1;
85
86       res->tv_sec = 0;
87       res->tv_nsec = (int)((POW10_9 + (pf.QuadPart >> 1)) / pf.QuadPart);
88       if (res->tv_nsec < 1)
89         res->tv_nsec = 1;
90
91       return 0;
92     }
93
94     case CLOCK_REALTIME:
95     case CLOCK_PROCESS_CPUTIME_ID:
96     case CLOCK_THREAD_CPUTIME_ID:
97     {
98       DWORD   timeAdjustment, timeIncrement;
99       BOOL    isTimeAdjustmentDisabled;
100
101       (void)GetSystemTimeAdjustment(
102         &timeAdjustment,
103         &timeIncrement,
104         &isTimeAdjustmentDisabled
105       );
106       res->tv_sec = 0;
107       res->tv_nsec = timeIncrement * 100;
108
109       return 0;
110     }
111     default:
112       break;
113   }
114
115   return -1;
116 }
117
118 int clock_gettime(clockid_t clock_id, struct timespec *tp)
119 {
120   unsigned __int64 t;
121   LARGE_INTEGER pf, pc;
122   union {
123     unsigned __int64 u64;
124     FILETIME ft;
125   }  ct, et, kt, ut;
126
127   switch (clock_id) {
128     case CLOCK_REALTIME:
129     {
130       GetSystemTimeAsFileTime(&ct.ft);
131       t = ct.u64 - DELTA_EPOCH_IN_100NS;
132       tp->tv_sec = t / POW10_7;
133       tp->tv_nsec = ((int)(t % POW10_7)) * 100;
134
135       return 0;
136     }
137
138     case CLOCK_MONOTONIC:
139     {
140       if (QueryPerformanceFrequency(&pf) == 0)
141         return -1;
142
143       if (QueryPerformanceCounter(&pc) == 0)
144         return -1;
145
146       tp->tv_sec = pc.QuadPart / pf.QuadPart;
147       tp->tv_nsec = (int)(
148         ((pc.QuadPart % pf.QuadPart) * POW10_9 + (pf.QuadPart >> 1)) /
149         pf.QuadPart
150       );
151       if (tp->tv_nsec >= POW10_9) {
152         tp->tv_sec++;
153         tp->tv_nsec -= POW10_9;
154       }
155
156       return 0;
157     }
158
159     case CLOCK_PROCESS_CPUTIME_ID:
160     {
161       if (0 == GetProcessTimes(GetCurrentProcess(),
162                                &ct.ft, &et.ft, &kt.ft, &ut.ft)) {
163         return -1;
164       }
165       t = kt.u64 + ut.u64;
166       tp->tv_sec = t / POW10_7;
167       tp->tv_nsec = ((int)(t % POW10_7)) * 100;
168
169       return 0;
170     }
171
172     case CLOCK_THREAD_CPUTIME_ID:
173     {
174       if (0 == GetThreadTimes(GetCurrentThread(),
175                               &ct.ft, &et.ft, &kt.ft, &ut.ft)) {
176         return -1;
177       }
178       t = kt.u64 + ut.u64;
179       tp->tv_sec = t / POW10_7;
180       tp->tv_nsec = ((int)(t % POW10_7)) * 100;
181
182       return 0;
183     }
184
185     default:
186       break;
187   }
188
189   return -1;
190 }
191 #elif defined(__CYGWIN__) || defined(__MINGW__)
192 // using winpthreads from mingw-w64
193 // <pthreads_time.h> has clock_gettime and friends
194 // make sure to include <pthread.h> as well for typedefs of timespec/etc
195 #else
196 #error No clock_gettime(2) compatibility wrapper available for this platform.
197 #endif