aboutsummaryrefslogtreecommitdiff
path: root/pw_chrono/public/pw_chrono/system_clock.h
blob: 72035fb563e3a805255c1f3ae61f99efbfd01fb9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
// Copyright 2020 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
#pragma once

#include <stddef.h>
#include <stdint.h>

#include "pw_preprocessor/util.h"

// The backend implements this header to provide the following SystemClock
// parameters, for more detail on the parameters see the SystemClock usage of
// them below:
//   PW_CHRONO_SYSTEM_CLOCK_PERIOD_SECONDS_NUMERATOR
//   PW_CHRONO_SYSTEM_CLOCK_PERIOD_SECONDS_DENOMINATOR
//   constexpr pw::chrono::Epoch pw::chrono::backend::kSystemClockEpoch;
//   constexpr bool pw::chrono::backend::kSystemClockFreeRunning;
//   constexpr bool pw::chrono::backend::kSystemClockNmiSafe;
#include "pw_chrono_backend/system_clock_config.h"

#ifdef __cplusplus

#include <chrono>
#include <ratio>

namespace pw::chrono {
namespace backend {

/// The ARM AEBI does not permit the opaque 'time_point' to be passed via
/// registers, ergo the underlying fundamental type is forward declared.
/// A SystemCLock tick has the units of one SystemClock::period duration.
/// This must be thread and IRQ safe and provided by the backend.
///
int64_t GetSystemClockTickCount();

}  // namespace backend

/// The `SystemClock` represents an unsteady, monotonic clock.
///
/// The epoch of this clock is unspecified and may not be related to wall time
/// (for example, it can be time since boot). The time between ticks of this
/// clock may vary due to sleep modes and potential interrupt handling.
/// `SystemClock` meets the requirements of C++'s `TrivialClock` and Pigweed's
/// `PigweedClock.`
///
/// `SystemClock` is compatible with C++'s `Clock` & `TrivialClock` including:
/// - `SystemClock::rep`
/// - `SystemClock::period`
/// - `SystemClock::duration`
/// - `SystemClock::time_point`
/// - `SystemClock::is_steady`
/// - `SystemClock::now()`
///
/// Example:
///
/// @code
///   SystemClock::time_point before = SystemClock::now();
///   TakesALongTime();
///   SystemClock::duration time_taken = SystemClock::now() - before;
///   bool took_way_too_long = false;
///   if (time_taken > std::chrono::seconds(42)) {
///     took_way_too_long = true;
///   }
/// @endcode
///
/// This code is thread & IRQ safe, it may be NMI safe depending on is_nmi_safe.
///
struct SystemClock {
  using rep = int64_t;
  /// The period must be provided by the backend.
  using period = std::ratio<PW_CHRONO_SYSTEM_CLOCK_PERIOD_SECONDS_NUMERATOR,
                            PW_CHRONO_SYSTEM_CLOCK_PERIOD_SECONDS_DENOMINATOR>;
  /// Alias for durations representable with this clock.
  using duration = std::chrono::duration<rep, period>;
  using time_point = std::chrono::time_point<SystemClock>;
  /// The epoch must be provided by the backend.
  static constexpr Epoch epoch = backend::kSystemClockEpoch;

  /// The time points of this clock cannot decrease, however the time between
  /// ticks of this clock may slightly vary due to sleep modes. The duration
  /// during sleep may be ignored or backfilled with another clock.
  static constexpr bool is_monotonic = true;
  static constexpr bool is_steady = false;

  /// The now() function may not move forward while in a critical section or
  /// interrupt. This must be provided by the backend.
  static constexpr bool is_free_running = backend::kSystemClockFreeRunning;

  /// The clock must stop while in halting debug mode.
  static constexpr bool is_stopped_in_halting_debug_mode = true;

  /// The now() function can be invoked at any time.
  static constexpr bool is_always_enabled = true;

  /// The now() function may work in non-masking interrupts, depending on the
  /// backend. This must be provided by the backend.
  static constexpr bool is_nmi_safe = backend::kSystemClockNmiSafe;

  /// This is thread and IRQ safe. This must be provided by the backend.
  static time_point now() noexcept {
    return time_point(duration(backend::GetSystemClockTickCount()));
  }

  /// This is purely a helper, identical to directly using std::chrono::ceil, to
  /// convert a duration type which cannot be implicitly converted where the
  /// result is rounded up.
  template <class Rep, class Period>
  static constexpr duration for_at_least(std::chrono::duration<Rep, Period> d) {
    return std::chrono::ceil<duration>(d);
  }

