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