40 U32 candidateSequence,
43 bool preferred =
false;
44 if (candidatePriority > bestPriority) {
46 }
else if (candidatePriority == bestPriority) {
49 const U32 difference = candidateSequence - bestSequence;
50 const U32 topBit =
static_cast<U32
>(1) << (std::numeric_limits<U32>::digits - 1);
51 preferred = (difference & topBit) != 0;
57 : m_stateTag(packStateTag(
LOCKLESS_SLOT_FREE, 0)), m_sequence(0), m_size(0), m_priority(0) {}
62 m_slotsAllocation(nullptr),
83 static_cast<void>(name);
94 std::atomic<LocklessStateTagType> probe(0);
96 std::atomic<FwQueuePriorityType> priorityProbe(0);
98 std::atomic<U32> counterProbe(0);
104 const FwSizeType maxSize = std::numeric_limits<FwSizeType>::max();
106 FW_ASSERT(depth <= (maxSize / messageSize));
111 FW_ASSERT(depth < (std::numeric_limits<U32>::max() / 2));
117 void* slotsAllocation =
nullptr;
125 FwSizeType slotBytesAllocated = slotBytesRequested;
127 if (slotsAllocation ==
nullptr) {
128 status = QueueInterface::Status::ALLOCATION_FAILED;
129 }
else if (slotBytesAllocated < slotBytesRequested) {
131 status = QueueInterface::Status::ALLOCATION_FAILED;
133 const PlatformPointerCastType base =
reinterpret_cast<PlatformPointerCastType
>(slotsAllocation);
134 const PlatformPointerCastType aligned =
137 slots = Fw::arrayPlacementNew<LocklessSlot>(
138 Fw::ByteArray(static_cast<U8*>(slotsAllocation) + offset, slotBytesAllocated - offset), depth);
143 FwSizeType dataBytesRequested = depth * messageSize;
144 FwSizeType dataBytesAllocated = dataBytesRequested;
145 void* dataAllocation = allocator.
allocate(
id, dataBytesAllocated,
alignof(
U8));
146 if (dataAllocation ==
nullptr) {
147 Fw::arrayPlacementDestruct<LocklessSlot>(slots, depth);
149 status = QueueInterface::Status::ALLOCATION_FAILED;
150 }
else if (dataBytesAllocated < dataBytesRequested) {
151 Fw::arrayPlacementDestruct<LocklessSlot>(slots, depth);
154 status = QueueInterface::Status::ALLOCATION_FAILED;
156 data =
static_cast<U8*
>(dataAllocation);
209 return QueueInterface::Status::SIZE_MISMATCH;
213 const bool blocking = (blockType == QueueInterface::BlockingType::BLOCKING);
224 if (slot.
m_stateTag.compare_exchange_strong(packed, desired, std::memory_order_acq_rel,
225 std::memory_order_relaxed)) {
228 static_cast<void>(::memcpy(this->
m_handle.
m_data + offset, buffer, static_cast<size_t>(size)));
231 slot.
m_priority.store(priority, std::memory_order_relaxed);
233 std::memory_order_relaxed);
238 const U32 nextCount = this->
m_handle.
m_count.fetch_add(1, std::memory_order_acq_rel) + 1;
250 for (
FwSizeType markPass = 0; (markPass < depth) && (nextCount > prevMark); markPass++) {
252 prevMark, nextCount, std::memory_order_relaxed, std::memory_order_relaxed)) {
264 return QueueInterface::Status::FULL;
278 const bool blocking = (blockType == QueueInterface::BlockingType::BLOCKING);
291 U32 bestSequence = 0;
301 const U32 candidateSequence = slot.
m_sequence.load(std::memory_order_relaxed);
305 if (packed != packedRecheck) {
308 if ((bestIndex == depth) ||
311 bestPriority = candidatePriority;
312 bestSequence = candidateSequence;
317 if (bestIndex == depth) {
326 bestPacked, desired, std::memory_order_acq_rel, std::memory_order_relaxed)) {
333 if (storedSize > 0) {
336 ::memcpy(destination, this->
m_handle.
m_data + offset, static_cast<size_t>(storedSize)));
338 actualSize = storedSize;
339 priority = slot.
m_priority.load(std::memory_order_relaxed);
344 static_cast<void>(this->
m_handle.
m_count.fetch_sub(1, std::memory_order_acq_rel));
353 return QueueInterface::Status::EMPTY;
std::atomic< U32 > m_count
Occupancy count (claimed-or-queued slots) used only for the high-water mark.
virtual void * allocate(const FwEnumStoreType identifier, FwSizeType &size, bool &recoverable, FwSizeType alignment=alignof(std::max_align_t))=0
PlatformSizeType FwSizeType
std::atomic< U32 > m_available
slot contains no data; available to a producer
Status
status returned from the queue send function
~LocklessPriorityQueue() override
destructor
LocklessSlot * m_slots
Pre-allocated array of m_depth slots, aligned within m_slotsAllocation.
consumer has reserved the slot and is draining it
QueueHandle parent class.
FwSizeType m_depth
Configured queue depth in messages.
Status receive(U8 *destination, FwSizeType capacity, BlockingType blockType, FwSizeType &actualSize, FwQueuePriorityType &priority) override
receive a message from the queue
FwSizeType getMessageHighWaterMark() const override
get the maximum number of messages that have been queued at once
static MemAllocatorRegistry & getInstance()
get the singleton registry
static constexpr LocklessStateTagType STATE_MASK
Mask for the state portion of m_stateTag.
Status create(FwEnumStoreType id, const Fw::ConstStringBase &name, FwSizeType depth, FwSizeType messageSize) override
create queue storage
std::atomic< FwQueuePriorityType > m_priority
Stored message priority. Atomic because consumers read it during the scan phase.
static bool isCandidatePreferred(FwQueuePriorityType candidatePriority, U32 candidateSequence, FwQueuePriorityType bestPriority, U32 bestSequence)
decide whether a candidate (priority, sequence) is preferred over the current best ...
std::atomic< U32 > m_sequence
LocklessPriorityQueueHandle m_handle
Persistent queue state.
REQUIRED: required for Os::Queue memory allocation when using queues that allocate memory...
MemAllocator & getAnAllocator(const MemoryAllocation::MemoryAllocatorType type)
per-slot data for the lockless priority queue
void * m_slotsAllocation
Raw allocation backing m_slots; retained because allocators may ignore alignment. ...
Status send(const U8 *buffer, FwSizeType size, FwQueuePriorityType priority, BlockingType blockType) override
send a message into the queue
std::atomic< U32 > m_sequence
Sequence assigned to messages on publication for FIFO tiebreak; may wrap (compared modularly)...
LocklessSlot()
Construct a slot in the FREE state with a zero tag.
static constexpr U32 STATE_BITS
Number of low bits used for the state value within m_stateTag.
A variable-length byte array.
std::atomic< LocklessStateTagType > m_stateTag
FwSizeType getMessagesAvailable() const override
get number of messages currently receivable
uint8_t U8
8-bit unsigned integer
producer has reserved the slot and is filling it
PlatformQueuePriorityType FwQueuePriorityType
The type of queue priorities used.
Memory Allocation base class.
constexpr U32 LOCKLESS_QUEUE_BLOCKING_BACKOFF_US
void teardown() override
tear down the queue
A read-only abstract superclass for StringBase.
std::atomic< U32 > m_highMark
Maximum value m_count has ever held. Updated by producers via a bounded CAS loop. ...
static Status delay(const Fw::TimeInterval &interval)
delay the current task
QueueHandle * getHandle() override
return the underlying queue handle
LocklessPriorityQueueHandle()
Default-construct a handle in the uncreated state.
Defines a base class for a memory allocator for classes.
FwEnumStoreType m_id
Identifier passed to the memory allocator at create() time and reused at teardown().
slot contains a published message available to a consumer
static constexpr FwSizeType MAX_RETRY_PASSES
virtual void deallocate(const FwEnumStoreType identifier, void *ptr)=0
U8 * m_data
Pre-allocated array of m_depth * m_messageSize bytes for message payloads.
FwSizeType m_messageSize
Configured maximum size of a single message.