Zth (libzth)
Loading...
Searching...
No Matches
time.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2019-2026 Jochem Rutgers
3 *
4 * SPDX-License-Identifier: MPL-2.0
5 */
6
7#include <libzth/macros.h>
8
9#include <libzth/init.h>
10#include <libzth/time.h>
11#include <libzth/util.h>
12#include <cerrno>
13
14#ifdef ZTH_OS_MAC
15# include <mach/mach_time.h>
16#endif
17
18#ifdef ZTH_OS_MAC
19
20# ifdef ZTH_CUSTOM_CLOCK_GETTIME
21static mach_timebase_info_data_t clock_info;
22static uint64_t mach_clock_start;
23
24static void clock_global_init()
25{
26 mach_timebase_info(&clock_info);
27 mach_clock_start = mach_absolute_time();
28}
29ZTH_INIT_CALL(clock_global_init)
30
31int clock_gettime(int clk_id, struct timespec* res)
32{
33 if(unlikely(!res))
34 return EFAULT;
35
36 zth_assert(clk_id == CLOCK_MONOTONIC);
37 uint64_t c = (mach_absolute_time() - mach_clock_start);
38 uint64_t ns;
39 if(unlikely(clock_info.numer != clock_info.denom)) // For all recent Mac OSX versions,
40 // mach_absolute_time() is in ns.
41 {
42 uint64_t chigh = (c >> 32U) * clock_info.numer;
43 uint64_t chighrem = ((chigh % clock_info.denom) << 32U) / clock_info.denom;
44 chigh /= clock_info.denom;
45 uint64_t clow =
46 (c & (((uint64_t)1 << 32U) - 1)) * clock_info.numer / clock_info.denom;
47 clow += chighrem;
48 ns = (chigh << 32U)
49 + clow; // 64-bit ns gives us more than 500 y before wrap-around.
50 } else {
51 ns = c;
52 }
53
54 // Split in sec + nsec
55 res->tv_nsec = (long)(ns % zth::TimeInterval::BILLION);
56 res->tv_sec = (time_t)(ns / zth::TimeInterval::BILLION);
57 return 0;
58}
59# endif // ZTH_CUSTOM_CLOCK_GETTIME
60
61int clock_nanosleep(int clk_id, int flags, struct timespec const* request, struct timespec* remain)
62{
63 if(unlikely(!request))
64 return EFAULT;
65 if(unlikely(clk_id != CLOCK_MONOTONIC || flags != TIMER_ABSTIME))
66 return EINVAL;
67
68 struct timespec now;
69 int res = clock_gettime(CLOCK_MONOTONIC, &now);
70 if(unlikely(res))
71 return res;
72
73 if(now.tv_sec > request->tv_sec)
74 // No need to sleep.
75 return 0;
76
77 time_t sec = request->tv_sec - now.tv_sec;
78 if(sec == 0 && request->tv_nsec <= now.tv_nsec) {
79 // No need to sleep.
80 return 0;
81 } else if(sec < std::numeric_limits<useconds_t>::max() / 1000000 - 1) {
82 useconds_t us = (useconds_t)(sec * 1000000);
83 us += (useconds_t)((request->tv_nsec - now.tv_nsec) / 1000);
84 if(likely(!usleep(us)))
85 return 0;
86 } else {
87 // Overflow, sleep (almost) infinitely.
88 if(likely(!usleep(std::numeric_limits<useconds_t>::max())))
89 return 0;
90 }
91
92 if(remain) {
93 struct timespec intr;
94 if((res = clock_gettime(CLOCK_MONOTONIC, &intr)))
95 return res;
96
97 remain->tv_sec = intr.tv_sec - now.tv_sec;
98 remain->tv_nsec = intr.tv_nsec - now.tv_nsec;
99 if(remain->tv_nsec < 0) {
100 remain->tv_nsec += 1000000000L;
101 remain->tv_sec--;
102 }
103 }
104
105 return EINTR;
106}
107#endif
108
109#ifdef ZTH_OS_WINDOWS
110// When using gcc and MinGW, clock_nanosleep() only accepts CLOCK_REALTIME, but we use
111// CLOCK_MONOTONIC.
112# ifdef WINPTHREADS_TIME_BITS
113// The newer MinGW redirects clock_nanosleep() to either clock_nanosleep32() or
114// clock_nanosleep64().
115# if WINPTHREADS_TIME_BITS == 32
116int clock_nanosleep32(
117 clockid_t clock_id, int flags, const struct _timespec32* request,
118 struct _timespec32* remain)
119# else // 64-bit time_t
120int clock_nanosleep64(
121 clockid_t clock_id, int flags, const struct _timespec64* request,
122 struct _timespec64* remain)
123# endif // 64-bit time_t
124# else
125int clock_nanosleep(
126 clockid_t clock_id, int flags, const struct timespec* request, struct timespec* remain)
127# endif
128{
129 if(unlikely(!request))
130 return EFAULT;
131 if(unlikely(clock_id != CLOCK_MONOTONIC || flags != TIMER_ABSTIME))
132 return EINVAL;
133
134 struct timespec t;
135 int res = clock_gettime(CLOCK_MONOTONIC, &t);
136 if(unlikely(res))
137 return res;
138
139 if(t.tv_sec > request->tv_sec)
140 return 0;
141 if(t.tv_sec == request->tv_sec && t.tv_nsec > request->tv_nsec)
142 return 0;
143
144 t.tv_sec = request->tv_sec - t.tv_sec;
145 t.tv_nsec = request->tv_nsec - t.tv_nsec;
146 if(t.tv_nsec < 0) {
147 t.tv_nsec += 1000000000L;
148 t.tv_sec--;
149 }
150
151 // NOLINTNEXTLINE
152 return nanosleep(&t, (struct timespec*)remain) ? errno : 0;
153}
154#endif
155
156#ifdef ZTH_OS_BAREMETAL
157// Clock functions are typically not provided by the std library.
158
159// As there is no OS, just do polling for the clock. Please provide a more accurate one, when
160// appropriate.
161__attribute__((weak)) int clock_nanosleep(
162 int clk_id, int flags, struct timespec const* request, struct timespec* UNUSED_PAR(remain))
163{
164 if(unlikely(!request))
165 return EFAULT;
166 if(unlikely(clk_id != CLOCK_MONOTONIC || flags != TIMER_ABSTIME))
167 return EINVAL;
168
169 while(true) {
170 struct timespec now;
171 int res = clock_gettime(CLOCK_MONOTONIC, &now);
172 if(unlikely(res))
173 return res;
174 else if(now.tv_sec > request->tv_sec)
175 return 0;
176 else if(now.tv_sec == request->tv_sec && now.tv_nsec >= request->tv_nsec)
177 return 0;
178 // else just keep polling...
179 }
180}
181#endif
182
183#ifdef ZTH_OS_MAC
184namespace zth {
186}
187
188static void startTimeInit()
189{
191}
192ZTH_INIT_CALL(startTimeInit)
193#else // !ZTH_OS_MAC
194// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
196
197static void startTimeInit()
198{
200}
201ZTH_INIT_CALL(startTimeInit)
202
203// Let the const zth::startTime alias to the non-const startTime_.
204namespace zth {
205extern Timestamp const __attribute__((alias("startTime_"))) startTime;
206}
207#endif // ZTH_OS_MAC
static long const BILLION
Definition time.h:62
Convenient wrapper around struct timespec that contains an absolute timestamp.
Definition time.h:568
static Timestamp now()
Definition time.h:595
#define ZTH_INIT_CALL(f)
Definition init.h:48
#define UNUSED_PAR(name)
Definition macros.h:78
Timestamp const startTime
Definition time.cpp:205
zth::Timestamp startTime_
Definition time.cpp:195
#define zth_assert(expr)
assert(), but better integrated in Zth.
Definition util.h:212
#define likely(expr)
Marks the given expression to likely be evaluated to true.
Definition util.h:40
#define unlikely(expr)
Marks the given expression to likely be evaluated to true.
Definition util.h:55