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 <cstring>
7 #include "Fw/LanguageHelpers.hpp"
8 #include "Fw/Types/Assert.hpp"
11 
12 namespace Os {
13 namespace Generic {
14 
16  FwSizeType index = this->m_indices[this->m_startIndex % this->m_depth];
17  this->m_startIndex = (this->m_startIndex + 1) % this->m_depth;
18  return index;
19 }
20 
22  this->m_indices[this->m_stopIndex % this->m_depth] = index;
23  this->m_stopIndex = (this->m_stopIndex + 1) % this->m_depth;
24 }
25 
26 void PriorityQueueHandle ::store_data(FwSizeType index, const U8* data, FwSizeType size) {
27  FW_ASSERT(size <= this->m_maxSize);
28  FW_ASSERT(index < this->m_depth);
29 
30  FwSizeType offset = this->m_maxSize * index;
31  (void)::memcpy(this->m_data + offset, data, static_cast<size_t>(size));
32  this->m_sizes[index] = size;
33 }
34 
35 void PriorityQueueHandle ::load_data(FwSizeType index, U8* destination, FwSizeType size) {
36  FW_ASSERT(size <= this->m_maxSize);
37  FW_ASSERT(index < this->m_depth);
38  FwSizeType offset = this->m_maxSize * index;
39  (void)::memcpy(destination, this->m_data + offset, static_cast<size_t>(size));
40 }
41 
43 
45  const Fw::ConstStringBase& name,
46  FwSizeType depth,
47  FwSizeType messageSize) {
48  const FwEnumStoreType identifier = id;
50  // Ensure we are created exactly once
51  FW_ASSERT(this->m_handle.m_indices == nullptr);
52  FW_ASSERT(this->m_handle.m_sizes == nullptr);
53  FW_ASSERT(this->m_handle.m_data == nullptr);
54 
55  // Get the memory allocator configured for priority queues
58 
59  // Allocate indices list
60  void* allocation = nullptr;
61  FwSizeType size = 0;
62  FwSizeType* indices = nullptr;
63  FwSizeType* sizes = nullptr;
64  U8* data = nullptr;
65  U8* heap_pointer = nullptr;
66 
67  // Allocate indices list and construct it when valid
68  size = depth * sizeof(FwSizeType);
69  allocation = allocator.allocate(identifier, size, alignof(FwSizeType));
70  if (allocation == nullptr) {
71  status = QueueInterface::Status::ALLOCATION_FAILED;
72  } else if (size < (depth * sizeof(FwSizeType))) {
73  allocator.deallocate(identifier, allocation);
74  status = QueueInterface::Status::ALLOCATION_FAILED;
75  } else {
76  indices = Fw::arrayPlacementNew<FwSizeType>(Fw::ByteArray(static_cast<U8*>(allocation), size), depth);
77  }
78 
79  // Allocate sizes list and construct it when valid
80  if (status == QueueInterface::Status::OP_OK) {
81  size = depth * sizeof(FwSizeType);
82  allocation = allocator.allocate(identifier, size, alignof(FwSizeType));
83  if (allocation == nullptr) {
84  allocator.deallocate(identifier, indices);
85  status = QueueInterface::Status::ALLOCATION_FAILED;
86  } else if (size < (depth * sizeof(FwSizeType))) {
87  allocator.deallocate(identifier, indices);
88  allocator.deallocate(identifier, allocation);
89  status = QueueInterface::Status::ALLOCATION_FAILED;
90  } else {
91  sizes = Fw::arrayPlacementNew<FwSizeType>(Fw::ByteArray(static_cast<U8*>(allocation), size), depth);
92  }
93  }
94  // Allocate data
95  if (status == QueueInterface::Status::OP_OK) {
96  size = depth * messageSize;
97  allocation = allocator.allocate(identifier, size, alignof(U8));
98  if (allocation == nullptr) {
99  allocator.deallocate(identifier, indices);
100  allocator.deallocate(identifier, sizes);
101  status = QueueInterface::Status::ALLOCATION_FAILED;
102  } else if (size < (depth * messageSize)) {
103  allocator.deallocate(identifier, indices);
104  allocator.deallocate(identifier, sizes);
105  allocator.deallocate(identifier, allocation);
106  status = QueueInterface::Status::ALLOCATION_FAILED;
107  } else {
108  data = static_cast<U8*>(allocation);
109  }
110  }
111  // Allocate data for max heap
112  if (status == QueueInterface::Status::OP_OK) {
113  size = Types::MaxHeap::ELEMENT_SIZE * depth;
114  allocation = allocator.allocate(identifier, size, Types::MaxHeap::ALIGNMENT);
115  if (allocation == nullptr) {
116  allocator.deallocate(identifier, indices);
117  allocator.deallocate(identifier, sizes);
118  allocator.deallocate(identifier, data);
119  status = QueueInterface::Status::ALLOCATION_FAILED;
120  } else if (size < (Types::MaxHeap::ELEMENT_SIZE * depth)) {
121  allocator.deallocate(identifier, indices);
122  allocator.deallocate(identifier, sizes);
123  allocator.deallocate(identifier, data);
124  allocator.deallocate(identifier, allocation);
125  status = QueueInterface::Status::ALLOCATION_FAILED;
126  } else {
127  heap_pointer = static_cast<U8*>(allocation);
128  this->m_handle.m_heap.create(depth, Fw::ByteArray(static_cast<U8*>(allocation), size));
129  }
130  }
131  // Set up structures when all allocations succeeded
132  if (status == QueueInterface::Status::OP_OK) {
133  // Assign initial indices and sizes
134  for (FwSizeType i = 0; i < depth; i++) {
135  indices[i] = i;
136  sizes[i] = 0;
137  }
138  // Set local tracking variables
139  this->m_handle.m_id = id;
140  this->m_handle.m_maxSize = messageSize;
141  this->m_handle.m_indices = indices;
142  this->m_handle.m_data = data;
143  this->m_handle.m_sizes = sizes;
144  this->m_handle.m_heap_pointer = heap_pointer;
145  this->m_handle.m_startIndex = 0;
146  this->m_handle.m_stopIndex = 0;
147  this->m_handle.m_depth = depth;
148  this->m_handle.m_highMark = 0;
149  }
150  return status;
151 }
152 
154  this->teardownInternal();
155 }
156 
158  if (this->m_handle.m_data != nullptr) {
159  const FwEnumStoreType identifier = this->m_handle.m_id;
162  allocator.deallocate(identifier, this->m_handle.m_data);
163  allocator.deallocate(identifier, this->m_handle.m_indices);
164  allocator.deallocate(identifier, this->m_handle.m_sizes);
165  this->m_handle.m_heap.teardown();
166  allocator.deallocate(identifier, this->m_handle.m_heap_pointer);
167 
168  // Set these pointers to nullptr
169  this->m_handle.m_data = nullptr;
170  this->m_handle.m_indices = nullptr;
171  this->m_handle.m_sizes = nullptr;
172  }
173 }
174 
176  FwSizeType size,
177  FwQueuePriorityType priority,
178  QueueInterface::BlockingType blockType) {
179  // Check for sizing problem before locking
180  if (size > this->m_handle.m_maxSize) {
181  return QueueInterface::Status::SIZE_MISMATCH;
182  }
183  // Artificial block scope for scope lock ensuring an unlock in all cases and ensuring an unlock before notify
184  {
185  Os::ScopeLock lock(this->m_handle.m_data_lock);
186  if (this->m_handle.m_heap.isFull() and blockType == BlockingType::NONBLOCKING) {
187  return QueueInterface::Status::FULL;
188  }
189  // Will loop and block until full is false
190  while (this->m_handle.m_heap.isFull()) {
191  this->m_handle.m_full.wait(this->m_handle.m_data_lock);
192  }
193  FwSizeType index = this->m_handle.find_index();
194 
195  // Space must exist, push must work
196  FW_ASSERT(this->m_handle.m_heap.push(priority, index));
197  this->m_handle.store_data(index, buffer, size);
198  this->m_handle.m_sizes[index] = size;
199  this->m_handle.m_highMark = FW_MAX(this->m_handle.m_highMark, this->getMessagesAvailable());
200  }
201  this->m_handle.m_empty.notify();
203 }
204 
206  FwSizeType capacity,
208  FwSizeType& actualSize,
209  FwQueuePriorityType& priority) {
210  {
211  Os::ScopeLock lock(this->m_handle.m_data_lock);
212  if (this->m_handle.m_heap.isEmpty() and blockType == BlockingType::NONBLOCKING) {
213  return QueueInterface::Status::EMPTY;
214  }
215  // Loop and lock while empty
216  while (this->m_handle.m_heap.isEmpty()) {
218  }
219 
220  FwSizeType index;
221  // Message must exist, so pop must pass and size must be valid
222  FW_ASSERT(this->m_handle.m_heap.pop(priority, index));
223  actualSize = this->m_handle.m_sizes[index];
224  FW_ASSERT(actualSize <= capacity);
225  this->m_handle.load_data(index, destination, actualSize);
226  this->m_handle.return_index(index);
227  }
228  this->m_handle.m_full.notify();
230 }
231 
233  return this->m_handle.m_heap.getSize();
234 }
235 
237  // Safe to cast away const in this context because scope lock will restore unlocked state on return
238  Os::ScopeLock lock(const_cast<Mutex&>(this->m_handle.m_data_lock));
239  return this->m_handle.m_highMark;
240 }
241 
243  return &this->m_handle;
244 }
245 
246 } // namespace Generic
247 } // 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:26
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
#define FW_MAX(a, b)
MAX macro.
Definition: BasicTypes.h:91
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:53
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