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