Monado OpenXR Runtime
os_time.h
Go to the documentation of this file.
1// Copyright 2019-2022, Collabora, Ltd.
2// SPDX-License-Identifier: BSL-1.0
3/*!
4 * @file
5 * @brief Wrapper around OS native time functions.
6 *
7 * These should be preferred over directly using native OS time functions in
8 * potentially-portable code. Additionally, in most cases these are preferred
9 * over timepoints from @ref time_state for general usage in drivers, etc.
10 *
11 * @author Drew DeVault <sir@cmpwn.com>
12 * @author Jakob Bornecrantz <jakob@collabora.com>
13 * @author Rylie Pavlik <rylie.pavlik@collabora.com>
14 *
15 * @ingroup aux_os
16 */
17
18#pragma once
19
20#include "xrt/xrt_config_os.h"
21#include "xrt/xrt_compiler.h"
22
23#include "util/u_time.h"
24
25#ifdef XRT_OS_LINUX
26#include <time.h>
27#include <sys/time.h>
28#define XRT_HAVE_TIMESPEC
29#define XRT_HAVE_TIMEVAL
30
31#elif defined(XRT_OS_WINDOWS)
32#if defined(XRT_ENV_MINGW)
33// That define is needed before to include windows.h, to avoid a collision
34// between the 'byte' type defined by windows and std::byte defined in cstddef
35// since C++17
36#define byte win_byte_override
37#include <windows.h>
38#undef byte
39#endif
40
41#include <time.h>
42#include <timeapi.h>
43#define XRT_HAVE_TIMESPEC
44
45#elif defined(XRT_DOXYGEN)
46#include <time.h>
47#define XRT_HAVE_TIMESPEC
48#define XRT_HAVE_TIMEVAL
49
50#else
51#error "No time support on non-Linux platforms yet."
52#endif
53
54#ifdef __cplusplus
55extern "C" {
56#endif
57
58/*!
59 * @defgroup aux_os_time Portable Timekeeping
60 * @ingroup aux_os
61 *
62 * @brief Unifying wrapper around system time retrieval functions.
63 */
64
65
66/*!
67 * @defgroup aux_os_time_extra Extra Timekeeping Utilities
68 * @ingroup aux_os_time
69 *
70 * @brief Less-portable utility functions for manipulating system time, for
71 * interoperation with platform APIs.
72 */
73
74/*!
75 * Return a monotonic clock in nanoseconds.
76 * @ingroup aux_os_time
77 */
78static inline int64_t
80
81/*!
82 * Sleep the given number of nanoseconds.
83 *
84 * Note that on some platforms, this may be somewhat less accurate than you might want.
85 * On all platforms, the system scheduler has the final say.
86 *
87 * @see os_precise_sleeper
88 *
89 * @ingroup aux_os_time
90 */
91static inline void
92os_nanosleep(int64_t nsec);
93
94/*!
95 * A structure for storing state as needed for more precise sleeping, mostly for compositor use.
96 * @ingroup aux_os_time
97 */
99
100/*!
101 * Initialize members of @ref os_precise_sleeper.
102 * @public @memberof os_precise_sleeper
103 */
104static inline void
106
107/*!
108 * De-initialize members of @ref os_precise_sleeper, and free resources, without actually freeing the given
109 * pointer.
110 * @public @memberof os_precise_sleeper
111 */
112static inline void
114
115/*!
116 * Sleep the given number of nanoseconds, trying harder to be precise.
117 *
118 * On some platforms, there is no way to improve sleep precision easily with some OS-specific state, so we forward
119 * to os_nanosleep().
120 *
121 * Note that on all platforms, the system scheduler has the final say.
122 *
123 * @public @memberof os_precise_sleeper
124 */
125static inline void
126os_precise_sleeper_nanosleep(struct os_precise_sleeper *ops, int32_t nsec);
127
128#if defined(XRT_HAVE_TIMESPEC) || defined(XRT_DOXYGEN)
129/*!
130 * Convert a timespec struct to nanoseconds.
131 *
132 * Note that this only does the value combining, no adjustment for epochs is performed.
133 *
134 * @ingroup aux_os_time_extra
135 */
136static inline int64_t
137os_timespec_to_ns(const struct timespec *spec);
138
139/*!
140 * Convert an nanosecond integer to a timespec struct.
141 *
142 * Note that this only does the value splitting, no adjustment for epochs is performed.
143 * @ingroup aux_os_time_extra
144 */
145static inline void
146os_ns_to_timespec(int64_t ns, struct timespec *spec);
147#endif
148
149#if defined(XRT_HAVE_TIMEVAL) || defined(XRT_DOXYGEN)
150/*!
151 * Convert a timeval struct to nanoseconds.
152 * @ingroup aux_os_time_extra
153 */
154static inline int64_t
155os_timeval_to_ns(struct timeval *val);
156#endif
157
158#if defined(XRT_OS_LINUX) || defined(XRT_DOXYGEN)
159/*!
160 * Return a realtime clock in nanoseconds (Linux-only)
161 *
162 * @ingroup aux_os_time_extra
163 */
164static inline int64_t
166#endif
167
168#if defined(XRT_OS_WINDOWS)
169/*!
170 * Return a realtime clock in nanoseconds
171 *
172 * @ingroup aux_os_time_extra
173 */
174int64_t
176#endif
177
178#if defined(XRT_OS_WINDOWS) || defined(XRT_DOXYGEN)
179/*!
180 * @brief Return a qpc freq in nanoseconds.
181 * @ingroup aux_os_time
182 */
183static inline int64_t
185#endif
186
187
188/*
189 *
190 * implementations follow
191 *
192 */
193
194static inline void
195os_nanosleep(int64_t nsec)
196{
197#if defined(XRT_OS_LINUX)
198 struct timespec spec;
199 spec.tv_sec = (nsec / U_1_000_000_000);
200 spec.tv_nsec = (nsec % U_1_000_000_000);
201 nanosleep(&spec, NULL);
202#elif defined(XRT_OS_WINDOWS)
203 Sleep((DWORD)(nsec / U_TIME_1MS_IN_NS));
204#endif
205}
206
208{
209#if defined(XRT_OS_WINDOWS)
210 HANDLE timer;
211#else
212 int unused_;
213#endif
214};
215
216static inline void
218{
219#if defined(XRT_OS_WINDOWS)
220 ops->timer = CreateWaitableTimer(NULL, TRUE, NULL);
221#endif
222}
223
224static inline void
226{
227#if defined(XRT_OS_WINDOWS)
228 if (ops->timer) {
229 CloseHandle(ops->timer);
230 ops->timer = NULL;
231 }
232#endif
233}
234
235static inline void
237{
238#if defined(XRT_OS_WINDOWS)
239 timeBeginPeriod(1);
240 if (ops->timer) {
241 LARGE_INTEGER timeperiod;
242 timeperiod.QuadPart = -(nsec / 100);
243 if (SetWaitableTimer(ops->timer, &timeperiod, 0, NULL, NULL, FALSE)) {
244 // OK we could set up the timer, now let's wait.
245 WaitForSingleObject(ops->timer, INFINITE);
246 timeEndPeriod(1);
247 return;
248 }
249 }
250#endif
251 // If we fall through from an implementation, or there's no implementation needed for a platform, we
252 // delegate to the regular os_nanosleep.
253 os_nanosleep(nsec);
254
255#if defined(XRT_OS_WINDOWS)
256 timeEndPeriod(1);
257#endif
258}
259
260#if defined(XRT_HAVE_TIMESPEC)
261static inline int64_t
262os_timespec_to_ns(const struct timespec *spec)
263{
264 int64_t ns = 0;
265 ns += (int64_t)spec->tv_sec * U_1_000_000_000;
266 ns += (int64_t)spec->tv_nsec;
267 return ns;
268}
269
270static inline void
271os_ns_to_timespec(int64_t ns, struct timespec *spec)
272{
273 spec->tv_sec = (ns / U_1_000_000_000);
274 spec->tv_nsec = (ns % U_1_000_000_000);
275}
276#endif // XRT_HAVE_TIMESPEC
277
278
279#if defined(XRT_HAVE_TIMEVAL) && defined(XRT_OS_LINUX)
280
281#define OS_NS_PER_USEC (1000)
282
283static inline int64_t
284os_timeval_to_ns(struct timeval *val)
285{
286 int64_t ns = 0;
287 ns += (int64_t)val->tv_sec * U_1_000_000_000;
288 ns += (int64_t)val->tv_usec * OS_NS_PER_USEC;
289 return ns;
290}
291#endif // defined(XRT_HAVE_TIMEVAL) && defined(XRT_OS_LINUX)
292
293#if defined(XRT_OS_WINDOWS)
294static inline int64_t
296{
297 static int64_t ns_per_qpc_tick = 0;
298 if (ns_per_qpc_tick == 0) {
299 // Fixed at startup, so we can cache this.
300 LARGE_INTEGER freq;
301 QueryPerformanceFrequency(&freq);
302 ns_per_qpc_tick = U_1_000_000_000 / freq.QuadPart;
303 }
304 return ns_per_qpc_tick;
305}
306#endif // defined(XRT_OS_WINDOWS)
307
308static inline int64_t
310{
311#if defined(XRT_OS_LINUX)
312 struct timespec ts;
313 int ret = clock_gettime(CLOCK_MONOTONIC, &ts);
314 if (ret != 0) {
315 return 0;
316 }
317
318 return os_timespec_to_ns(&ts);
319#elif defined(XRT_OS_WINDOWS)
320 LARGE_INTEGER qpc;
321 QueryPerformanceCounter(&qpc);
322 return qpc.QuadPart * os_ns_per_qpc_tick_get();
323#else
324#error "need port"
325#endif
326}
327
328#ifdef XRT_OS_LINUX
329static inline int64_t
331{
332 struct timespec ts;
333 int ret = clock_gettime(CLOCK_REALTIME, &ts);
334 if (ret != 0) {
335 return 0;
336 }
337
338 return os_timespec_to_ns(&ts);
339}
340#endif
341
342
343#ifdef __cplusplus
344}
345#endif
static int64_t os_timeval_to_ns(struct timeval *val)
Convert a timeval struct to nanoseconds.
Definition: os_time.h:284
static int64_t os_realtime_get_ns(void)
Return a realtime clock in nanoseconds (Linux-only)
Definition: os_time.h:330
static int64_t os_timespec_to_ns(const struct timespec *spec)
Convert a timespec struct to nanoseconds.
Definition: os_time.h:262
static void os_ns_to_timespec(int64_t ns, struct timespec *spec)
Convert an nanosecond integer to a timespec struct.
Definition: os_time.h:271
static int64_t os_ns_per_qpc_tick_get(void)
Return a qpc freq in nanoseconds.
static int64_t os_monotonic_get_ns(void)
Return a monotonic clock in nanoseconds.
Definition: os_time.h:309
static void os_nanosleep(int64_t nsec)
Sleep the given number of nanoseconds.
Definition: os_time.h:195
Definition: os_time.h:208
static void os_precise_sleeper_deinit(struct os_precise_sleeper *ops)
De-initialize members of os_precise_sleeper, and free resources, without actually freeing the given p...
Definition: os_time.h:225
static void os_precise_sleeper_init(struct os_precise_sleeper *ops)
Initialize members of os_precise_sleeper.
Definition: os_time.h:217
static void os_precise_sleeper_nanosleep(struct os_precise_sleeper *ops, int32_t nsec)
Sleep the given number of nanoseconds, trying harder to be precise.
Definition: os_time.h:236
Time-keeping: a clock that is steady, convertible to system time, and ideally high-resolution.
#define U_1_000_000_000
Helper define to make code more readable.
Definition: u_time.h:39
#define U_TIME_1MS_IN_NS
The number of nanoseconds in a millisecond.
Definition: u_time.h:54
Header holding common defines.
Auto detect OS and certain features.