blob: c6a485ff3e494bd4e4f4d6f51cf60024436a8e26 [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
4#include <sys/types.h>
Brian Silverman1ed5df52021-09-13 20:14:06 -07005
Austin Schuh25356e22019-09-11 19:27:07 -07006#include <atomic>
Austin Schuh20b2b082019-09-11 20:42:56 -07007#include <string>
Austin Schuh25356e22019-09-11 19:27:07 -07008
Brian Silverman1ed5df52021-09-13 20:14:06 -07009#include "aos/ipc_lib/shm_observers.h"
Brian Silverman177567e2020-08-12 19:51:33 -070010#include "glog/logging.h"
11
Austin Schuh25356e22019-09-11 19:27:07 -070012namespace aos {
13namespace ipc_lib {
14
15struct AtomicQueueIndex;
16class AtomicIndex;
17class Index;
18
19namespace testing {
20class QueueIndexTest;
21} // namespace testing
22
23// There are 2 types of indices in the queue. 1 is the index into the overall
24// queue. If we have sent 1,000,005 messages, this is that number.
25//
26// The other is the index into the message list. This is essentially a message
27// pointer. It has some of the lower bits of the queue index encoded into it as
28// a safeguard to detect if someone re-used a message out from under us and we
29// couldn't tell otherwise. It is used to map a queue index to a message index.
30//
31// Each of these index types has an atomic version and a non-atomic. The atomic
32// version is a wrapper around a uint32_t to hang helper functions off of.
33// The non-atomic version contains all the logic. These are all in the header
34// file to encourage the compiler to inline aggressively.
35//
36// The user should very infrequently be manipulating the values of these
37// directly. Use the classes instead to do the heavy lifting.
38
39// Structure for holding the index into the queue.
40class QueueIndex {
41 public:
42 // Returns an invalid queue element which uses a reserved value.
43 static QueueIndex Invalid() { return QueueIndex(0xffffffff, 0); }
44 // Returns a queue element pointing to 0.
45 static QueueIndex Zero(uint32_t count) { return QueueIndex(0, count); }
46
47 // Returns true if the index is valid.
48 bool valid() const { return index_ != 0xffffffff; }
49
50 // Returns the modulo base used to wrap to avoid overlapping with the reserved
51 // number.
52 // max_value is one more than the max value we can store.
53 // count is the the number of elements in the queue.
54 static constexpr uint32_t MaxIndex(uint32_t max_value, uint32_t count) {
55 return (max_value / count) * count;
56 }
57
58 // Gets the next index.
Brian Silverman177567e2020-08-12 19:51:33 -070059 QueueIndex Increment() const { return IncrementBy(1u); }
Austin Schuh25356e22019-09-11 19:27:07 -070060
61 // Gets the nth next element.
62 QueueIndex IncrementBy(uint32_t amount) const {
63 uint32_t index = index_ + amount;
64 uint32_t max_index = MaxIndex(sentinal_value(), count_);
65
66 if (index < index_) {
67 // We wrapped. We are shifting up by 0x100000000 - MaxIndex(...).
68 // Which is equivalent to subtracting MaxIndex since everything is modular
69 // with a uint32_t.
70 index -= max_index;
71 }
72
73 // Now, wrap the remainder.
74 index = index % max_index;
75 return QueueIndex(index, count_);
76 }
77
78 // Gets the nth previous element.
79 QueueIndex DecrementBy(uint32_t amount) const {
80 uint32_t index = index_ - amount;
81 if (index > index_) {
82 // We wrapped. We are shifting down by 0x100000000 - MaxIndex(...).
83 // Which is equivalent to adding MaxIndex since everything is modular with
84 // a uint32_t.
85 index += MaxIndex(sentinal_value(), count_);
86 }
87 return QueueIndex(index, count_);
88 }
89
90 // Returns true if the lowest 16 bits of the queue index from the Index could
91 // plausibly match this queue index.
92 bool IsPlausible(uint16_t queue_index) const {
93 return valid() && (queue_index == static_cast<uint16_t>(index_ & 0xffff));
94 }
95
96 bool operator==(const QueueIndex other) const {
97 return other.index_ == index_;
98 }
99
100 bool operator!=(const QueueIndex other) const {
101 return other.index_ != index_;
102 }
103
104 // Returns the wrapped index into the queue.
105 uint32_t Wrapped() const { return index_ % count_; }
106
107 // Returns the raw index. This should be used very sparingly.
108 uint32_t index() const { return index_; }
109
Austin Schuh20b2b082019-09-11 20:42:56 -0700110 QueueIndex Clear() const { return QueueIndex(0, count_); }
111
112 // Returns a string representing the index.
113 ::std::string DebugString() const;
114
Austin Schuh25356e22019-09-11 19:27:07 -0700115 private:
116 QueueIndex(uint32_t index, uint32_t count) : index_(index), count_(count) {}
117
118 static constexpr uint32_t sentinal_value() { return 0xffffffffu; }
119
120 friend struct AtomicQueueIndex;
121 friend class Index;
122 // For testing.
123 friend class testing::QueueIndexTest;
124
125 // Index and number of elements in the queue.
126 uint32_t index_;
127 // Count is stored here rather than passed in everywhere in the hopes that the
128 // compiler completely optimizes out this class and this variable if it isn't
129 // used.
130 uint32_t count_;
131};
132
133// Atomic storage for setting and getting QueueIndex objects.
134// Count is the number of messages in the queue.
135struct AtomicQueueIndex {
136 public:
137 // Atomically reads the index without any ordering constraints.
Brian Silvermanfc0d2e82020-08-12 19:58:35 -0700138 QueueIndex RelaxedLoad(uint32_t count) const {
Austin Schuh25356e22019-09-11 19:27:07 -0700139 return QueueIndex(index_.load(::std::memory_order_relaxed), count);
140 }
141
142 // Full bidirectional barriers here.
Brian Silvermanfc0d2e82020-08-12 19:58:35 -0700143 QueueIndex Load(uint32_t count) const {
Brian Silverman2035de72019-12-18 21:39:14 -0800144 return QueueIndex(index_.load(::std::memory_order_acquire), count);
145 }
146 inline void Store(QueueIndex value) {
147 index_.store(value.index_, ::std::memory_order_release);
148 }
Austin Schuh25356e22019-09-11 19:27:07 -0700149
150 // Invalidates the element unconditionally.
151 inline void Invalidate() { Store(QueueIndex::Invalid()); }
152
Brian Silverman177567e2020-08-12 19:51:33 -0700153 inline void RelaxedInvalidate() {
154 index_.store(QueueIndex::Invalid().index_, ::std::memory_order_relaxed);
155 }
156
Austin Schuh25356e22019-09-11 19:27:07 -0700157 // Swaps expected for index atomically. Returns true on success, false
158 // otherwise.
159 inline bool CompareAndExchangeStrong(QueueIndex expected, QueueIndex index) {
Brian Silverman1ed5df52021-09-13 20:14:06 -0700160 linux_code::ipc_lib::RunShmObservers run_observers(&index_, true);
Brian Silverman2035de72019-12-18 21:39:14 -0800161 return index_.compare_exchange_strong(expected.index_, index.index_,
162 ::std::memory_order_acq_rel);
Austin Schuh25356e22019-09-11 19:27:07 -0700163 }
164
165 private:
166 ::std::atomic<uint32_t> index_;
167};
168
169// Structure holding the queue index and the index into the message list.
170class Index {
171 public:
172 // Constructs an Index. queue_index is the QueueIndex of this message, and
173 // message_index is the index into the messages structure.
174 Index(QueueIndex queue_index, uint16_t message_index)
175 : Index(queue_index.index_, message_index) {}
176 Index(uint32_t queue_index, uint16_t message_index)
177 : index_((queue_index & 0xffff) |
Brian Silverman177567e2020-08-12 19:51:33 -0700178 (static_cast<uint32_t>(message_index) << 16)) {
179 CHECK_LE(message_index, MaxMessages());
180 }
Austin Schuh25356e22019-09-11 19:27:07 -0700181
182 // Index of this message in the message array.
183 uint16_t message_index() const { return (index_ >> 16) & 0xffff; }
184
185 // Lowest 16 bits of the queue index of this message in the queue.
186 uint16_t queue_index() const { return index_ & 0xffff; }
187
188 // Returns true if the provided queue index plausibly represents this Index.
189 bool IsPlausible(QueueIndex queue_index) const {
190 return queue_index.IsPlausible(this->queue_index());
191 }
192
193 // Returns an invalid Index.
194 static Index Invalid() { return Index(sentinal_value()); }
195 // Checks if this Index is valid or not.
196 bool valid() const { return index_ != sentinal_value(); }
197
198 // Returns the raw Index. This should only be used for debug.
199 uint32_t get() const { return index_; }
200
201 // Returns the maximum number of messages we can store before overflowing.
202 static constexpr uint16_t MaxMessages() { return 0xfffe; }
203
204 bool operator==(const Index other) const { return other.index_ == index_; }
Brian Silverman177567e2020-08-12 19:51:33 -0700205 bool operator!=(const Index other) const { return other.index_ != index_; }
Austin Schuh25356e22019-09-11 19:27:07 -0700206
Austin Schuh20b2b082019-09-11 20:42:56 -0700207 // Returns a string representing the index.
208 ::std::string DebugString() const;
209
Austin Schuh25356e22019-09-11 19:27:07 -0700210 private:
Brian Silverman177567e2020-08-12 19:51:33 -0700211 Index(uint32_t index) : index_(index) {}
Austin Schuh25356e22019-09-11 19:27:07 -0700212
213 friend class AtomicIndex;
214
215 static constexpr uint32_t sentinal_value() { return 0xffffffffu; }
216
217 // Note: a value of 0xffffffff is a sentinal to represent an invalid entry.
218 // This works because we would need to have a queue index of 0x*ffff, *and*
219 // have 0xffff messages in the message list. That constraint is easy to
220 // enforce by limiting the max messages.
221 uint32_t index_;
222};
223
224// Atomic storage for setting and getting Index objects.
225class AtomicIndex {
226 public:
227 // Stores and loads atomically without ordering constraints.
Brian Silvermanfc0d2e82020-08-12 19:58:35 -0700228 Index RelaxedLoad() const {
Austin Schuh25356e22019-09-11 19:27:07 -0700229 return Index(index_.load(::std::memory_order_relaxed));
230 }
231 void RelaxedStore(Index index) {
232 index_.store(index.index_, ::std::memory_order_relaxed);
233 }
234
235 // Invalidates the index atomically, but without any ordering constraints.
236 void RelaxedInvalidate() { RelaxedStore(Index::Invalid()); }
237
Brian Silverman2035de72019-12-18 21:39:14 -0800238 // Full barriers here.
Austin Schuh20b2b082019-09-11 20:42:56 -0700239 void Invalidate() { Store(Index::Invalid()); }
Brian Silverman2035de72019-12-18 21:39:14 -0800240 void Store(Index index) {
241 index_.store(index.index_, ::std::memory_order_release);
242 }
Brian Silvermanfc0d2e82020-08-12 19:58:35 -0700243 Index Load() const { return Index(index_.load(::std::memory_order_acquire)); }
Austin Schuh25356e22019-09-11 19:27:07 -0700244
245 // Swaps expected for index atomically. Returns true on success, false
246 // otherwise.
Brian Silverman177567e2020-08-12 19:51:33 -0700247 bool CompareAndExchangeStrong(Index expected, Index index) {
Brian Silverman1ed5df52021-09-13 20:14:06 -0700248 linux_code::ipc_lib::RunShmObservers run_observers(&index_, true);
Brian Silverman2035de72019-12-18 21:39:14 -0800249 return index_.compare_exchange_strong(expected.index_, index.index_,
250 ::std::memory_order_acq_rel);
Austin Schuh25356e22019-09-11 19:27:07 -0700251 }
252
Brian Silverman177567e2020-08-12 19:51:33 -0700253 bool CompareAndExchangeWeak(Index *expected, Index index) {
254 return index_.compare_exchange_weak(expected->index_, index.index_,
255 ::std::memory_order_acq_rel);
256 }
257
Austin Schuh25356e22019-09-11 19:27:07 -0700258 private:
259 ::std::atomic<uint32_t> index_;
260};
261
262} // namespace ipc_lib
263} // namespace aos
264
265#endif // AOS_IPC_LIB_INDEX_H_