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_WINDOWS
19# include <windows.h>
20#endif
21
22#ifdef ZTH_OS_MAC
23
24# ifdef ZTH_CUSTOM_CLOCK_GETTIME
25static mach_timebase_info_data_t clock_info;
26static uint64_t mach_clock_start;
27
28static void clock_global_init()
29{
30 mach_timebase_info(&clock_info);
31 mach_clock_start = mach_absolute_time();
32}
33ZTH_INIT_CALL(clock_global_init)
34
35int clock_gettime(int clk_id, struct timespec* res)
36{
37 if(unlikely(!res)) {
38 errno = EFAULT;
39 return -1;
40 }
41
42 zth_assert(clk_id == CLOCK_MONOTONIC);
43 uint64_t c = (mach_absolute_time() - mach_clock_start);
44 uint64_t ns;
45 if(unlikely(clock_info.numer != clock_info.denom)) // For all recent Mac OSX versions,
46 // mach_absolute_time() is in ns.
47 {
48 uint64_t chigh = (c >> 32U) * clock_info.numer;
49 uint64_t chighrem = ((chigh % clock_info.denom) << 32U) / clock_info.denom;
50 chigh /= clock_info.denom;
51 uint64_t clow =
52 (c & (((uint64_t)1 << 32U) - 1)) * clock_info.numer / clock_info.denom;
53 clow += chighrem;
54 ns = (chigh << 32U)
55 + clow; // 64-bit ns gives us more than 500 y before wrap-around.
56 } else {
57 ns = c;
58 }
59
60 // Split in sec + nsec
61 res->tv_nsec = (long)(ns % zth::TimeInterval::BILLION);
62 res->tv_sec = (time_t)(ns / zth::TimeInterval::BILLION);
63 return 0;
64}
65# endif // ZTH_CUSTOM_CLOCK_GETTIME
66
67int clock_nanosleep(int clk_id, int flags, struct timespec const* request, struct timespec* remain)
68{
69 if(unlikely(!request))
70 return EFAULT;
71 if(unlikely(clk_id != CLOCK_MONOTONIC || flags != TIMER_ABSTIME))
72 return EINVAL;
73
74 struct timespec now;
75 if(unlikely(clock_gettime(CLOCK_MONOTONIC, &now)))
76 return errno;
77
78 if(now.tv_sec > request->tv_sec)
79 // No need to sleep.
80 return 0;
81
82 time_t sec = request->tv_sec - now.tv_sec;
83 if(sec == 0 && request->tv_nsec <= now.tv_nsec) {
84 // No need to sleep.
85 return 0;
86 } else if(sec < std::numeric_limits<useconds_t>::max() / 1000000 - 1) {
87 useconds_t us = (useconds_t)(sec * 1000000);
88 us += (useconds_t)((request->tv_nsec - now.tv_nsec) / 1000);
89 if(likely(!usleep(us)))
90 return 0;
91 } else {
92 // Overflow, sleep (almost) infinitely.
93 if(likely(!usleep(std::numeric_limits<useconds_t>::max())))
94 return 0;
95 }
96
97 if(remain) {
98 struct timespec intr;
99 if(clock_gettime(CLOCK_MONOTONIC, &intr))
100 return errno;
101
102 remain->tv_sec = intr.tv_sec - now.tv_sec;
103 remain->tv_nsec = intr.tv_nsec - now.tv_nsec;
104 if(remain->tv_nsec < 0) {
105 remain->tv_nsec += 1000000000L;
106 remain->tv_sec--;
107 }
108 }
109
110 return EINTR;
111}
112#endif
113
114#ifdef ZTH_OS_BAREMETAL
115// Clock functions are typically not provided by the std library.
116
117// As there is no OS, just do polling for the clock. Please provide a more accurate one, when
118// appropriate.
119__attribute__((weak)) int clock_nanosleep(
120 int clk_id, int flags, struct timespec const* request, struct timespec* UNUSED_PAR(remain))
121{
122 if(unlikely(!request))
123 return EFAULT;
124 if(unlikely(clk_id != CLOCK_MONOTONIC || flags != TIMER_ABSTIME))
125 return EINVAL;
126
127 while(true) {
128 struct timespec now;
129 if(unlikely(clock_gettime(CLOCK_MONOTONIC, &now)))
130 return errno;
131 else if(now.tv_sec > request->tv_sec)
132 return 0;
133 else if(now.tv_sec == request->tv_sec && now.tv_nsec >= request->tv_nsec)
134 return 0;
135 // else just keep polling...
136 }
137}
138#endif
139
140namespace zth {
141int realsleep(struct timespec const& ts)
142{
143#ifdef ZTH_OS_WINDOWS
144 while(true) {
145 struct timespec t;
146 if(unlikely(clock_gettime(CLOCK_MONOTONIC, &t)))
147 return errno;
148
149 if(t.tv_sec > ts.tv_sec)
150 return 0;
151 if(t.tv_sec == ts.tv_sec && t.tv_nsec > ts.tv_nsec)
152 return 0;
153
154 t.tv_sec = ts.tv_sec - t.tv_sec;
155 t.tv_nsec = ts.tv_nsec - t.tv_nsec;
156 if(t.tv_nsec < 0) {
157 t.tv_nsec += 1000000000L;
158 t.tv_sec--;
159 }
160
161 // NOLINTNEXTLINE
162 switch(nanosleep(&t, nullptr) ? errno : 0) {
163 case 0:
164 return 0;
165 case EINTR:
166 // Interrupted, try again.
167 continue;
168 default:
169 return errno;
170 }
171 }
172#else
173 return clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &ts, nullptr) ? errno : 0;
174#endif
175}
176
177} // namespace zth
178
179#ifdef ZTH_OS_MAC
180namespace zth {
182}
183
184static void startTimeInit()
185{
187}
188ZTH_INIT_CALL(startTimeInit)
189#else // !ZTH_OS_MAC
190// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables,misc-use-internal-linkage)
192
193static void startTimeInit()
194{
196}
197ZTH_INIT_CALL(startTimeInit)
198
199// Let the const zth::startTime alias to the non-const startTime_.
200namespace zth {
201extern Timestamp const __attribute__((alias("startTime_"))) startTime;
202}
203#endif // ZTH_OS_MAC
static long const BILLION
Definition time.h:89
Convenient wrapper around struct timespec that contains an absolute timestamp.
Definition time.h:629
static Timestamp now()
Definition time.h:656
#define ZTH_INIT_CALL(f)
Definition init.h:41
#define UNUSED_PAR(name)
Definition macros.h:79
void now(struct timespec &ts)
Returns the current timestamp.
Definition time.h:59
Timestamp const startTime
Definition time.cpp:201
int realsleep(struct timespec const &ts)
Sleeps the current thread.
Definition time.cpp:141
zth::Timestamp startTime_
Definition time.cpp:191
#define zth_assert(expr)
assert(), but better integrated in Zth.
Definition util.h:217
#define likely(expr)
Marks the given expression to likely be evaluated to true.
Definition util.h:45
#define unlikely(expr)
Marks the given expression to likely be evaluated to true.
Definition util.h:60