  /// Computes the nearest time_point after the specified duration has elapsed.
  ///
  /// This is useful for translating delay or timeout durations into deadlines.
  ///
  /// The time_point is computed based on now() plus the specified duration
  /// where a singular clock tick is added to handle partial ticks. This ensures
  /// that a duration of at least 1 tick does not result in [0,1] ticks and
  /// instead in [1,2] ticks.
  static time_point TimePointAfterAtLeast(duration after_at_least) {
    return now() + after_at_least + duration(1);
  }
};

/// An abstract interface representing a SystemClock.
///
/// This interface allows decoupling code that uses time from the code that
/// creates a point in time. You can use this to your advantage by injecting
/// Clocks into interfaces rather than having implementations call
/// `SystemClock::now()` directly. However, this comes at a cost of a vtable per
/// implementation and more importantly passing and maintaining references to
/// the VirtualSystemCLock for all of the users.
///
/// The `VirtualSystemClock::RealClock()` function returns a reference to the
/// real global SystemClock.
///
/// Example:
///
/// @code
///   void DoFoo(VirtualSystemClock& system_clock) {
///     SystemClock::time_point now = clock.now();
///     // ... Code which consumes now.
///   }
///
///   // Production code:
///   DoFoo(VirtualSystemCLock::RealClock);
///
///   // Test code:
///   MockClock test_clock();
///   DoFoo(test_clock);
/// @endcode
///
/// This interface is thread and IRQ safe.
class VirtualSystemClock {
 public:
  /// Returns a reference to the real system clock to aid instantiation.
  static VirtualSystemClock& RealClock();

  virtual ~VirtualSystemClock() = default;

  /// Returns the current time.
  virtual SystemClock::time_point now() = 0;
};

}  // namespace pw::chrono

// The backend can opt to include an inlined implementation of the following:
//   int64_t GetSystemClockTickCount();
#if __has_include("pw_chrono_backend/system_clock_inline.h")
#include "pw_chrono_backend/system_clock_inline.h"
#endif  // __has_include("pw_chrono_backend/system_clock_inline.h")

#endif  // __cplusplus

PW_EXTERN_C_START

// C API Users should not create pw_chrono_SystemClock_Duration's directly,
// instead it is strongly recommended to use macros which express the duration
// in time units, instead of non-portable ticks.
//
// The following macros round up just like std::chrono::ceil, this is the
// recommended rounding to maintain the "at least" contract of timeouts and
// deadlines (note the *_CEIL macros are the same only more explicit):
//   PW_SYSTEM_CLOCK_MS(milliseconds)
//   PW_SYSTEM_CLOCK_S(seconds)
//   PW_SYSTEM_CLOCK_MIN(minutes)
//   PW_SYSTEM_CLOCK_H(hours)
//   PW_SYSTEM_CLOCK_MS_CEIL(milliseconds)
//   PW_SYSTEM_CLOCK_S_CEIL(seconds)
//   PW_SYSTEM_CLOCK_MIN_CEIL(minutes)
//   PW_SYSTEM_CLOCK_H_CEIL(hours)
//
// The following macros round down like std::chrono::{floor,duration_cast},
// these are discouraged but sometimes necessary:
//   PW_SYSTEM_CLOCK_MS_FLOOR(milliseconds)
//   PW_SYSTEM_CLOCK_S_FLOOR(seconds)
//   PW_SYSTEM_CLOCK_MIN_FLOOR(minutes)
//   PW_SYSTEM_CLOCK_H_FLOOR(hours)
#include "pw_chrono/internal/system_clock_macros.h"

typedef struct {
  int64_t ticks;
} pw_chrono_SystemClock_Duration;

typedef struct {
  pw_chrono_SystemClock_Duration duration_since_epoch;
} pw_chrono_SystemClock_TimePoint;
typedef int64_t pw_chrono_SystemClock_Nanoseconds;

// Returns the current time, see SystemClock::now() for more detail.
pw_chrono_SystemClock_TimePoint pw_chrono_SystemClock_Now(void);

// Returns the change in time between the current_time - last_time.
pw_chrono_SystemClock_Duration pw_chrono_SystemClock_TimeElapsed(
    pw_chrono_SystemClock_TimePoint last_time,
    pw_chrono_SystemClock_TimePoint current_time);

// For lossless time unit conversion, the seconds per tick ratio that is
// numerator/denominator should be used:
//   PW_CHRONO_SYSTEM_CLOCK_PERIOD_SECONDS_NUMERATOR
//   PW_CHRONO_SYSTEM_CLOCK_PERIOD_SECONDS_DENOMINATOR

// Warning, this may be lossy due to the use of std::chrono::floor,
// rounding towards zero.
pw_chrono_SystemClock_Nanoseconds pw_chrono_SystemClock_DurationToNsFloor(
    pw_chrono_SystemClock_Duration duration);

PW_EXTERN_C_END