blob: 75f1e9b353940a9f6be18069e5aa1babf8418ef3 [file] [log] [blame]
Austin Schuh25356e22019-09-11 19:27:07 -07001#ifndef AOS_IPC_LIB_INDEX_H_
2#define AOS_IPC_LIB_INDEX_H_
3
Austin Schuh83cbb1e2023-06-23 12:59:02 -07004#include <stdint.h>
Austin Schuh25356e22019-09-11 19:27:07 -07005#include <sys/types.h>
Brian Silverman1ed5df52021-09-13 20:14:06 -07006
Austin Schuh25356e22019-09-11 19:27:07 -07007#include <atomic>
Austin Schuh20b2b082019-09-11 20:42:56 -07008#include <string>
Austin Schuh25356e22019-09-11 19:27:07 -07009
Brian Silverman177567e2020-08-12 19:51:33 -070010#include "glog/logging.h"
11
Philipp Schrader790cb542023-07-05 21:06:52 -070012#include "aos/ipc_lib/shm_observers.h"
13
Austin Schuh83cbb1e2023-06-23 12:59:02 -070014#ifndef AOS_QUEUE_ATOMIC_SIZE
15#if UINTPTR_MAX == 0xffffffff
16#define AOS_QUEUE_ATOMIC_SIZE 32
17/* 32-bit */
18#elif UINTPTR_MAX == 0xffffffffffffffff
19#define AOS_QUEUE_ATOMIC_SIZE 64
20/* 64-bit */
21#else
22#error "Unknown pointer size"
23#endif
24#endif
25
Stephan Pleinesd99b1ee2024-02-02 20:56:44 -080026namespace aos::ipc_lib {
Austin Schuh25356e22019-09-11 19:27:07 -070027
28struct AtomicQueueIndex;
29class AtomicIndex;
30class Index;
31
32namespace testing {
33class QueueIndexTest;
34} // namespace testing
35
36// There are 2 types of indices in the queue. 1 is the index into the overall
37// queue. If we have sent 1,000,005 messages, this is that number.
38//
39// The other is the index into the message list. This is essentially a message
40// pointer. It has some of the lower bits of the queue index encoded into it as
41// a safeguard to detect if someone re-used a message out from under us and we
42// couldn't tell otherwise. It is used to map a queue index to a message index.
43//
44// Each of these index types has an atomic version and a non-atomic. The atomic
45// version is a wrapper around a uint32_t to hang helper functions off of.
46// The non-atomic version contains all the logic. These are all in the header
47// file to encourage the compiler to inline aggressively.
48//
49// The user should very infrequently be manipulating the values of these
50// directly. Use the classes instead to do the heavy lifting.
51
52// Structure for holding the index into the queue.
53class QueueIndex {
54 public:
Austin Schuh83cbb1e2023-06-23 12:59:02 -070055#if AOS_QUEUE_ATOMIC_SIZE == 64
56 typedef uint32_t PackedIndexType;
57#else
58 typedef uint16_t PackedIndexType;
59#endif
60
Austin Schuh25356e22019-09-11 19:27:07 -070061 // Returns an invalid queue element which uses a reserved value.
Austin Schuh83cbb1e2023-06-23 12:59:02 -070062 static QueueIndex Invalid() { return QueueIndex(sentinal_value(), 0); }
Austin Schuh25356e22019-09-11 19:27:07 -070063 // Returns a queue element pointing to 0.
64 static QueueIndex Zero(uint32_t count) { return QueueIndex(0, count); }
65
66 // Returns true if the index is valid.
Austin Schuh83cbb1e2023-06-23 12:59:02 -070067 bool valid() const { return index_ != sentinal_value(); }
Austin Schuh25356e22019-09-11 19:27:07 -070068
69 // Returns the modulo base used to wrap to avoid overlapping with the reserved
70 // number.
71 // max_value is one more than the max value we can store.
72 // count is the the number of elements in the queue.
73 static constexpr uint32_t MaxIndex(uint32_t max_value, uint32_t count) {
74 return (max_value / count) * count;
75 }
76
77 // Gets the next index.
Brian Silverman177567e2020-08-12 19:51:33 -070078 QueueIndex Increment() const { return IncrementBy(1u); }
Austin Schuh25356e22019-09-11 19:27:07 -070079
80 // Gets the nth next element.
81 QueueIndex IncrementBy(uint32_t amount) const {
82 uint32_t index = index_ + amount;
83 uint32_t max_index = MaxIndex(sentinal_value(), count_);
84
85 if (index < index_) {
86 // We wrapped. We are shifting up by 0x100000000 - MaxIndex(...).
87 // Which is equivalent to subtracting MaxIndex since everything is modular
88 // with a uint32_t.
89 index -= max_index;
90 }
91
92 // Now, wrap the remainder.
93 index = index % max_index;
94 return QueueIndex(index, count_);
95 }
96
97 // Gets the nth previous element.
98 QueueIndex DecrementBy(uint32_t amount) const {
99 uint32_t index = index_ - amount;
100 if (index > index_) {
101 // We wrapped. We are shifting down by 0x100000000 - MaxIndex(...).
102 // Which is equivalent to adding MaxIndex since everything is modular with
103 // a uint32_t.
104 index += MaxIndex(sentinal_value(), count_);
105 }
106 return QueueIndex(index, count_);
107 }
108
Austin Schuh83cbb1e2023-06-23 12:59:02 -0700109 // Returns true if the lowest bits of the queue index from the Index could
110 // plausibly match this queue index. The number of bits matched depends on
111 // the the size of atomics in use.
112 bool IsPlausible(PackedIndexType queue_index) const {
113 return valid() &&
114 (queue_index ==
115 static_cast<PackedIndexType>(
116 index_ & std::numeric_limits<PackedIndexType>::max()));
Austin Schuh25356e22019-09-11 19:27:07 -0700117 }
118
119 bool operator==(const QueueIndex other) const {
120 return other.index_ == index_;
121 }
122
123 bool operator!=(const QueueIndex other) const {
124 return other.index_ != index_;
125 }
126
127 // Returns the wrapped index into the queue.
128 uint32_t Wrapped() const { return index_ % count_; }
129
130 // Returns the raw index. This should be used very sparingly.
131 uint32_t index() const { return index_; }
132
Austin Schuh20b2b082019-09-11 20:42:56 -0700133 QueueIndex Clear() const { return QueueIndex(0, count_); }
134
135 // Returns a string representing the index.
136 ::std::string DebugString() const;
137
Austin Schuh25356e22019-09-11 19:27:07 -0700138 private:
139 QueueIndex(uint32_t index, uint32_t count) : index_(index), count_(count) {}
140
141 static constexpr uint32_t sentinal_value() { return 0xffffffffu; }
142
143 friend struct AtomicQueueIndex;
144 friend class Index;
145 // For testing.
146 friend class testing::QueueIndexTest;
147
148 // Index and number of elements in the queue.
149 uint32_t index_;
150 // Count is stored here rather than passed in everywhere in the hopes that the
151 // compiler completely optimizes out this class and this variable if it isn't
152 // used.
153 uint32_t count_;
154};
155
156// Atomic storage for setting and getting QueueIndex objects.
157// Count is the number of messages in the queue.
158struct AtomicQueueIndex {
159 public:
160 // Atomically reads the index without any ordering constraints.
Brian Silvermanfc0d2e82020-08-12 19:58:35 -0700161 QueueIndex RelaxedLoad(uint32_t count) const {
Austin Schuh25356e22019-09-11 19:27:07 -0700162 return QueueIndex(index_.load(::std::memory_order_relaxed), count);
163 }
164
165 // Full bidirectional barriers here.
Brian Silvermanfc0d2e82020-08-12 19:58:35 -0700166 QueueIndex Load(uint32_t count) const {
Brian Silverman2035de72019-12-18 21:39:14 -0800167 return QueueIndex(index_.load(::std::memory_order_acquire), count);
168 }
169 inline void Store(QueueIndex value) {
170 index_.store(value.index_, ::std::memory_order_release);
171 }
Austin Schuh25356e22019-09-11 19:27:07 -0700172
173 // Invalidates the element unconditionally.
174 inline void Invalidate() { Store(QueueIndex::Invalid()); }
175
Brian Silverman177567e2020-08-12 19:51:33 -0700176 inline void RelaxedInvalidate() {
177 index_.store(QueueIndex::Invalid().index_, ::std::memory_order_relaxed);
178 }
179
Austin Schuh25356e22019-09-11 19:27:07 -0700180 // Swaps expected for index atomically. Returns true on success, false
181 // otherwise.
182 inline bool CompareAndExchangeStrong(QueueIndex expected, QueueIndex index) {
Brian Silverman1ed5df52021-09-13 20:14:06 -0700183 linux_code::ipc_lib::RunShmObservers run_observers(&index_, true);
Brian Silverman2035de72019-12-18 21:39:14 -0800184 return index_.compare_exchange_strong(expected.index_, index.index_,
185 ::std::memory_order_acq_rel);
Austin Schuh25356e22019-09-11 19:27:07 -0700186 }
187
188 private:
189 ::std::atomic<uint32_t> index_;
190};
191
192// Structure holding the queue index and the index into the message list.
193class Index {
194 public:
Austin Schuh83cbb1e2023-06-23 12:59:02 -0700195#if AOS_QUEUE_ATOMIC_SIZE == 64
196 typedef uint64_t IndexType;
197 typedef uint32_t MessageIndexType;
198#else
199 typedef uint32_t IndexType;
200 typedef uint16_t MessageIndexType;
201#endif
202 typedef QueueIndex::PackedIndexType PackedIndexType;
203
Austin Schuh25356e22019-09-11 19:27:07 -0700204 // Constructs an Index. queue_index is the QueueIndex of this message, and
205 // message_index is the index into the messages structure.
Austin Schuh83cbb1e2023-06-23 12:59:02 -0700206 Index(QueueIndex queue_index, MessageIndexType message_index)
Austin Schuh25356e22019-09-11 19:27:07 -0700207 : Index(queue_index.index_, message_index) {}
Austin Schuh83cbb1e2023-06-23 12:59:02 -0700208 Index(uint32_t queue_index, MessageIndexType message_index)
209 : index_(static_cast<IndexType>(
210 queue_index & std::numeric_limits<PackedIndexType>::max()) |
211 (static_cast<IndexType>(message_index)
212 << std::numeric_limits<PackedIndexType>::digits)) {
Brian Silverman177567e2020-08-12 19:51:33 -0700213 CHECK_LE(message_index, MaxMessages());
214 }
Austin Schuh25356e22019-09-11 19:27:07 -0700215
216 // Index of this message in the message array.
Austin Schuh83cbb1e2023-06-23 12:59:02 -0700217 MessageIndexType message_index() const {
218 return (index_ >> std::numeric_limits<PackedIndexType>::digits) &
219 std::numeric_limits<MessageIndexType>::max();
220 }
Austin Schuh25356e22019-09-11 19:27:07 -0700221
Austin Schuh83cbb1e2023-06-23 12:59:02 -0700222 // Lowest bits of the queue index of this message in the queue. This will
223 // either be 16 or 32 bits, depending on if we have 32 or 64 bit atomics under
224 // the cover.
225 PackedIndexType queue_index() const {
226 return index_ & std::numeric_limits<PackedIndexType>::max();
227 }
Austin Schuh25356e22019-09-11 19:27:07 -0700228
229 // Returns true if the provided queue index plausibly represents this Index.
230 bool IsPlausible(QueueIndex queue_index) const {
231 return queue_index.IsPlausible(this->queue_index());
232 }
233
234 // Returns an invalid Index.
235 static Index Invalid() { return Index(sentinal_value()); }
236 // Checks if this Index is valid or not.
237 bool valid() const { return index_ != sentinal_value(); }
238
239 // Returns the raw Index. This should only be used for debug.
Austin Schuh83cbb1e2023-06-23 12:59:02 -0700240 IndexType get() const { return index_; }
Austin Schuh25356e22019-09-11 19:27:07 -0700241
242 // Returns the maximum number of messages we can store before overflowing.
Austin Schuh83cbb1e2023-06-23 12:59:02 -0700243 static constexpr MessageIndexType MaxMessages() {
244 return std::numeric_limits<MessageIndexType>::max() - 1;
245 }
Austin Schuh25356e22019-09-11 19:27:07 -0700246
247 bool operator==(const Index other) const { return other.index_ == index_; }
Brian Silverman177567e2020-08-12 19:51:33 -0700248 bool operator!=(const Index other) const { return other.index_ != index_; }
Austin Schuh25356e22019-09-11 19:27:07 -0700249
Austin Schuh20b2b082019-09-11 20:42:56 -0700250 // Returns a string representing the index.
Austin Schuh83cbb1e2023-06-23 12:59:02 -0700251 std::string DebugString() const;
Austin Schuh20b2b082019-09-11 20:42:56 -0700252
Austin Schuh25356e22019-09-11 19:27:07 -0700253 private:
Austin Schuh83cbb1e2023-06-23 12:59:02 -0700254 Index(IndexType index) : index_(index) {}
Austin Schuh25356e22019-09-11 19:27:07 -0700255
256 friend class AtomicIndex;
257
Austin Schuh83cbb1e2023-06-23 12:59:02 -0700258 static constexpr IndexType sentinal_value() {
259 return std::numeric_limits<IndexType>::max();
260 }
Austin Schuh25356e22019-09-11 19:27:07 -0700261
Austin Schuh83cbb1e2023-06-23 12:59:02 -0700262 // Note: a value of all 1 bits is a sentinal to represent an invalid entry.
Austin Schuh25356e22019-09-11 19:27:07 -0700263 // This works because we would need to have a queue index of 0x*ffff, *and*
264 // have 0xffff messages in the message list. That constraint is easy to
265 // enforce by limiting the max messages.
Austin Schuh83cbb1e2023-06-23 12:59:02 -0700266 IndexType index_;
Austin Schuh25356e22019-09-11 19:27:07 -0700267};
268
269// Atomic storage for setting and getting Index objects.
270class AtomicIndex {
271 public:
272 // Stores and loads atomically without ordering constraints.
Brian Silvermanfc0d2e82020-08-12 19:58:35 -0700273 Index RelaxedLoad() const {
Austin Schuh25356e22019-09-11 19:27:07 -0700274 return Index(index_.load(::std::memory_order_relaxed));
275 }
276 void RelaxedStore(Index index) {
277 index_.store(index.index_, ::std::memory_order_relaxed);
278 }
279
280 // Invalidates the index atomically, but without any ordering constraints.
281 void RelaxedInvalidate() { RelaxedStore(Index::Invalid()); }
282
Brian Silverman2035de72019-12-18 21:39:14 -0800283 // Full barriers here.
Austin Schuh20b2b082019-09-11 20:42:56 -0700284 void Invalidate() { Store(Index::Invalid()); }
Brian Silverman2035de72019-12-18 21:39:14 -0800285 void Store(Index index) {
286 index_.store(index.index_, ::std::memory_order_release);
287 }
Brian Silvermanfc0d2e82020-08-12 19:58:35 -0700288 Index Load() const { return Index(index_.load(::std::memory_order_acquire)); }
Austin Schuh25356e22019-09-11 19:27:07 -0700289
290 // Swaps expected for index atomically. Returns true on success, false
291 // otherwise.
Brian Silverman177567e2020-08-12 19:51:33 -0700292 bool CompareAndExchangeStrong(Index expected, Index index) {
Brian Silverman1ed5df52021-09-13 20:14:06 -0700293 linux_code::ipc_lib::RunShmObservers run_observers(&index_, true);
Brian Silverman2035de72019-12-18 21:39:14 -0800294 return index_.compare_exchange_strong(expected.index_, index.index_,
295 ::std::memory_order_acq_rel);
Austin Schuh25356e22019-09-11 19:27:07 -0700296 }
297
Brian Silverman177567e2020-08-12 19:51:33 -0700298 bool CompareAndExchangeWeak(Index *expected, Index index) {
299 return index_.compare_exchange_weak(expected->index_, index.index_,
300 ::std::memory_order_acq_rel);
301 }
302
Austin Schuh25356e22019-09-11 19:27:07 -0700303 private:
Austin Schuh83cbb1e2023-06-23 12:59:02 -0700304 ::std::atomic<Index::IndexType> index_;
Austin Schuh25356e22019-09-11 19:27:07 -0700305};
306
Stephan Pleinesd99b1ee2024-02-02 20:56:44 -0800307} // namespace aos::ipc_lib
Austin Schuh25356e22019-09-11 19:27:07 -0700308
309#endif // AOS_IPC_LIB_INDEX_H_