1 | /* |
2 | * Copyright 2008-2018 Aerospike, Inc. |
3 | * |
4 | * Portions may be licensed to Aerospike, Inc. under one or more contributor |
5 | * license agreements. |
6 | * |
7 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not |
8 | * use this file except in compliance with the License. You may obtain a copy of |
9 | * the License at http://www.apache.org/licenses/LICENSE-2.0 |
10 | * |
11 | * Unless required by applicable law or agreed to in writing, software |
12 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
13 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
14 | * License for the specific language governing permissions and limitations under |
15 | * the License. |
16 | */ |
17 | #pragma once |
18 | |
19 | #include <aerospike/as_std.h> |
20 | #include <time.h> |
21 | |
22 | #ifdef __cplusplus |
23 | extern "C" { |
24 | #endif |
25 | |
26 | /****************************************************************************** |
27 | * TYPES & CONSTANTS |
28 | ******************************************************************************/ |
29 | |
30 | typedef uint64_t cf_clock; |
31 | |
32 | #define CITRUSLEAF_EPOCH 1262304000 |
33 | #define CITRUSLEAF_EPOCH_MS (CITRUSLEAF_EPOCH * 1000ULL) |
34 | #define CITRUSLEAF_EPOCH_US (CITRUSLEAF_EPOCH * 1000000ULL) |
35 | #define CITRUSLEAF_EPOCH_NS (CITRUSLEAF_EPOCH * 1000000000ULL) |
36 | |
37 | /****************************************************************************** |
38 | * LINUX INLINE FUNCTIONS |
39 | ******************************************************************************/ |
40 | |
41 | #if defined(__linux__) || defined(__FreeBSD__) |
42 | |
43 | // MONOTONIC |
44 | |
45 | static inline cf_clock |
46 | cf_getns() |
47 | { |
48 | struct timespec ts; |
49 | clock_gettime(CLOCK_MONOTONIC, &ts); |
50 | return (ts.tv_sec * 1000 * 1000 * 1000) + ts.tv_nsec; |
51 | } |
52 | |
53 | static inline cf_clock |
54 | cf_getus() |
55 | { |
56 | struct timespec ts; |
57 | clock_gettime(CLOCK_MONOTONIC, &ts); |
58 | return (ts.tv_sec * 1000 * 1000) + (ts.tv_nsec / 1000); |
59 | } |
60 | |
61 | static inline cf_clock |
62 | cf_getms() |
63 | { |
64 | struct timespec ts; |
65 | clock_gettime(CLOCK_MONOTONIC, &ts); |
66 | return (ts.tv_sec * 1000) + (ts.tv_nsec / (1000 * 1000)); |
67 | } |
68 | |
69 | static inline cf_clock |
70 | cf_get_seconds() |
71 | { |
72 | struct timespec ts; |
73 | clock_gettime(CLOCK_MONOTONIC, &ts); |
74 | return ts.tv_sec; |
75 | } |
76 | |
77 | // WALL CLOCK (system epoch) |
78 | |
79 | static inline cf_clock |
80 | cf_clock_getabsolute() // wall clock time in milliseconds |
81 | { |
82 | struct timespec ts; |
83 | clock_gettime(CLOCK_REALTIME, &ts); |
84 | return (ts.tv_sec * 1000) + (ts.tv_nsec / (1000 * 1000)); |
85 | } |
86 | |
87 | static inline void |
88 | cf_set_wait_timespec(int ms_wait, struct timespec* out) |
89 | { |
90 | clock_gettime(CLOCK_REALTIME, out); |
91 | out->tv_sec += ms_wait / 1000; |
92 | out->tv_nsec += (ms_wait % 1000) * 1000 * 1000; |
93 | |
94 | if (out->tv_nsec > (1000 * 1000 * 1000)) { |
95 | out->tv_nsec -= 1000 * 1000 * 1000; |
96 | out->tv_sec++; |
97 | } |
98 | } |
99 | |
100 | static inline void |
101 | cf_clock_current_add(struct timespec* delta, struct timespec* out) |
102 | { |
103 | clock_gettime(CLOCK_REALTIME, out); |
104 | out->tv_sec += delta->tv_sec; |
105 | out->tv_nsec += delta->tv_nsec; |
106 | |
107 | if (out->tv_nsec > (1000 * 1000 * 1000)) { |
108 | out->tv_nsec -= 1000 * 1000 * 1000; |
109 | out->tv_sec++; |
110 | } |
111 | } |
112 | |
113 | // WALL CLOCK (citrusleaf epoch) |
114 | |
115 | static inline cf_clock |
116 | cf_clepoch_milliseconds() |
117 | { |
118 | struct timespec ts; |
119 | clock_gettime(CLOCK_REALTIME, &ts); |
120 | return (ts.tv_sec * 1000) + (ts.tv_nsec / (1000 * 1000)) - CITRUSLEAF_EPOCH_MS; |
121 | } |
122 | |
123 | static inline cf_clock |
124 | cf_secs_since_clepoch() |
125 | { |
126 | struct timespec ts; |
127 | clock_gettime(CLOCK_REALTIME, &ts); |
128 | return ts.tv_sec - CITRUSLEAF_EPOCH; |
129 | } |
130 | |
131 | #else |
132 | |
133 | /****************************************************************************** |
134 | * APPLE INLINE FUNCTIONS |
135 | ******************************************************************************/ |
136 | |
137 | #if defined(__APPLE__) |
138 | |
139 | #include <mach/mach.h> |
140 | #include <mach/mach_time.h> |
141 | #include <sys/time.h> |
142 | |
143 | // MONOTONIC |
144 | |
145 | static inline cf_clock |
146 | cf_getns() |
147 | { |
148 | // mach_absolute_time() currently returns nanoseconds, but is not |
149 | // guaranteed to do so. This code may have to be revised at a later date. |
150 | return mach_absolute_time(); |
151 | } |
152 | |
153 | // WALL CLOCK (system epoch) |
154 | |
155 | static inline cf_clock |
156 | cf_clock_getabsolute() // wall clock time in milliseconds |
157 | { |
158 | struct timeval tv; |
159 | gettimeofday(&tv, NULL); |
160 | return (tv.tv_sec * 1000) + (tv.tv_usec / 1000); |
161 | } |
162 | |
163 | static inline void |
164 | cf_set_wait_timespec(int ms_wait, struct timespec* out) |
165 | { |
166 | // Has slightly less resolution than the pure linux version. |
167 | struct timeval now; |
168 | gettimeofday(&now, NULL); |
169 | out->tv_sec = now.tv_sec + (ms_wait / 1000); |
170 | out->tv_nsec = now.tv_usec * 1000 + (ms_wait % 1000) * 1000 * 1000; |
171 | |
172 | if (out->tv_nsec > (1000 * 1000 * 1000)) { |
173 | out->tv_nsec -= 1000 * 1000 * 1000; |
174 | out->tv_sec++; |
175 | } |
176 | } |
177 | |
178 | static inline void |
179 | cf_clock_current_add(struct timespec* delta, struct timespec* out) |
180 | { |
181 | // Has slightly less resolution than the pure linux version. |
182 | struct timeval now; |
183 | gettimeofday(&now, NULL); |
184 | out->tv_sec = now.tv_sec + delta->tv_sec; |
185 | out->tv_nsec = now.tv_usec * 1000 + delta->tv_nsec; |
186 | |
187 | if (out->tv_nsec > (1000 * 1000 * 1000)) { |
188 | out->tv_nsec -= 1000 * 1000 * 1000; |
189 | out->tv_sec++; |
190 | } |
191 | } |
192 | |
193 | // WALL CLOCK (citrusleaf epoch) |
194 | |
195 | static inline cf_clock |
196 | cf_clepoch_milliseconds() |
197 | { |
198 | struct timeval tv; |
199 | gettimeofday(&tv, NULL); |
200 | return (tv.tv_sec * 1000) + (tv.tv_usec / 1000) - CITRUSLEAF_EPOCH_MS; |
201 | } |
202 | |
203 | static inline cf_clock |
204 | cf_secs_since_clepoch() |
205 | { |
206 | struct timeval tv; |
207 | gettimeofday(&tv, NULL); |
208 | return tv.tv_sec - CITRUSLEAF_EPOCH; |
209 | } |
210 | |
211 | /****************************************************************************** |
212 | * MICROSOFT INLINE FUNCTIONS |
213 | ******************************************************************************/ |
214 | |
215 | #elif defined(_MSC_VER) |
216 | |
217 | #if defined(WIN32_LEAN_AND_MEAN) |
218 | #include <windows.h> |
219 | #else |
220 | #define WIN32_LEAN_AND_MEAN |
221 | #include <windows.h> |
222 | #undef WIN32_LEAN_AND_MEAN |
223 | #endif |
224 | |
225 | AS_EXTERN extern double cf_clock_freq; |
226 | AS_EXTERN extern int64_t cf_clock_start; |
227 | AS_EXTERN extern uint64_t cf_wall_clock_start; |
228 | |
229 | // MONOTONIC |
230 | |
231 | static inline cf_clock |
232 | cf_getns() |
233 | { |
234 | LARGE_INTEGER t; |
235 | QueryPerformanceCounter(&t); |
236 | return (uint64_t)((t.QuadPart - cf_clock_start) / cf_clock_freq); |
237 | } |
238 | |
239 | // WALL CLOCK (system epoch) |
240 | |
241 | static inline cf_clock |
242 | cf_clock_getabsolute() // wall clock time in milliseconds |
243 | { |
244 | FILETIME f; |
245 | GetSystemTimeAsFileTime(&f); |
246 | |
247 | // Convert 100 nanosecond units to milliseconds. |
248 | uint64_t val = ((uint64_t)f.dwHighDateTime << 32) + (uint64_t)f.dwLowDateTime; |
249 | return (val - cf_wall_clock_start) / (10 * 1000); |
250 | } |
251 | |
252 | static inline void |
253 | cf_set_wait_timespec(int ms_wait, struct timespec* out) |
254 | { |
255 | // Has slightly less resolution than the pure linux version. |
256 | FILETIME f; |
257 | GetSystemTimeAsFileTime(&f); |
258 | |
259 | // Convert 100 nanosecond units to nanoseconds and add wait. |
260 | uint64_t nanos = ((((uint64_t)f.dwHighDateTime << 32) + (uint64_t)f.dwLowDateTime - cf_wall_clock_start) * 100) + |
261 | (ms_wait * 1000 * 1000); |
262 | |
263 | out->tv_sec = nanos / (1000 * 1000 * 1000); |
264 | out->tv_nsec = nanos % (1000 * 1000 * 1000); |
265 | } |
266 | |
267 | static inline void |
268 | cf_clock_current_add(struct timespec* delta, struct timespec* out) |
269 | { |
270 | // Has slightly less resolution than the pure linux version. |
271 | FILETIME f; |
272 | GetSystemTimeAsFileTime(&f); |
273 | |
274 | // Convert 100 nanosecond units to nanoseconds. |
275 | uint64_t nanos = (((uint64_t)f.dwHighDateTime << 32) + (uint64_t)f.dwLowDateTime - cf_wall_clock_start) * 100; |
276 | |
277 | out->tv_sec = nanos / (1000 * 1000 * 1000) + delta->tv_sec; |
278 | out->tv_nsec = (nanos % (1000 * 1000 * 1000)) + delta->tv_nsec; |
279 | |
280 | if (out->tv_nsec > (1000 * 1000 * 1000)) { |
281 | out->tv_nsec -= 1000 * 1000 * 1000; |
282 | out->tv_sec++; |
283 | } |
284 | } |
285 | |
286 | // WALL CLOCK (citrusleaf epoch) |
287 | |
288 | static inline cf_clock |
289 | cf_clepoch_milliseconds() |
290 | { |
291 | FILETIME f; |
292 | GetSystemTimeAsFileTime(&f); |
293 | |
294 | // Convert 100 nanosecond units to milliseconds. |
295 | uint64_t val = ((uint64_t)f.dwHighDateTime << 32) + (uint64_t)f.dwLowDateTime; |
296 | return ((val - cf_wall_clock_start) / (10 * 1000)) - CITRUSLEAF_EPOCH_MS; |
297 | } |
298 | |
299 | static inline cf_clock |
300 | cf_secs_since_clepoch() |
301 | { |
302 | FILETIME f; |
303 | GetSystemTimeAsFileTime(&f); |
304 | |
305 | // Convert 100 nanosecond units to seconds. |
306 | uint64_t val = ((uint64_t)f.dwHighDateTime << 32) + (uint64_t)f.dwLowDateTime; |
307 | return ((val - cf_wall_clock_start) / (10 * 1000 * 1000)) - CITRUSLEAF_EPOCH; |
308 | } |
309 | |
310 | #endif |
311 | |
312 | /****************************************************************************** |
313 | * APPLE AND MICROSOFT INLINE FUNCTIONS |
314 | ******************************************************************************/ |
315 | |
316 | // MONOTONIC |
317 | |
318 | static inline cf_clock |
319 | cf_getus() |
320 | { |
321 | return cf_getns() / 1000; |
322 | } |
323 | |
324 | static inline cf_clock |
325 | cf_getms() |
326 | { |
327 | return cf_getns() / (1000 * 1000); |
328 | } |
329 | |
330 | static inline cf_clock |
331 | cf_get_seconds() |
332 | { |
333 | return cf_getns() / (1000 * 1000 * 1000); |
334 | } |
335 | |
336 | #endif |
337 | |
338 | /****************************************************************************** |
339 | * COMMON FUNCTIONS |
340 | ******************************************************************************/ |
341 | |
342 | // Required on windows only. Should be called once on dll initialization. |
343 | bool |
344 | cf_clock_init(); |
345 | |
346 | static inline void |
347 | cf_clock_set_timespec_ms(int ms, struct timespec* out) |
348 | { |
349 | out->tv_sec = ms / 1000; |
350 | out->tv_nsec = (ms % 1000) * 1000 * 1000; |
351 | } |
352 | |
353 | // Convert from UTC nanosecond times to Citrusleaf epoch times. |
354 | // UTC nanosecond times before the Citrusleaf epoch are floored to return 0. |
355 | static inline uint64_t |
356 | cf_clepoch_ns_from_utc_ns(uint64_t utc_ns) |
357 | { |
358 | return utc_ns > CITRUSLEAF_EPOCH_NS ? utc_ns - CITRUSLEAF_EPOCH_NS : 0; |
359 | } |
360 | |
361 | static inline uint64_t |
362 | cf_clepoch_us_from_utc_ns(uint64_t utc_ns) |
363 | { |
364 | return cf_clepoch_ns_from_utc_ns(utc_ns) / 1000; |
365 | } |
366 | |
367 | static inline uint64_t |
368 | cf_clepoch_ms_from_utc_ns(uint64_t utc_ns) |
369 | { |
370 | return cf_clepoch_ns_from_utc_ns(utc_ns) / 1000000; |
371 | } |
372 | |
373 | static inline uint64_t |
374 | cf_clepoch_sec_from_utc_ns(uint64_t utc_ns) |
375 | { |
376 | return cf_clepoch_ns_from_utc_ns(utc_ns) / 1000000000; |
377 | } |
378 | |
379 | // Convert from Citrusleaf epoch times to UTC nanosecond times. |
380 | // Citrusleaf epoch times that cause overflow of uint64_t are not detected. |
381 | static inline uint64_t |
382 | cf_utc_ns_from_clepoch_ns(uint64_t clepoch_ns) |
383 | { |
384 | return CITRUSLEAF_EPOCH_NS + clepoch_ns; |
385 | } |
386 | |
387 | static inline uint64_t |
388 | cf_utc_ns_from_clepoch_us(uint64_t clepoch_us) |
389 | { |
390 | return CITRUSLEAF_EPOCH_NS + (clepoch_us * 1000); |
391 | } |
392 | |
393 | static inline uint64_t |
394 | cf_utc_ns_from_clepoch_ms(uint64_t clepoch_ms) |
395 | { |
396 | return CITRUSLEAF_EPOCH_NS + (clepoch_ms * 1000000); |
397 | } |
398 | |
399 | static inline uint64_t |
400 | cf_utc_ns_from_clepoch_sec(uint64_t clepoch_sec) |
401 | { |
402 | return CITRUSLEAF_EPOCH_NS + (clepoch_sec * 1000000000); |
403 | } |
404 | |
405 | static inline uint32_t |
406 | cf_clepoch_seconds() |
407 | { |
408 | return (uint32_t)cf_secs_since_clepoch(); |
409 | } |
410 | |
411 | // Special client-only conversion utility. |
412 | static inline uint32_t |
413 | cf_server_void_time_to_ttl(uint32_t server_void_time) |
414 | { |
415 | // This is the server's flag indicating the record never expires... |
416 | if (server_void_time == 0) { |
417 | // ... converted to the new client-side convention for "never expires": |
418 | return (uint32_t)-1; |
419 | } |
420 | |
421 | uint32_t now = cf_clepoch_seconds(); |
422 | |
423 | // Record may not have expired on server, but delay or clock differences may |
424 | // cause it to look expired on client. (We give the record to the app anyway |
425 | // to avoid internal cleanup complications.) Floor at 1, not 0, to avoid old |
426 | // "never expires" interpretation. |
427 | return server_void_time > now ? server_void_time - now : 1; |
428 | } |
429 | |
430 | #ifdef __cplusplus |
431 | } // end extern "C" |
432 | #endif |
433 | |