21 U32 AtomicQueue::computeChecksum(
const U8* buffer,
FwSizeType size) {
26 sum = (sum << 1) | (sum >> 31);
33 m_bufferMemory(nullptr),
41 m_notFullSem(nullptr) {}
51 FW_ASSERT(numBuffers > 0, static_cast<FwAssertArgType>(numBuffers));
52 FW_ASSERT(bufferSize > 0, static_cast<FwAssertArgType>(bufferSize));
54 this->m_capacity = numBuffers;
55 this->m_bufferSize = bufferSize;
56 this->m_allocator = &allocator;
57 this->m_allocatorId = allocatorId;
60 bool isPowerOf2 = (numBuffers & (numBuffers - 1)) == 0;
61 this->m_mask = isPowerOf2 ? (numBuffers - 1) : 0;
64 FW_ASSERT(numBuffers <= std::numeric_limits<FwSizeType>::max() /
sizeof(Slot),
65 static_cast<FwAssertArgType>(numBuffers), static_cast<FwAssertArgType>(
sizeof(Slot)));
66 FwSizeType slotsSize = numBuffers *
sizeof(Slot);
67 void* slotMem = allocator.
checkedAllocate(allocatorId, slotsSize,
alignof(Slot));
68 FW_ASSERT(slotMem !=
nullptr, static_cast<FwAssertArgType>(numBuffers), static_cast<FwAssertArgType>(bufferSize));
69 this->m_slots =
static_cast<Slot*
>(slotMem);
72 FW_ASSERT(numBuffers <= std::numeric_limits<FwSizeType>::max() / bufferSize,
73 static_cast<FwAssertArgType>(numBuffers), static_cast<FwAssertArgType>(bufferSize));
74 FwSizeType totalBufferSize = numBuffers * bufferSize;
75 void* bufferMem = allocator.
checkedAllocate(allocatorId, totalBufferSize, 64);
76 FW_ASSERT(bufferMem !=
nullptr, static_cast<FwAssertArgType>(numBuffers), static_cast<FwAssertArgType>(bufferSize));
77 this->m_bufferMemory =
static_cast<U8*
>(bufferMem);
81 Slot* slot =
new (&this->m_slots[i]) Slot();
84 slot->buffer = this->m_bufferMemory + (i * bufferSize);
86 slot->sequence.store(i, std::memory_order_relaxed);
91 static_cast<FwAssertArgType>(numBuffers));
98 FW_ASSERT(semMem !=
nullptr, static_cast<FwAssertArgType>(numBuffers));
102 FW_ASSERT(this->m_notFullSem !=
nullptr, static_cast<FwAssertArgType>(numBuffers));
104 this->m_enqueuePos.store(0, std::memory_order_relaxed);
105 this->m_dequeuePos.store(0, std::memory_order_relaxed);
110 if (this->m_notFullSem !=
nullptr) {
111 FW_ASSERT(this->m_allocator !=
nullptr, 0);
117 this->m_allocator->
deallocate(this->m_allocatorId, this->m_notFullSem);
118 this->m_notFullSem =
nullptr;
122 if (this->m_slots !=
nullptr) {
123 FW_ASSERT(this->m_capacity > 0, static_cast<FwAssertArgType>(this->m_capacity));
124 FW_ASSERT(this->m_allocator !=
nullptr, 0);
127 for (
FwSizeType i = 0; i < this->m_capacity; ++i) {
128 FW_ASSERT(i < this->m_capacity, static_cast<FwAssertArgType>(i),
129 static_cast<FwAssertArgType>(this->m_capacity));
130 this->m_slots[i].~Slot();
134 if (this->m_bufferMemory !=
nullptr) {
135 this->m_allocator->
deallocate(this->m_allocatorId, this->m_bufferMemory);
136 this->m_bufferMemory =
nullptr;
140 this->m_allocator->
deallocate(this->m_allocatorId, this->m_slots);
141 this->m_slots =
nullptr;
144 this->m_enqueuePos.store(0, std::memory_order_relaxed);
145 this->m_dequeuePos.store(0, std::memory_order_relaxed);
146 this->m_capacity = 0;
147 this->m_bufferSize = 0;
149 this->m_allocator =
nullptr;
152 bool AtomicQueue::enqueueInternal(
const U8* buffer,
FwSizeType size) {
155 FW_ASSERT(size > 0, static_cast<FwAssertArgType>(size));
156 FW_ASSERT(size <= this->m_bufferSize, static_cast<FwAssertArgType>(size),
157 static_cast<FwAssertArgType>(this->m_bufferSize));
159 for (
FwSizeType retry = 0; retry < MAX_CAS_RETRIES; ++retry) {
160 FW_ASSERT(retry < MAX_CAS_RETRIES, static_cast<FwAssertArgType>(retry));
166 FwSizeType pos = this->m_enqueuePos.load(std::memory_order_relaxed);
169 FwSizeType deqPos = this->m_dequeuePos.load(std::memory_order_relaxed);
171 if (queueDiff >= static_cast<FwSignedSizeType>(this->m_capacity)) {
175 Slot* slot = &this->m_slots[this->getIndex(pos)];
176 FwSizeType seq = slot->sequence.load(std::memory_order_acquire);
183 if (this->m_enqueuePos.compare_exchange_weak(pos, pos + 1, std::memory_order_release,
184 std::memory_order_relaxed)) {
186 FW_ASSERT(slot->buffer !=
nullptr, static_cast<FwAssertArgType>(pos));
187 (void)std::memcpy(slot->buffer, buffer, size);
191 slot->sequence.store(pos + 1, std::memory_order_release);
194 }
else if (diff < 0) {
205 bool success = this->enqueueInternal(buffer, size);
215 if (success && this->m_notFullSem !=
nullptr) {
216 (void)this->m_notFullSem->
tryWait();
227 if (!blockIfFull || this->m_notFullSem ==
nullptr) {
228 return this->
enqueue(buffer, size);
233 for (
FwSizeType attempt = 0; attempt < MAX_CAS_RETRIES; ++attempt) {
234 FW_ASSERT(attempt < MAX_CAS_RETRIES, static_cast<FwAssertArgType>(attempt));
242 if (this->enqueueInternal(buffer, size)) {
248 status = this->m_notFullSem->
post();
259 FW_ASSERT(capacity > 0, static_cast<FwAssertArgType>(capacity));
261 for (
FwSizeType retry = 0; retry < MAX_CAS_RETRIES; ++retry) {
262 FW_ASSERT(retry < MAX_CAS_RETRIES, static_cast<FwAssertArgType>(retry));
268 FwSizeType pos = this->m_dequeuePos.load(std::memory_order_relaxed);
269 Slot* slot = &this->m_slots[this->getIndex(pos)];
270 FwSizeType seq = slot->sequence.load(std::memory_order_acquire);
277 if (this->m_dequeuePos.compare_exchange_weak(pos, pos + 1, std::memory_order_release,
278 std::memory_order_relaxed)) {
282 FW_ASSERT(slot->buffer !=
nullptr, static_cast<FwAssertArgType>(pos));
283 FW_ASSERT(slot->size > 0, static_cast<FwAssertArgType>(slot->size));
284 FW_ASSERT(slot->size <= capacity, static_cast<FwAssertArgType>(slot->size),
285 static_cast<FwAssertArgType>(capacity));
287 actualSize = slot->size;
288 (void)std::memcpy(buffer, slot->buffer, actualSize);
291 slot->sequence.store(pos + this->m_capacity, std::memory_order_release);
296 if (this->m_notFullSem !=
nullptr) {
299 static_cast<FwAssertArgType>(status));
304 }
else if (diff < 0) {
317 if (this->m_capacity == 0) {
320 return this->
getSize() >= this->m_capacity;
329 if (this->m_capacity == 0) {
335 FwSizeType enq = this->m_enqueuePos.load(std::memory_order_relaxed);
336 FwSizeType deq = this->m_dequeuePos.load(std::memory_order_relaxed);
345 if (static_cast<FwSizeType>(diff) > this->m_capacity) {
346 return this->m_capacity;
353 return this->m_capacity;
358 return this->m_bufferSize;
~CountingSemaphore() final
Destructor.
PlatformSizeType FwSizeType
~AtomicQueue()
AtomicQueue destructor.
AtomicQueue()
AtomicQueue constructor.
PlatformSignedSizeType FwSignedSizeType
FwSizeType getSize() const
Get the current number of elements in the queue.
bool enqueueBlocking(const U8 *buffer, FwSizeType size, bool blockIfFull)
Enqueue with optional blocking (multi-producer safe, O(1))
Status tryWait() override
non-blocking attempt to decrement the semaphore
bool isEmpty() const
Check if the queue is empty.
void * checkedAllocate(const FwEnumStoreType identifier, FwSizeType &size, bool &recoverable, FwSizeType alignment=alignof(std::max_align_t))
bool isFull() const
Check if the queue is full.
uint8_t U8
8-bit unsigned integer
Memory Allocation base class.
void teardown()
Teardown the queue and free allocated memory.
FwSizeType getBufferSize() const
Get the buffer size for each message.
bool enqueue(const U8 *buffer, FwSizeType size)
Enqueue a message (multi-producer safe, non-blocking, O(1))
FwSizeType getCapacity() const
Get the maximum capacity of the queue.
Status wait() override
wait (decrement), blocking if count is zero
Status post() override
post (increment) the semaphore, potentially waking a waiting thread
void create(FwSizeType numBuffers, FwSizeType bufferSize, Fw::MemAllocator &allocator, FwEnumStoreType allocatorId)
Create the queue with embedded buffer storage.
virtual void deallocate(const FwEnumStoreType identifier, void *ptr)=0
bool dequeue(U8 *buffer, FwSizeType capacity, FwSizeType &actualSize)
Dequeue a message (multi-consumer safe, non-blocking, O(1))
PlatformAssertArgType FwAssertArgType
The type of arguments to assert functions.