xref: /aosp_15_r20/external/pigweed/pw_chrono/public/pw_chrono/system_clock.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2020 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 #pragma once
15 
16 #include <stddef.h>
17 #include <stdint.h>
18 
19 #include "pw_preprocessor/util.h"
20 
21 // The backend implements this header to provide the following SystemClock
22 // parameters, for more detail on the parameters see the SystemClock usage of
23 // them below:
24 //   PW_CHRONO_SYSTEM_CLOCK_PERIOD_SECONDS_NUMERATOR
25 //   PW_CHRONO_SYSTEM_CLOCK_PERIOD_SECONDS_DENOMINATOR
26 //   constexpr pw::chrono::Epoch pw::chrono::backend::kSystemClockEpoch;
27 //   constexpr bool pw::chrono::backend::kSystemClockFreeRunning;
28 //   constexpr bool pw::chrono::backend::kSystemClockNmiSafe;
29 #include "pw_chrono_backend/system_clock_config.h"
30 
31 #ifdef __cplusplus
32 
33 #include <chrono>
34 #include <ratio>
35 
36 #include "pw_chrono/virtual_clock.h"
37 
38 namespace pw::chrono {
39 namespace backend {
40 
41 /// The ARM AEBI does not permit the opaque 'time_point' to be passed via
42 /// registers, ergo the underlying fundamental type is forward declared.
43 /// A SystemCLock tick has the units of one SystemClock::period duration.
44 /// This must be thread and IRQ safe and provided by the backend.
45 ///
46 int64_t GetSystemClockTickCount();
47 
48 }  // namespace backend
49 
50 /// The `SystemClock` represents an unsteady, monotonic clock.
51 ///
52 /// The epoch of this clock is unspecified and may not be related to wall time
53 /// (for example, it can be time since boot). The time between ticks of this
54 /// clock may vary due to sleep modes and potential interrupt handling.
55 /// `SystemClock` meets the requirements of C++'s `TrivialClock` and Pigweed's
56 /// `PigweedClock.`
57 ///
58 /// `SystemClock` is compatible with C++'s `Clock` & `TrivialClock` including:
59 /// - `SystemClock::rep`
60 /// - `SystemClock::period`
61 /// - `SystemClock::duration`
62 /// - `SystemClock::time_point`
63 /// - `SystemClock::is_steady`
64 /// - `SystemClock::now()`
65 ///
66 /// Example:
67 ///
68 /// @code
69 ///   SystemClock::time_point before = SystemClock::now();
70 ///   TakesALongTime();
71 ///   SystemClock::duration time_taken = SystemClock::now() - before;
72 ///   bool took_way_too_long = false;
73 ///   if (time_taken > std::chrono::seconds(42)) {
74 ///     took_way_too_long = true;
75 ///   }
76 /// @endcode
77 ///
78 /// This code is thread & IRQ safe, it may be NMI safe depending on is_nmi_safe.
79 ///
80 struct SystemClock {
81   using rep = int64_t;
82   /// The period must be provided by the backend.
83   using period = std::ratio<PW_CHRONO_SYSTEM_CLOCK_PERIOD_SECONDS_NUMERATOR,
84                             PW_CHRONO_SYSTEM_CLOCK_PERIOD_SECONDS_DENOMINATOR>;
85   /// Alias for durations representable with this clock.
86   using duration = std::chrono::duration<rep, period>;
87   using time_point = std::chrono::time_point<SystemClock>;
88   /// The epoch must be provided by the backend.
89   static constexpr Epoch epoch = backend::kSystemClockEpoch;
90 
91   /// The time points of this clock cannot decrease, however the time between
92   /// ticks of this clock may slightly vary due to sleep modes. The duration
93   /// during sleep may be ignored or backfilled with another clock.
94   static constexpr bool is_monotonic = true;
95   static constexpr bool is_steady = false;
96 
97   /// The now() function may not move forward while in a critical section or
98   /// interrupt. This must be provided by the backend.
99   static constexpr bool is_free_running = backend::kSystemClockFreeRunning;
100 
101   /// The clock must stop while in halting debug mode.
102   static constexpr bool is_stopped_in_halting_debug_mode = true;
103 
104   /// The now() function can be invoked at any time.
105   static constexpr bool is_always_enabled = true;
106 
107   /// The now() function may work in non-maskable interrupt contexts (e.g.
108   /// exception/fault handlers), depending on the backend. This must be provided
109   /// by the backend.
110   static constexpr bool is_nmi_safe = backend::kSystemClockNmiSafe;
111 
112   /// This is thread and IRQ safe. This must be provided by the backend.
nowSystemClock113   static time_point now() noexcept {
114     return time_point(duration(backend::GetSystemClockTickCount()));
115   }
116 
117   /// This is purely a helper, identical to directly using std::chrono::ceil, to
118   /// convert a duration type which cannot be implicitly converted where the
119   /// result is rounded up.
120   template <class Rep, class Period>
for_at_leastSystemClock121   static constexpr duration for_at_least(std::chrono::duration<Rep, Period> d) {
122     return std::chrono::ceil<duration>(d);
123   }
124 
125   /// Computes the nearest time_point after the specified duration has elapsed.
126   ///
127   /// This is useful for translating delay or timeout durations into deadlines.
128   ///
129   /// The time_point is computed based on now() plus the specified duration
130   /// where a singular clock tick is added to handle partial ticks. This ensures
131   /// that a duration of at least 1 tick does not result in [0,1] ticks and
132   /// instead in [1,2] ticks.
TimePointAfterAtLeastSystemClock133   static time_point TimePointAfterAtLeast(duration after_at_least) {
134     return now() + after_at_least + duration(1);
135   }
136 };
137 
138 // NOTE: VirtualClock here is specialized on SystemClock in order to provide
139 // the `RealClock` function.
140 //
141 // SystemClock is defined in this file, so there's no risk of an ODR violation
142 // as other libraries are unable to spell VirtualClock<SystemClock> unless
143 // they have first included this file.
144 
145 /// An abstract interface representing a SystemClock.
146 ///
147 /// This interface allows decoupling code that uses time from the code that
148 /// creates a point in time. You can use this to your advantage by injecting
149 /// Clocks into interfaces rather than having implementations call
150 /// `SystemClock::now()` directly. However, this comes at a cost of a vtable per
151 /// implementation and more importantly passing and maintaining references to
152 /// the VirtualClock for all of the users.
153 ///
154 /// The `VirtualSystemClock::RealClock()` function returns a reference to the
155 /// real global SystemClock.
156 ///
157 /// Example:
158 ///
159 /// @code
160 ///   void DoFoo(VirtualSystemClock& system_clock) {
161 ///     SystemClock::time_point now = clock.now();
162 ///     // ... Code which consumes now.
163 ///   }
164 ///
165 ///   // Production code:
166 ///   DoFoo(VirtualSystemCLock::RealClock);
167 ///
168 ///   // Test code:
169 ///   MockClock test_clock();
170 ///   DoFoo(test_clock);
171 /// @endcode
172 ///
173 /// This interface is thread and IRQ safe.
174 template <>
175 class VirtualClock<SystemClock> {
176  public:
177   /// Returns a reference to the real system clock to aid instantiation.
178   static VirtualClock<SystemClock>& RealClock();
179 
180   virtual ~VirtualClock() = default;
181 
182   /// Returns the current time.
183   virtual SystemClock::time_point now() = 0;
184 };
185 
186 using VirtualSystemClock = VirtualClock<SystemClock>;
187 
188 }  // namespace pw::chrono
189 
190 // The backend can opt to include an inlined implementation of the following:
191 //   int64_t GetSystemClockTickCount();
192 #if __has_include("pw_chrono_backend/system_clock_inline.h")
193 #include "pw_chrono_backend/system_clock_inline.h"
194 #endif  // __has_include("pw_chrono_backend/system_clock_inline.h")
195 
196 #endif  // __cplusplus
197 
198 PW_EXTERN_C_START
199 
200 // C API Users should not create pw_chrono_SystemClock_Duration's directly,
201 // instead it is strongly recommended to use macros which express the duration
202 // in time units, instead of non-portable ticks.
203 //
204 // The following macros round up just like std::chrono::ceil, this is the
205 // recommended rounding to maintain the "at least" contract of timeouts and
206 // deadlines (note the *_CEIL macros are the same only more explicit):
207 //   PW_SYSTEM_CLOCK_MS(milliseconds)
208 //   PW_SYSTEM_CLOCK_S(seconds)
209 //   PW_SYSTEM_CLOCK_MIN(minutes)
210 //   PW_SYSTEM_CLOCK_H(hours)
211 //   PW_SYSTEM_CLOCK_MS_CEIL(milliseconds)
212 //   PW_SYSTEM_CLOCK_S_CEIL(seconds)
213 //   PW_SYSTEM_CLOCK_MIN_CEIL(minutes)
214 //   PW_SYSTEM_CLOCK_H_CEIL(hours)
215 //
216 // The following macros round down like std::chrono::{floor,duration_cast},
217 // these are discouraged but sometimes necessary:
218 //   PW_SYSTEM_CLOCK_MS_FLOOR(milliseconds)
219 //   PW_SYSTEM_CLOCK_S_FLOOR(seconds)
220 //   PW_SYSTEM_CLOCK_MIN_FLOOR(minutes)
221 //   PW_SYSTEM_CLOCK_H_FLOOR(hours)
222 #include "pw_chrono/internal/system_clock_macros.h"
223 
224 typedef struct {
225   int64_t ticks;
226 } pw_chrono_SystemClock_Duration;
227 
228 typedef struct {
229   pw_chrono_SystemClock_Duration duration_since_epoch;
230 } pw_chrono_SystemClock_TimePoint;
231 typedef int64_t pw_chrono_SystemClock_Nanoseconds;
232 
233 // Returns the current time, see SystemClock::now() for more detail.
234 pw_chrono_SystemClock_TimePoint pw_chrono_SystemClock_Now(void);
235 
236 // Returns the change in time between the current_time - last_time.
237 pw_chrono_SystemClock_Duration pw_chrono_SystemClock_TimeElapsed(
238     pw_chrono_SystemClock_TimePoint last_time,
239     pw_chrono_SystemClock_TimePoint current_time);
240 
241 // For lossless time unit conversion, the seconds per tick ratio that is
242 // numerator/denominator should be used:
243 //   PW_CHRONO_SYSTEM_CLOCK_PERIOD_SECONDS_NUMERATOR
244 //   PW_CHRONO_SYSTEM_CLOCK_PERIOD_SECONDS_DENOMINATOR
245 
246 // Warning, this may be lossy due to the use of std::chrono::floor,
247 // rounding towards zero.
248 pw_chrono_SystemClock_Nanoseconds pw_chrono_SystemClock_DurationToNsFloor(
249     pw_chrono_SystemClock_Duration duration);
250 
251 PW_EXTERN_C_END
252