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
55 extern "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  */
78 static inline uint64_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  */
91 static inline void
92 os_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  */
98 struct os_precise_sleeper;
99 
100 /*!
101  * Initialize members of @ref os_precise_sleeper.
102  * @public @memberof os_precise_sleeper
103  */
104 static 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  */
112 static 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  */
125 static inline void
126 os_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  */
136 static inline uint64_t
137 os_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  */
145 static inline void
146 os_ns_to_timespec(uint64_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  */
154 static inline uint64_t
155 os_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  */
164 static inline uint64_t
165 os_realtime_get_ns(void);
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  */
174 uint64_t
175 os_realtime_get_ns(void);
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  */
183 static inline int64_t
185 #endif
186 
187 
188 /*
189  *
190  * implementations follow
191  *
192  */
193 
194 static inline void
195 os_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 
216 static inline void
218 {
219 #if defined(XRT_OS_WINDOWS)
220  ops->timer = CreateWaitableTimer(NULL, TRUE, NULL);
221 #endif
222 }
223 
224 static 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 
235 static 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)
261 static inline uint64_t
262 os_timespec_to_ns(const struct timespec *spec)
263 {
264  uint64_t ns = 0;
265  ns += (uint64_t)spec->tv_sec * U_1_000_000_000;
266  ns += (uint64_t)spec->tv_nsec;
267  return ns;
268 }
269 
270 static inline void
271 os_ns_to_timespec(uint64_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 
283 static inline uint64_t
284 os_timeval_to_ns(struct timeval *val)
285 {
286  uint64_t ns = 0;
287  ns += (uint64_t)val->tv_sec * U_1_000_000_000;
288  ns += (uint64_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)
294 static 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 
308 static inline uint64_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
329 static inline uint64_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 uint64_t os_timespec_to_ns(const struct timespec *spec)
Convert a timespec struct to nanoseconds.
Definition: os_time.h:262
static uint64_t os_realtime_get_ns(void)
Return a realtime clock in nanoseconds (Linux-only)
Definition: os_time.h:330
static void os_ns_to_timespec(uint64_t ns, struct timespec *spec)
Convert an nanosecond integer to a timespec struct.
Definition: os_time.h:271
static uint64_t os_timeval_to_ns(struct timeval *val)
Convert a timeval struct to nanoseconds.
Definition: os_time.h:284
static int64_t os_ns_per_qpc_tick_get(void)
Return a qpc freq in nanoseconds.
static uint64_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.