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  size = depth * messageSize;
103  allocation = allocator.allocate(identifier, size, alignof(U8));
104  if (allocation == nullptr) {
105  allocator.deallocate(identifier, indices);
106  allocator.deallocate(identifier, sizes);
107  status = QueueInterface::Status::ALLOCATION_FAILED;
108  } else if (size < (depth * messageSize)) {
109  allocator.deallocate(identifier, indices);
110  allocator.deallocate(identifier, sizes);
111  allocator.deallocate(identifier, allocation);
112  status = QueueInterface::Status::ALLOCATION_FAILED;
113  } else {
114  data = static_cast<U8*>(allocation);
115  }
116  }
117  // Allocate data for max heap
118  if (status == QueueInterface::Status::OP_OK) {
119  size = Types::MaxHeap::ELEMENT_SIZE * depth;
120  allocation = allocator.allocate(identifier, size, Types::MaxHeap::ALIGNMENT);
121  if (allocation == nullptr) {
122  allocator.deallocate(identifier, indices);
123  allocator.deallocate(identifier, sizes);
124  allocator.deallocate(identifier, data);
125  status = QueueInterface::Status::ALLOCATION_FAILED;
126  } else if (size < (Types::MaxHeap::ELEMENT_SIZE * depth)) {
127  allocator.deallocate(identifier, indices);
128  allocator.deallocate(identifier, sizes);
129  allocator.deallocate(identifier, data);
130  allocator.deallocate(identifier, allocation);
131  status = QueueInterface::Status::ALLOCATION_FAILED;
132  } else {
133  heap_pointer = static_cast<U8*>(allocation);
134  this->m_handle.m_heap.create(depth, Fw::ByteArray(static_cast<U8*>(allocation), size));
135  }
136  }
137  // Set up structures when all allocations succeeded
138  if (status == QueueInterface::Status::OP_OK) {
139  // Assign initial indices and sizes
140  for (FwSizeType i = 0; i < depth; i++) {
141  indices[i] = i;
142  sizes[i] = 0;
143  }
144  // Set local tracking variables
145  this->m_handle.m_id = id;
146  this->m_handle.m_maxSize = messageSize;
147  this->m_handle.m_indices = indices;
148  this->m_handle.m_data = data;
149  this->m_handle.m_sizes = sizes;
150  this->m_handle.m_heap_pointer = heap_pointer;
151  this->m_handle.m_startIndex = 0;
152  this->m_handle.m_stopIndex = 0;
153  this->m_handle.m_depth = depth;
154  this->m_handle.m_highMark = 0;
155  }
156  return status;
157 }
158 
160  this->teardownInternal();
161 }
162 
164  if (this->m_handle.m_data != nullptr) {
165  // All backing arrays are allocated together in create()
166  FW_ASSERT(this->m_handle.m_indices != nullptr);
167  FW_ASSERT(this->m_handle.m_sizes != nullptr);
168  FW_ASSERT(this->m_handle.m_heap_pointer != nullptr);
169  const FwEnumStoreType identifier = this->m_handle.m_id;
172  allocator.deallocate(identifier, this->m_handle.m_data);
173  allocator.deallocate(identifier, this->m_handle.m_indices);
174  allocator.deallocate(identifier, this->m_handle.m_sizes);
175  this->m_handle.m_heap.teardown();
176  allocator.deallocate(identifier, this->m_handle.m_heap_pointer);
177 
178  // Set these pointers to nullptr
179  this->m_handle.m_data = nullptr;
180  this->m_handle.m_indices = nullptr;
181  this->m_handle.m_sizes = nullptr;
182  }
183 }
184 
186  FwSizeType size,
187  FwQueuePriorityType priority,
188  QueueInterface::BlockingType blockType) {
189  // Check for sizing problem before locking
190  if (size > this->m_handle.m_maxSize) {
191  return QueueInterface::Status::SIZE_MISMATCH;
192  }
193  // Artificial block scope for scope lock ensuring an unlock in all cases and ensuring an unlock before notify
194  {
195  Os::ScopeLock lock(this->m_handle.m_data_lock);
196  if (this->m_handle.m_heap.isFull() and blockType == BlockingType::NONBLOCKING) {
197  return QueueInterface::Status::FULL;
198  }
199  // Will loop and block until full is false
200  // @non-terminating@: condition-variable wait loop
201  while (this->m_handle.m_heap.isFull()) {
202  this->m_handle.m_full.wait(this->m_handle.m_data_lock);
203  }
204  FwSizeType index = this->m_handle.find_index();
205 
206  // Space must exist, push must work
207  const bool pushed = this->m_handle.m_heap.push(priority, index);
208  FW_ASSERT(pushed);
209  this->m_handle.store_data(index, buffer, size);
210  this->m_handle.m_sizes[index] = size;
211  this->m_handle.m_highMark = std::max(this->m_handle.m_highMark, this->getMessagesAvailable());
212  }
213  this->m_handle.m_empty.notify();
215 }
216 
218  FwSizeType capacity,
220  FwSizeType& actualSize,
221  FwQueuePriorityType& priority) {
222  {
223  Os::ScopeLock lock(this->m_handle.m_data_lock);
224  if (this->m_handle.m_heap.isEmpty() and blockType == BlockingType::NONBLOCKING) {
225  return QueueInterface::Status::EMPTY;
226  }
227  // Loop and lock while empty
228  // @non-terminating@: condition-variable wait loop
229  while (this->m_handle.m_heap.isEmpty()) {
231  }
232 
233  FwSizeType index;
234  // Message must exist, so pop must pass and size must be valid
235  const bool popped = this->m_handle.m_heap.pop(priority, index);
236  FW_ASSERT(popped);
237  actualSize = this->m_handle.m_sizes[index];
238  FW_ASSERT(actualSize <= capacity);
239  this->m_handle.load_data(index, destination, actualSize);
240  this->m_handle.return_index(index);
241  }
242  this->m_handle.m_full.notify();
244 }
245 
247  return this->m_handle.m_heap.getSize();
248 }
249 
251  // Safe to cast away const in this context because scope lock will restore unlocked state on return
252  Os::ScopeLock lock(const_cast<Mutex&>(this->m_handle.m_data_lock));
253  return this->m_handle.m_highMark;
254 }
255 
257  return &this->m_handle;
258 }
259 
260 } // namespace Generic
261 } // 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