F´ Flight Software - C/C++ Documentation
A framework for building embedded system applications to NASA flight quality standards.
PriorityQueue.cpp
Go to the documentation of this file.
1 // ======================================================================
2 // \title Os/Generic/PriorityQueue.cpp
3 // \brief priority queue implementation for Os::Queue
4 // ======================================================================
6 #include <algorithm>
7 #include <cstring>
8 #include "Fw/LanguageHelpers.hpp"
9 #include "Fw/Types/Assert.hpp"
12 
13 namespace Os {
14 namespace Generic {
15 
17  FW_ASSERT(this->m_depth > 0);
18  FwSizeType index = this->m_indices[this->m_startIndex % this->m_depth];
19  this->m_startIndex = (this->m_startIndex + 1) % this->m_depth;
20  return index;
21 }
22 
24  FW_ASSERT(this->m_depth > 0);
25  this->m_indices[this->m_stopIndex % this->m_depth] = index;
26  this->m_stopIndex = (this->m_stopIndex + 1) % this->m_depth;
27 }
28 
29 void PriorityQueueHandle ::store_data(FwSizeType index, const U8* data, FwSizeType size) {
30  FW_ASSERT(size <= this->m_maxSize);
31  FW_ASSERT(index < this->m_depth);
32 
33  FwSizeType offset = this->m_maxSize * index;
34  (void)::memcpy(this->m_data + offset, data, static_cast<size_t>(size));
35  this->m_sizes[index] = size;
36 }
37 
38 void PriorityQueueHandle ::load_data(FwSizeType index, U8* destination, FwSizeType size) {
39  FW_ASSERT(size <= this->m_maxSize);
40  FW_ASSERT(index < this->m_depth);
41  FwSizeType offset = this->m_maxSize * index;
42  (void)::memcpy(destination, this->m_data + offset, static_cast<size_t>(size));
43 }
44 
46 
48  const Fw::ConstStringBase& name,
49  FwSizeType depth,
50  FwSizeType messageSize) {
51  const FwEnumStoreType identifier = id;
53  // Ensure we are created exactly once
54  FW_ASSERT(this->m_handle.m_indices == nullptr);
55  FW_ASSERT(this->m_handle.m_sizes == nullptr);
56  FW_ASSERT(this->m_handle.m_data == nullptr);
57 
58  // Get the memory allocator configured for priority queues
61 
62  // Allocate indices list
63  void* allocation = nullptr;
64  FwSizeType size = 0;
65  FwSizeType* indices = nullptr;
66  FwSizeType* sizes = nullptr;
67  U8* data = nullptr;
68  U8* heap_pointer = nullptr;
69 
70  // Prevent integer overflow when computing allocation size (depth * sizeof(FwSizeType))
71  FW_ASSERT(depth < std::numeric_limits<FwSizeType>::max() / sizeof(FwSizeType));
72 
73  // Allocate indices list and construct it when valid
74  size = depth * sizeof(FwSizeType);
75  allocation = allocator.allocate(identifier, size, alignof(FwSizeType));
76  if (allocation == nullptr) {
77  status = QueueInterface::Status::ALLOCATION_FAILED;
78  } else if (size < (depth * sizeof(FwSizeType))) {
79  allocator.deallocate(identifier, allocation);
80  status = QueueInterface::Status::ALLOCATION_FAILED;
81  } else {
82  indices = Fw::arrayPlacementNew<FwSizeType>(Fw::ByteArray(static_cast<U8*>(allocation), size), depth);
83  }
84 
85  // Allocate sizes list and construct it when valid
86  if (status == QueueInterface::Status::OP_OK) {
87  size = depth * sizeof(FwSizeType);
88  allocation = allocator.allocate(identifier, size, alignof(FwSizeType));
89  if (allocation == nullptr) {
90  allocator.deallocate(identifier, indices);
91  status = QueueInterface::Status::ALLOCATION_FAILED;
92  } else if (size < (depth * sizeof(FwSizeType))) {
93  allocator.deallocate(identifier, indices);
94  allocator.deallocate(identifier, allocation);
95  status = QueueInterface::Status::ALLOCATION_FAILED;
96  } else {
97  sizes = Fw::arrayPlacementNew<FwSizeType>(Fw::ByteArray(static_cast<U8*>(allocation), size), depth);
98  }
99  }
100  // Allocate data
101  if (status == QueueInterface::Status::OP_OK) {
102  // Prevent integer overflow when computing allocation size (depth * messageSize)
103  FW_ASSERT((depth == 0) || (messageSize <= std::numeric_limits<FwSizeType>::max() / depth));
104  size = depth * messageSize;
105  allocation = allocator.allocate(identifier, size, alignof(U8));
106  if (allocation == nullptr) {
107  allocator.deallocate(identifier, indices);
108  allocator.deallocate(identifier, sizes);
109  status = QueueInterface::Status::ALLOCATION_FAILED;
110  } else if (size < (depth * messageSize)) {
111  allocator.deallocate(identifier, indices);
112  allocator.deallocate(identifier, sizes);
113  allocator.deallocate(identifier, allocation);
114  status = QueueInterface::Status::ALLOCATION_FAILED;
115  } else {
116  data = static_cast<U8*>(allocation);
117  }
118  }
119  // Allocate data for max heap
120  if (status == QueueInterface::Status::OP_OK) {
121  size = Types::MaxHeap::ELEMENT_SIZE * depth;
122  allocation = allocator.allocate(identifier, size, Types::MaxHeap::ALIGNMENT);
123  if (allocation == nullptr) {
124  allocator.deallocate(identifier, indices);
125  allocator.deallocate(identifier, sizes);
126  allocator.deallocate(identifier, data);
127  status = QueueInterface::Status::ALLOCATION_FAILED;
128  } else if (size < (Types::MaxHeap::ELEMENT_SIZE * depth)) {
129  allocator.deallocate(identifier, indices);
130  allocator.deallocate(identifier, sizes);
131  allocator.deallocate(identifier, data);
132  allocator.deallocate(identifier, allocation);
133  status = QueueInterface::Status::ALLOCATION_FAILED;
134  } else {
135  heap_pointer = static_cast<U8*>(allocation);
136  this->m_handle.m_heap.create(depth, Fw::ByteArray(static_cast<U8*>(allocation), size));
137  }
138  }
139  // Set up structures when all allocations succeeded
140  if (status == QueueInterface::Status::OP_OK) {
141  // Assign initial indices and sizes
142  for (FwSizeType i = 0; i < depth; i++) {
143  indices[i] = i;
144  sizes[i] = 0;
145  }
146  // Set local tracking variables
147  this->m_handle.m_id = id;
148  this->m_handle.m_maxSize = messageSize;
149  this->m_handle.m_indices = indices;
150  this->m_handle.m_data = data;
151  this->m_handle.m_sizes = sizes;
152  this->m_handle.m_heap_pointer = heap_pointer;
153  this->m_handle.m_startIndex = 0;
154  this->m_handle.m_stopIndex = 0;
155  this->m_handle.m_depth = depth;
156  this->m_handle.m_highMark = 0;
157  }
158  return status;
159 }
160 
162  this->teardownInternal();
163 }
164 
166  if (this->m_handle.m_data != nullptr) {
167  // All backing arrays are allocated together in create()
168  FW_ASSERT(this->m_handle.m_indices != nullptr);
169  FW_ASSERT(this->m_handle.m_sizes != nullptr);
170  FW_ASSERT(this->m_handle.m_heap_pointer != nullptr);
171  const FwEnumStoreType identifier = this->m_handle.m_id;
174  allocator.deallocate(identifier, this->m_handle.m_data);
175  allocator.deallocate(identifier, this->m_handle.m_indices);
176  allocator.deallocate(identifier, this->m_handle.m_sizes);
177  this->m_handle.m_heap.teardown();
178  allocator.deallocate(identifier, this->m_handle.m_heap_pointer);
179 
180  // Set these pointers to nullptr
181  this->m_handle.m_data = nullptr;
182  this->m_handle.m_indices = nullptr;
183  this->m_handle.m_sizes = nullptr;
184  }
185 }
186 
188  FwSizeType size,
189  FwQueuePriorityType priority,
190  QueueInterface::BlockingType blockType) {
191  // Check for sizing problem before locking
192  if (size > this->m_handle.m_maxSize) {
193  return QueueInterface::Status::SIZE_MISMATCH;
194  }
195  // Artificial block scope for scope lock ensuring an unlock in all cases and ensuring an unlock before notify
196  {
197  Os::ScopeLock lock(this->m_handle.m_data_lock);
198  if (this->m_handle.m_heap.isFull() and blockType == BlockingType::NONBLOCKING) {
199  return QueueInterface::Status::FULL;
200  }
201  // Will loop and block until full is false
202  // @non-terminating@: condition-variable wait loop
203  while (this->m_handle.m_heap.isFull()) {
204  this->m_handle.m_full.wait(this->m_handle.m_data_lock);
205  }
206  FwSizeType index = this->m_handle.find_index();
207 
208  // Space must exist, push must work
209  const bool pushed = this->m_handle.m_heap.push(priority, index);
210  FW_ASSERT(pushed);
211  this->m_handle.store_data(index, buffer, size);
212  this->m_handle.m_sizes[index] = size;
213  this->m_handle.m_highMark = std::max(this->m_handle.m_highMark, this->getMessagesAvailable());
214  }
215  this->m_handle.m_empty.notify();
217 }
218 
220  FwSizeType capacity,
222  FwSizeType& actualSize,
223  FwQueuePriorityType& priority) {
224  {
225  Os::ScopeLock lock(this->m_handle.m_data_lock);
226  if (this->m_handle.m_heap.isEmpty() and blockType == BlockingType::NONBLOCKING) {
227  return QueueInterface::Status::EMPTY;
228  }
229  // Loop and lock while empty
230  // @non-terminating@: condition-variable wait loop
231  while (this->m_handle.m_heap.isEmpty()) {
233  }
234 
235  FwSizeType index;
236  // Message must exist, so pop must pass and size must be valid
237  const bool popped = this->m_handle.m_heap.pop(priority, index);
238  FW_ASSERT(popped);
239  actualSize = this->m_handle.m_sizes[index];
240  FW_ASSERT(actualSize <= capacity);
241  this->m_handle.load_data(index, destination, actualSize);
242  this->m_handle.return_index(index);
243  }
244  this->m_handle.m_full.notify();
246 }
247 
249  return this->m_handle.m_heap.getSize();
250 }
251 
253  // Safe to cast away const in this context because scope lock will restore unlocked state on return
254  Os::ScopeLock lock(const_cast<Mutex&>(this->m_handle.m_data_lock));
255  return this->m_handle.m_highMark;
256 }
257 
259  return &this->m_handle;
260 }
261 
262 } // namespace Generic
263 } // namespace Os
void notify() override
notify a single waiter on this condition variable
Definition: Condition.cpp:23
virtual void * allocate(const FwEnumStoreType identifier, FwSizeType &size, bool &recoverable, FwSizeType alignment=alignof(std::max_align_t))=0
static constexpr FwSizeType ALIGNMENT
Exposes the ALIGNMENT for pre-allocation.
Definition: MaxHeap.hpp:114
Operation succeeded.
Definition: Os.hpp:27
FwSizeType * m_sizes
Size store for each method.
PlatformSizeType FwSizeType
I32 FwEnumStoreType
Status send(const U8 *buffer, FwSizeType size, FwQueuePriorityType priority, BlockingType blockType) override
send a message into the queue
Status
status returned from the queue send function
Definition: Queue.hpp:30
FwSizeType * m_indices
List of indices into data.
U8 * m_heap_pointer
Pointer to the MaxHeap data store.
QueueHandle parent class.
Definition: Queue.hpp:19
bool pop(FwQueuePriorityType &value, FwSizeType &id)
Pop an item from the heap.
Definition: MaxHeap.cpp:113
U8 * m_data
Pointer to data allocation.
FwSizeType m_stopIndex
End index of the circular data structure.
FwSizeType m_startIndex
Start index of the circular data structure.
FwSizeType m_maxSize
Maximum size allowed of a message.
FwSizeType m_depth
Depth of the queue.
static MemAllocatorRegistry & getInstance()
get the singleton registry
void teardownInternal()
teardown the queue
FwSizeType m_highMark
Message count high water mark.
REQUIRED: required for Os::Queue memory allocation when using queues that allocate memory...
QueueHandle * getHandle() override
return the underlying queue handle (implementation specific)
MemAllocator & getAnAllocator(const MemoryAllocation::MemoryAllocatorType type)
FwSizeType getMessageHighWaterMark() const override
get maximum messages stored at any given time
void store_data(FwSizeType index, const U8 *source, FwSizeType size)
store data into a set index in the data store
bool isEmpty()
Is the heap empty?
Definition: MaxHeap.cpp:147
bool push(FwQueuePriorityType value, FwSizeType id)
Push an item onto the heap.
Definition: MaxHeap.cpp:65
A variable-length byte array.
Definition: ByteArray.hpp:23
bool isFull()
Is the heap full?
Definition: MaxHeap.cpp:142
Os::ConditionVariable m_empty
Queue empty condition variable to support blocking.
FwSizeType find_index()
find an available index to store data from the list
FwSizeType getSize() const
Get the current number of elements on the heap.
Definition: MaxHeap.cpp:152
uint8_t U8
8-bit unsigned integer
Definition: BasicTypes.h:54
BlockingType
message type
Definition: Queue.hpp:46
Os::ConditionVariable m_full
Queue full condition variable to support blocking.
void load_data(FwSizeType index, U8 *destination, FwSizeType capacity)
load data from a set index in the data store
PlatformQueuePriorityType FwQueuePriorityType
The type of queue priorities used.
void wait(Os::Mutex &mutex)
wait on a condition variable
Definition: Condition.cpp:19
Memory Allocation base class.
FwEnumStoreType m_id
Identifier for the queue, used for memory allocation.
A read-only abstract superclass for StringBase.
Status create(FwEnumStoreType id, const Fw::ConstStringBase &name, FwSizeType depth, FwSizeType messageSize) override
create queue storage
void teardown() override
teardown the queue
locks a mutex within the current scope
Definition: Mutex.hpp:80
static constexpr FwSizeType ELEMENT_SIZE
Exposes the ELEMENT_SIZE for pre-allocation.
Definition: MaxHeap.hpp:112
Status receive(U8 *destination, FwSizeType capacity, BlockingType blockType, FwSizeType &actualSize, FwQueuePriorityType &priority) override
receive a message from the queue
Defines a base class for a memory allocator for classes.
Os::Mutex m_data_lock
Lock against data manipulation.
void create(FwSizeType capacity, Fw::ByteArray heap_allocation)
MaxHeap creation.
Definition: MaxHeap.cpp:42
virtual void deallocate(const FwEnumStoreType identifier, void *ptr)=0
void return_index(FwSizeType index)
return index to the circular data structure
virtual ~PriorityQueue()
default queue destructor
Types::MaxHeap m_heap
MaxHeap data store for tracking priority.
#define FW_ASSERT(...)
Definition: Assert.hpp:14
FwSizeType getMessagesAvailable() const override
get number of messages available
void teardown()
MaxHeap teardown.
Definition: MaxHeap.cpp:53
PriorityQueueHandle m_handle