RTOS Wrapper
API reference
Loading...
Searching...
No Matches
SeqlockSnapshot.h
Go to the documentation of this file.
1
30#ifndef HF_UTILS_RTOS_WRAP_SEQLOCKSNAPSHOT_H_
31#define HF_UTILS_RTOS_WRAP_SEQLOCKSNAPSHOT_H_
32
33#include <atomic>
34#include <climits>
35#include <cstddef>
36#include <cstdint>
37#include <cstring>
38#include <type_traits>
39
40#include "OsAbstraction.h"
41#include "OsUtility.h"
42
43namespace hf {
44
45template <typename T>
47public:
48 virtual ~SnapshotReader() noexcept = default;
49
54 virtual uint32_t Read(T& out) const noexcept = 0;
55
57 [[nodiscard]] virtual uint32_t Seq() const noexcept = 0;
58
63 virtual bool WaitForChange(uint32_t timeout_ms) noexcept = 0;
64
66 virtual bool ClearWaitEvent() noexcept = 0;
67};
68
69template <typename T>
71public:
72 virtual ~SnapshotWriter() noexcept = default;
73
79 virtual void Publish(const T& value) noexcept = 0;
80};
81
82template <typename T>
83class SeqlockSnapshot final
84 : public SnapshotReader<T>
85 , public SnapshotWriter<T>
86{
87 static_assert(std::is_trivially_copyable_v<T>,
88 "SeqlockSnapshot<T> requires a trivially copyable T");
89
90public:
91 SeqlockSnapshot() noexcept = default;
92
93 ~SeqlockSnapshot() override
94 {
95 if (event_group_created_) {
96 (void)os_event_group_delete(&event_group_);
97 event_group_created_ = false;
98 }
99 }
100
103
104 /* ── Writer ──────────────────────────────────────────────────── */
105
106 void Publish(const T& value) noexcept override
107 {
108 const uint32_t s0 = seq_.load(std::memory_order_relaxed);
109 seq_.store(s0 + 1U, std::memory_order_release); // mark odd (writing)
110 std::atomic_thread_fence(std::memory_order_release);
111 std::memcpy(&payload_, &value, sizeof(T));
112 std::atomic_thread_fence(std::memory_order_release);
113 seq_.store(s0 + 2U, std::memory_order_release); // back to even
114 SignalChange_();
115 }
116
117 /* ── Reader ──────────────────────────────────────────────────── */
118
119 uint32_t Read(T& out) const noexcept override
120 {
121 for (;;) {
122 const uint32_t s1 = seq_.load(std::memory_order_acquire);
123 if ((s1 & 1U) != 0U) {
124 continue; // writer in progress; spin
125 }
126 std::atomic_thread_fence(std::memory_order_acquire);
127 std::memcpy(&out, &payload_, sizeof(T));
128 std::atomic_thread_fence(std::memory_order_acquire);
129 const uint32_t s2 = seq_.load(std::memory_order_acquire);
130 if (s1 == s2) {
131 return s2;
132 }
133 // torn read — retry
134 }
135 }
136
137 [[nodiscard]] uint32_t Seq() const noexcept override
138 {
139 return seq_.load(std::memory_order_acquire);
140 }
141
142 bool WaitForChange(uint32_t timeout_ms) noexcept override
143 {
144 if (!EnsureEventGroup_()) return false;
145 OS_Ulong actual = 0;
146 const OS_Ulong wait = (timeout_ms == UINT32_MAX)
147 ? static_cast<OS_Ulong>(OS_WAIT_FOREVER)
148 : static_cast<OS_Ulong>(os_convert_msec_to_delay_ticks(timeout_ms));
149 // Consume the signal: the next WaitForChange() waits for a new change.
150 const OS_Uint rc = os_event_group_get(&event_group_, kEventBit_,
151 static_cast<OS_Uint>(OS_OR) |
152 static_cast<OS_Uint>(OS_EVENT_CLEAR),
153 &actual, wait);
154 return (rc == OS_SUCCESS) && ((actual & kEventBit_) != 0U);
155 }
156
157 bool ClearWaitEvent() noexcept override
158 {
159 if (!EnsureEventGroup_()) return false;
160 return os_event_group_clear(&event_group_, kEventBit_) == OS_SUCCESS;
161 }
162
163private:
164 void SignalChange_() noexcept
165 {
166 if (EnsureEventGroup_()) {
167 (void)os_event_group_set(&event_group_, kEventBit_);
168 }
169 }
170
171 bool EnsureEventGroup_() noexcept
172 {
173 if (event_group_created_) return true;
174 if (os_event_group_create(&event_group_, "SeqlockSnap") == OS_SUCCESS) {
175 event_group_created_ = true;
176 }
177 return event_group_created_;
178 }
179
180 static constexpr OS_Ulong kEventBit_ = 0x1U;
181
182 mutable std::atomic<uint32_t> seq_{0};
183 T payload_{};
184 OS_EventGroup event_group_{};
185 bool event_group_created_{false};
186};
187
188} // namespace hf
189
190#endif /* HF_UTILS_RTOS_WRAP_SEQLOCKSNAPSHOT_H_ */
Generic RTOS abstraction — public header.
C-facing helper functions for delays, cycle counters, and creation of RTOS handles (queues,...
constexpr uint32_t os_convert_msec_to_delay_ticks(uint32_t milliseconds)
Converts milliseconds to delay ticks, rounding down but never to zero.
Definition OsUtility.h:74
Definition SeqlockSnapshot.h:86
void SignalChange_() noexcept
Definition SeqlockSnapshot.h:164
SeqlockSnapshot() noexcept=default
SeqlockSnapshot & operator=(const SeqlockSnapshot &)=delete
bool EnsureEventGroup_() noexcept
Definition SeqlockSnapshot.h:171
uint32_t Seq() const noexcept override
Monotonic change counter (each successful Publish adds 2).
Definition SeqlockSnapshot.h:137
uint32_t Read(T &out) const noexcept override
Read the latest coherent snapshot.
Definition SeqlockSnapshot.h:119
SeqlockSnapshot(const SeqlockSnapshot &)=delete
bool WaitForChange(uint32_t timeout_ms) noexcept override
Block up to timeout_ms waiting for any change since the last call to WaitForChange / ClearWaitEvent.
Definition SeqlockSnapshot.h:142
void Publish(const T &value) noexcept override
Atomically replace the published snapshot with value.
Definition SeqlockSnapshot.h:106
bool ClearWaitEvent() noexcept override
Drop any pending change-signal without waiting.
Definition SeqlockSnapshot.h:157
Definition SeqlockSnapshot.h:46
virtual uint32_t Seq() const noexcept=0
Monotonic change counter (each successful Publish adds 2).
virtual bool ClearWaitEvent() noexcept=0
Drop any pending change-signal without waiting.
virtual uint32_t Read(T &out) const noexcept=0
Read the latest coherent snapshot.
virtual bool WaitForChange(uint32_t timeout_ms) noexcept=0
Block up to timeout_ms waiting for any change since the last call to WaitForChange / ClearWaitEvent.
virtual ~SnapshotReader() noexcept=default
Definition SeqlockSnapshot.h:70
virtual ~SnapshotWriter() noexcept=default
Definition ErrorHistory.h:34
@ OS_SUCCESS
Definition os_cmsis.h:50
@ OS_EVENT_CLEAR
Definition os_cmsis.h:53
@ OS_OR
Definition os_cmsis.h:52
@ OS_WAIT_FOREVER
Definition os_cmsis.h:48
static OS_Uint os_event_group_set(OS_EventGroup *g, OS_Ulong flags)
Definition os_cmsis.h:320
static OS_Uint os_event_group_get(OS_EventGroup *g, OS_Ulong flags, OS_Uint option, OS_Ulong *actual, OS_Ulong wait)
Definition os_cmsis.h:330
static OS_Uint os_event_group_delete(OS_EventGroup *g)
Definition os_cmsis.h:316
static OS_Uint os_event_group_clear(OS_EventGroup *g, OS_Ulong flags)
Definition os_cmsis.h:325
osEventFlagsId_t OS_EventGroup
Definition os_cmsis.h:31
unsigned int OS_Uint
Definition os_cmsis.h:36
unsigned long OS_Ulong
Definition os_cmsis.h:35
static OS_Uint os_event_group_create(OS_EventGroup *g, const char *name)
Definition os_cmsis.h:309