46 bool PriorityMemQueue::s_requirePrioritySizing =
false;
47 std::atomic<bool>* PriorityMemQueue::s_configsUsed =
nullptr;
48 bool PriorityMemQueue::s_configured =
false;
55 return 1U << priority;
98 if (static_cast<FwQueuePriorityType>(p) > this->
m_maxPriority) {
109 if (atomicQueuesMem ==
nullptr) {
120 FwSizeType hwmSize =
sizeof(std::atomic<U32>) * this->m_numActivePriorities;
121 void* hwmMem = allocator.
checkedAllocate(allocatorId, hwmSize,
alignof(std::atomic<U32>));
122 if (hwmMem ==
nullptr) {
165 FW_ASSERT(priority < Os::Generic::Queue::MAX_PRIORITIES, priority, this->
m_id);
175 FW_ASSERT(priority < Os::Generic::Queue::MAX_PRIORITIES, this->
m_id);
195 #if defined(__GNUC__) || defined(__clang__) 196 I32 msb = 31 - __builtin_clz(value);
201 U32 mask = 0x80000000;
216 "MAX_PRIORITIES must fit in a U32 priority bit mask");
219 if (priorities == 0) {
223 if (priorities == 0) {
259 for (
FwSizeType i = 0; i < numQueueConfigs; ++i) {
265 static_cast<FwAssertArgType>(i), currentConfig->
instanceId,
269 for (
FwSizeType j = i + 1; j < numQueueConfigs; ++j) {
272 static_cast<FwAssertArgType>(i), static_cast<FwAssertArgType>(j));
277 FW_ASSERT(priorityConfigs !=
nullptr, static_cast<FwAssertArgType>(i), currentConfig->
instanceId,
283 FW_ASSERT(pConfig->
maxMsgSize > 0, static_cast<FwAssertArgType>(i), static_cast<FwAssertArgType>(p),
285 FW_ASSERT(pConfig->
numMsgs > 0, static_cast<FwAssertArgType>(i), static_cast<FwAssertArgType>(p),
288 static_cast<FwAssertArgType>(i), static_cast<FwAssertArgType>(p), pConfig->
priority);
305 FW_ASSERT((queueConfigs !=
nullptr) || (numQueueConfigs == 0), 0);
313 FW_ASSERT(!(required && numQueueConfigs == 0), required, static_cast<FwAssertArgType>(numQueueConfigs));
316 if (queueConfigs !=
nullptr) {
323 if (numQueueConfigs > 0) {
324 FwSizeType expSize = numQueueConfigs *
sizeof(std::atomic<bool>);
325 s_configsUsed =
static_cast<std::atomic<bool>*
>(
326 allocator.
checkedAllocate(allocatorId, expSize,
alignof(std::atomic<bool>)));
329 for (
FwSizeType i = 0; i < numQueueConfigs; ++i) {
330 new (&s_configsUsed[i]) std::atomic<bool>(
false);
336 "QueueConfig array must be naturally aligned with QueuePriorityConfig");
337 if (numQueueConfigs > 0) {
339 for (
FwSizeType i = 0; i < numQueueConfigs; ++i) {
346 for (
FwSizeType i = 0; i < numQueueConfigs; ++i) {
347 s_configs[i] = queueConfigs[i];
349 (void)memcpy(priorityBase, queueConfigs[i].priorityConfigs, priorityConfigsSize);
354 s_numConfigs = numQueueConfigs;
355 s_requirePrioritySizing = required;
356 s_allocatorId = allocatorId;
361 FW_ASSERT((s_configs !=
nullptr) || (s_numConfigs == 0), static_cast<FwAssertArgType>(s_numConfigs));
363 if (s_configsUsed !=
nullptr || s_configs !=
nullptr) {
370 if (s_configsUsed !=
nullptr) {
371 allocator.
deallocate(allocatorId, s_configsUsed);
372 s_configsUsed =
nullptr;
376 if (s_configs !=
nullptr) {
384 s_requirePrioritySizing =
false;
385 s_configured =
false;
409 if (queueConfig !=
nullptr) {
424 return Os::QueueInterface::Status::ALLOCATION_FAILED;
433 if (semMem ==
nullptr) {
435 return Os::QueueInterface::Status::ALLOCATION_FAILED;
440 if (queueConfig !=
nullptr) {
442 return createConfiguredQueues(queueConfig, allocator,
id);
445 return createDefaultQueue(depth, messageSize, allocator,
id);
451 if (s_configs !=
nullptr && s_configsUsed !=
nullptr) {
452 for (
FwSizeType i = 0; i < s_numConfigs; ++i) {
453 if (s_configs[i].instanceId ==
id) {
455 bool expected =
false;
456 if (s_configsUsed[i].compare_exchange_strong(expected,
true, std::memory_order_acq_rel)) {
457 return &s_configs[i];
460 FW_ASSERT(
false,
id, s_configs[i].instanceId);
473 FW_ASSERT(queueConfig->priorityConfigs !=
nullptr, this->m_handle.m_id,
474 static_cast<FwAssertArgType>(queueConfig->numPriorities));
476 for (
FwSizeType i = 0; i < queueConfig->numPriorities; ++i) {
477 const QueuePriorityConfig& priorityConfig = queueConfig->priorityConfigs[i];
482 priorityConfig.numMsgs, allocator, allocatorId);
489 this->setPriorityEnabled(priority,
true);
527 atomicQueue->
create(numMsgs, maxMsgSize, allocator, allocatorId);
532 return Os::QueueInterface::Status::ALLOCATION_FAILED;
536 this->setPriorityEnabled(priority,
true);
575 if (s_configs !=
nullptr && s_configsUsed !=
nullptr) {
576 for (
FwSizeType i = 0; i < s_numConfigs; ++i) {
577 if (s_configs[i].instanceId == this->
m_handle.
m_id && s_configsUsed[i].load()) {
578 s_configsUsed[i].store(
false);
594 bool requirePrioritySizing) {
600 if (requirePrioritySizing) {
605 FW_ASSERT(index >= 0, queueId, priority);
625 FW_ASSERT(highWaterMarks !=
nullptr, queueId, static_cast<FwAssertArgType>(index));
626 U32 prevMax = highWaterMarks[index].load(std::memory_order_acquire);
628 constexpr U32 MAX_CAS_RETRIES = 100;
629 for (U32 casRetries = 0; casRetries < MAX_CAS_RETRIES; ++casRetries) {
630 if (currentDepth <= prevMax) {
633 if (highWaterMarks[index].compare_exchange_weak(prevMax, currentDepth, std::memory_order_release,
634 std::memory_order_acquire)) {
650 return QueueInterface::Status::INVALID_PRIORITY;
655 return QueueInterface::Status::UNINITIALIZED;
664 return QueueInterface::Status::SIZE_MISMATCH;
669 if (blockType == QueueInterface::BlockingType::BLOCKING) {
672 success = atomicQueue->
enqueue(buffer, size);
676 return QueueInterface::Status::FULL;
682 U32 currentDepth =
static_cast<U32
>(atomicQueue->
getSize());
684 this->m_handle.m_id);
704 return QueueInterface::Status::UNINITIALIZED;
709 blockType == QueueInterface::BlockingType::BLOCKING || blockType == QueueInterface::BlockingType::NONBLOCKING,
721 bool consumedCredit =
false;
741 const bool dequeued = aq->
dequeue(destination, capacity, actualSize);
745 if (!consumedCredit) {
748 priority = testPriority;
754 if (blockType == QueueInterface::BlockingType::BLOCKING) {
758 static_cast<FwAssertArgType>(semStatus));
759 consumedCredit =
true;
761 return QueueInterface::Status::EMPTY;
767 return QueueInterface::Status::UNKNOWN_ERROR;
775 static_cast<FwAssertArgType>(this->m_handle.m_numActivePriorities));
779 total += atomicQueue->
getSize();
793 static_cast<FwAssertArgType>(this->m_handle.m_numActivePriorities));
~CountingSemaphore() final
Destructor.
FwEnumStoreType m_allocatorId
I8 m_priorityMap[Queue::MAX_PRIORITIES]
void enablePriority(FwQueuePriorityType priority)
Enable a specific priority (in the handle )
std::atomic< U32 > m_priorityMask
PlatformSizeType FwSizeType
FwQueuePriorityType priority
void teardown() override
teardown the queue
Status
status returned from the queue send function
static constexpr FwSizeType MAX_PRIORITIES
constexpr U32 LOOP_GUARD_LIMIT
Configuration for a priority level.
QueueHandle parent class.
int8_t I8
8-bit signed integer
static void configure(QueueConfig *queueConfigs, FwSizeType numQueueConfigs, bool required, FwEnumStoreType allocatorId)
Register per-priority sizes for component instances which do queueing with multiple priorities...
~AtomicQueue()
AtomicQueue destructor.
static constexpr FwSizeType DEFAULT_PRIORITY
FwSizeType getSize() const
Get the current number of elements in the queue.
static MemAllocatorRegistry & getInstance()
get the singleton registry
QueueHandle * getHandle() override
return the underlying queue handle (implementation specific)
bool enqueueBlocking(const U8 *buffer, FwSizeType size, bool blockIfFull)
Enqueue with optional blocking (multi-producer safe, O(1))
virtual ~PriorityMemQueue()
default queue destructor
Types::AtomicQueue * m_atomicQueues
PriorityMemQueueHandle m_handle
static void updateHighWaterMark(std::atomic< U32 > *highWaterMarks, FwSizeType index, U32 currentDepth, FwEnumStoreType queueId)
Update per-priority high water mark atomically.
A lock-free MPMC FIFO circular buffer with fixed-size buffer storage.
Status receive(U8 *destination, FwSizeType capacity, BlockingType blockType, FwSizeType &actualSize, FwQueuePriorityType &priority) override
receive a message from the queue
REQUIRED: required for Os::Queue memory allocation when using queues that allocate memory...
MemAllocator & getAnAllocator(const MemoryAllocation::MemoryAllocatorType type)
static Types::AtomicQueue * resolvePriorityQueue(PriorityMemQueueHandle &handle, FwQueuePriorityType &priority, FwEnumStoreType queueId, bool requirePrioritySizing)
Resolve priority to a valid AtomicQueue, fallback to DEFAULT if needed.
static void resetConfig()
Reset static configuration (test environments only)
Status tryWait() override
non-blocking attempt to decrement the semaphore
FwSizeType getMessagesAvailable() const override
get number of messages available
void init()
Initialize the handle.
void * checkedAllocate(const FwEnumStoreType identifier, FwSizeType &size, bool &recoverable, FwSizeType alignment=alignof(std::max_align_t))
void deallocateArrays(Fw::MemAllocator &allocator, FwEnumStoreType allocatorId)
Deallocate arrays for priority data.
Status create(FwEnumStoreType id, const Fw::ConstStringBase &name, FwSizeType depth, FwSizeType messageSize) override
create queue storage
bool allocateArrays(Fw::MemAllocator &allocator, FwEnumStoreType allocatorId)
Allocate arrays for priority data (sparse allocation) Uses m_priorityMap to determine which prioritie...
bool isCreated() const
Check if queue has been successfully created.
Os::CountingSemaphore * m_notEmptySem
uint8_t U8
8-bit unsigned integer
Configuration for a queue with multiple priorities.
PlatformQueuePriorityType FwQueuePriorityType
The type of queue priorities used.
critical data stored for priority queue
static constexpr U32 priorityBitMask(FwQueuePriorityType priority)
Get the bit mask for a priority.
Memory Allocation base class.
I8 getPriorityIndex(FwQueuePriorityType priority) const
Get array index for a priority (inline for performance)
void teardown()
Teardown the queue and free allocated memory.
FwQueuePriorityType m_maxPriority
A read-only abstract superclass for StringBase.
void disablePriority(FwQueuePriorityType priority)
Disable a specific priority.
static void validateQueueConfigs(PriorityMemQueue::QueueConfig *queueConfigs, FwSizeType numQueueConfigs)
Validate queue configuration structures.
void teardownInternal()
teardown the queue
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))
Status wait() override
wait (decrement), blocking if count is zero
Defines a base class for a memory allocator for classes.
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.
static I32 findMSB(U32 value)
Find most significant bit set (IPC-style priority finding)
virtual void deallocate(const FwEnumStoreType identifier, void *ptr)=0
std::atomic< U32 > * m_highWaterMarks
bool dequeue(U8 *buffer, FwSizeType capacity, FwSizeType &actualSize)
Dequeue a message (multi-consumer safe, non-blocking, O(1))
Status send(const U8 *buffer, FwSizeType size, FwQueuePriorityType priority, BlockingType blockType) override
send a message into the queue
PriorityMemQueue()
queue interface constructor - initializes handle
QueuePriorityConfig * priorityConfigs
FwEnumStoreType instanceId
FwSizeType m_numActivePriorities
FwSizeType getMessageHighWaterMark() const override
get maximum messages stored at any given time