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
23extern "C" {
24#endif
25
26/******************************************************************************
27 * TYPES & CONSTANTS
28 ******************************************************************************/
29
30typedef 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
45static inline cf_clock
46cf_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
53static inline cf_clock
54cf_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
61static inline cf_clock
62cf_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
69static inline cf_clock
70cf_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
79static inline cf_clock
80cf_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
87static inline void
88cf_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
100static inline void
101cf_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
115static inline cf_clock
116cf_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
123static inline cf_clock
124cf_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
145static inline cf_clock
146cf_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
155static inline cf_clock
156cf_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
163static inline void
164cf_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
178static inline void
179cf_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
195static inline cf_clock
196cf_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
203static inline cf_clock
204cf_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
225AS_EXTERN extern double cf_clock_freq;
226AS_EXTERN extern int64_t cf_clock_start;
227AS_EXTERN extern uint64_t cf_wall_clock_start;
228
229// MONOTONIC
230
231static inline cf_clock
232cf_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
241static inline cf_clock
242cf_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
252static inline void
253cf_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
267static inline void
268cf_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
288static inline cf_clock
289cf_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
299static inline cf_clock
300cf_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
318static inline cf_clock
319cf_getus()
320{
321 return cf_getns() / 1000;
322}
323
324static inline cf_clock
325cf_getms()
326{
327 return cf_getns() / (1000 * 1000);
328}
329
330static inline cf_clock
331cf_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.
343bool
344cf_clock_init();
345
346static inline void
347cf_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.
355static inline uint64_t
356cf_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
361static inline uint64_t
362cf_clepoch_us_from_utc_ns(uint64_t utc_ns)
363{
364 return cf_clepoch_ns_from_utc_ns(utc_ns) / 1000;
365}
366
367static inline uint64_t
368cf_clepoch_ms_from_utc_ns(uint64_t utc_ns)
369{
370 return cf_clepoch_ns_from_utc_ns(utc_ns) / 1000000;
371}
372
373static inline uint64_t
374cf_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.
381static inline uint64_t
382cf_utc_ns_from_clepoch_ns(uint64_t clepoch_ns)
383{
384 return CITRUSLEAF_EPOCH_NS + clepoch_ns;
385}
386
387static inline uint64_t
388cf_utc_ns_from_clepoch_us(uint64_t clepoch_us)
389{
390 return CITRUSLEAF_EPOCH_NS + (clepoch_us * 1000);
391}
392
393static inline uint64_t
394cf_utc_ns_from_clepoch_ms(uint64_t clepoch_ms)
395{
396 return CITRUSLEAF_EPOCH_NS + (clepoch_ms * 1000000);
397}
398
399static inline uint64_t
400cf_utc_ns_from_clepoch_sec(uint64_t clepoch_sec)
401{
402 return CITRUSLEAF_EPOCH_NS + (clepoch_sec * 1000000000);
403}
404
405static inline uint32_t
406cf_clepoch_seconds()
407{
408 return (uint32_t)cf_secs_since_clepoch();
409}
410
411// Special client-only conversion utility.
412static inline uint32_t
413cf_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