| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // ====================================================================== | ||
| 2 | // \title Os/Generic/PriorityQueue.cpp | ||
| 3 | // \brief priority queue implementation for Os::Queue | ||
| 4 | // ====================================================================== | ||
| 5 | #include "Os/Generic/PriorityQueue.hpp" | ||
| 6 | #include <algorithm> | ||
| 7 | #include <cstring> | ||
| 8 | #include "Fw/LanguageHelpers.hpp" | ||
| 9 | #include "Fw/Types/Assert.hpp" | ||
| 10 | #include "Fw/Types/MemAllocator.hpp" | ||
| 11 | #include "config/MemoryAllocatorTypeEnumAc.hpp" | ||
| 12 | |||
| 13 | namespace Os { | ||
| 14 | namespace Generic { | ||
| 15 | |||
| 16 | 11512 | FwSizeType PriorityQueueHandle ::find_index() { | |
| 17 | 11512 | FW_ASSERT(this->m_depth > 0); | |
| 18 |
1/2✗ Branch 4 not taken.
✓ Branch 5 taken 11512 times.
|
11512 | FwSizeType index = this->m_indices[this->m_startIndex % this->m_depth]; |
| 19 |
1/2✗ Branch 4 not taken.
✓ Branch 5 taken 11512 times.
|
11512 | this->m_startIndex = (this->m_startIndex + 1) % this->m_depth; |
| 20 | 11512 | return index; | |
| 21 | } | ||
| 22 | |||
| 23 | 11403 | void PriorityQueueHandle ::return_index(FwSizeType index) { | |
| 24 | 11403 | FW_ASSERT(this->m_depth > 0); | |
| 25 |
1/2✗ Branch 4 not taken.
✓ Branch 5 taken 11403 times.
|
11403 | this->m_indices[this->m_stopIndex % this->m_depth] = index; |
| 26 |
1/2✗ Branch 4 not taken.
✓ Branch 5 taken 11403 times.
|
11403 | this->m_stopIndex = (this->m_stopIndex + 1) % this->m_depth; |
| 27 | 11403 | } | |
| 28 | |||
| 29 | 11512 | void PriorityQueueHandle ::store_data(FwSizeType index, const U8* data, FwSizeType size) { | |
| 30 | 11512 | FW_ASSERT(size <= this->m_maxSize); | |
| 31 | 11512 | FW_ASSERT(index < this->m_depth); | |
| 32 | |||
| 33 | 11512 | FwSizeType offset = this->m_maxSize * index; | |
| 34 |
2/4✗ Branch 3 not taken.
✓ Branch 4 taken 11512 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 11512 times.
|
11512 | (void)::memcpy(this->m_data + offset, data, static_cast<size_t>(size)); |
| 35 | 11512 | this->m_sizes[index] = size; | |
| 36 | 11512 | } | |
| 37 | |||
| 38 | 11403 | void PriorityQueueHandle ::load_data(FwSizeType index, U8* destination, FwSizeType size) { | |
| 39 | 11403 | FW_ASSERT(size <= this->m_maxSize); | |
| 40 | 11403 | FW_ASSERT(index < this->m_depth); | |
| 41 | 11403 | FwSizeType offset = this->m_maxSize * index; | |
| 42 |
2/4✗ Branch 3 not taken.
✓ Branch 4 taken 11403 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 11403 times.
|
11403 | (void)::memcpy(destination, this->m_data + offset, static_cast<size_t>(size)); |
| 43 | 11403 | } | |
| 44 | |||
| 45 | 40 | PriorityQueue::~PriorityQueue() {} | |
| 46 | |||
| 47 | 10 | QueueInterface::Status PriorityQueue::create(FwEnumStoreType id, | |
| 48 | const Fw::ConstStringBase& name, | ||
| 49 | FwSizeType depth, | ||
| 50 | FwSizeType messageSize) { | ||
| 51 | 10 | const FwEnumStoreType identifier = id; | |
| 52 | 10 | QueueInterface::Status status = Os::QueueInterface::Status::OP_OK; | |
| 53 | // Ensure we are created exactly once | ||
| 54 | 10 | FW_ASSERT(this->m_handle.m_indices == nullptr); | |
| 55 | 10 | FW_ASSERT(this->m_handle.m_sizes == nullptr); | |
| 56 | 10 | FW_ASSERT(this->m_handle.m_data == nullptr); | |
| 57 | |||
| 58 | // Get the memory allocator configured for priority queues | ||
| 59 |
3/3✓ Branch 1 taken 10 times.
✓ Branch 5 taken 10 times.
✓ Branch 8 taken 10 times.
|
20 | Fw::MemAllocator& allocator = Fw::MemAllocatorRegistry::getInstance().getAnAllocator( |
| 60 | 10 | Fw::MemoryAllocation::MemoryAllocatorType::OS_GENERIC_PRIORITY_QUEUE); | |
| 61 | |||
| 62 | // Allocate indices list | ||
| 63 | 10 | void* allocation = nullptr; | |
| 64 | 10 | FwSizeType size = 0; | |
| 65 | 10 | FwSizeType* indices = nullptr; | |
| 66 | 10 | FwSizeType* sizes = nullptr; | |
| 67 | 10 | U8* data = nullptr; | |
| 68 | 10 | U8* heap_pointer = nullptr; | |
| 69 | |||
| 70 | // Prevent integer overflow when computing allocation size (depth * sizeof(FwSizeType)) | ||
| 71 | 10 | FW_ASSERT(depth < std::numeric_limits<FwSizeType>::max() / sizeof(FwSizeType)); | |
| 72 | |||
| 73 | // Allocate indices list and construct it when valid | ||
| 74 | 10 | size = depth * sizeof(FwSizeType); | |
| 75 |
1/1✓ Branch 4 taken 10 times.
|
10 | allocation = allocator.allocate(identifier, size, alignof(FwSizeType)); |
| 76 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
|
10 | if (allocation == nullptr) { |
| 77 | ✗ | status = QueueInterface::Status::ALLOCATION_FAILED; | |
| 78 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
|
10 | } else if (size < (depth * sizeof(FwSizeType))) { |
| 79 | ✗ | allocator.deallocate(identifier, allocation); | |
| 80 | ✗ | status = QueueInterface::Status::ALLOCATION_FAILED; | |
| 81 | } else { | ||
| 82 |
1/1✓ Branch 3 taken 10 times.
|
10 | indices = Fw::arrayPlacementNew<FwSizeType>(Fw::ByteArray(static_cast<U8*>(allocation), size), depth); |
| 83 | } | ||
| 84 | |||
| 85 | // Allocate sizes list and construct it when valid | ||
| 86 |
1/2✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
|
10 | if (status == QueueInterface::Status::OP_OK) { |
| 87 | 10 | size = depth * sizeof(FwSizeType); | |
| 88 |
1/1✓ Branch 4 taken 10 times.
|
10 | allocation = allocator.allocate(identifier, size, alignof(FwSizeType)); |
| 89 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
|
10 | if (allocation == nullptr) { |
| 90 | ✗ | allocator.deallocate(identifier, indices); | |
| 91 | ✗ | status = QueueInterface::Status::ALLOCATION_FAILED; | |
| 92 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
|
10 | } 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 |
1/1✓ Branch 3 taken 10 times.
|
10 | sizes = Fw::arrayPlacementNew<FwSizeType>(Fw::ByteArray(static_cast<U8*>(allocation), size), depth); |
| 98 | } | ||
| 99 | } | ||
| 100 | // Allocate data | ||
| 101 |
1/2✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
|
10 | if (status == QueueInterface::Status::OP_OK) { |
| 102 | // Prevent integer overflow when computing allocation size (depth * messageSize) | ||
| 103 | 10 | FW_ASSERT((depth == 0) || (messageSize <= std::numeric_limits<FwSizeType>::max() / depth)); | |
| 104 | 10 | size = depth * messageSize; | |
| 105 |
1/1✓ Branch 4 taken 10 times.
|
10 | allocation = allocator.allocate(identifier, size, alignof(U8)); |
| 106 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
|
10 | if (allocation == nullptr) { |
| 107 | ✗ | allocator.deallocate(identifier, indices); | |
| 108 | ✗ | allocator.deallocate(identifier, sizes); | |
| 109 | ✗ | status = QueueInterface::Status::ALLOCATION_FAILED; | |
| 110 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
|
10 | } 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 | 10 | data = static_cast<U8*>(allocation); | |
| 117 | } | ||
| 118 | } | ||
| 119 | // Allocate data for max heap | ||
| 120 |
1/2✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
|
10 | if (status == QueueInterface::Status::OP_OK) { |
| 121 | 10 | size = Types::MaxHeap::ELEMENT_SIZE * depth; | |
| 122 |
1/1✓ Branch 4 taken 10 times.
|
10 | allocation = allocator.allocate(identifier, size, Types::MaxHeap::ALIGNMENT); |
| 123 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
|
10 | 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 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
|
10 | } 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 | 10 | heap_pointer = static_cast<U8*>(allocation); | |
| 136 |
1/1✓ Branch 6 taken 10 times.
|
10 | 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 |
1/2✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
|
10 | if (status == QueueInterface::Status::OP_OK) { |
| 141 | // Assign initial indices and sizes | ||
| 142 |
2/2✓ Branch 0 taken 466 times.
✓ Branch 1 taken 10 times.
|
476 | for (FwSizeType i = 0; i < depth; i++) { |
| 143 | 466 | indices[i] = i; | |
| 144 | 466 | sizes[i] = 0; | |
| 145 | } | ||
| 146 | // Set local tracking variables | ||
| 147 | 10 | this->m_handle.m_id = id; | |
| 148 | 10 | this->m_handle.m_maxSize = messageSize; | |
| 149 | 10 | this->m_handle.m_indices = indices; | |
| 150 | 10 | this->m_handle.m_data = data; | |
| 151 | 10 | this->m_handle.m_sizes = sizes; | |
| 152 | 10 | this->m_handle.m_heap_pointer = heap_pointer; | |
| 153 | 10 | this->m_handle.m_startIndex = 0; | |
| 154 | 10 | this->m_handle.m_stopIndex = 0; | |
| 155 | 10 | this->m_handle.m_depth = depth; | |
| 156 | 10 | this->m_handle.m_highMark = 0; | |
| 157 | } | ||
| 158 | 10 | return status; | |
| 159 | } | ||
| 160 | |||
| 161 | 19 | void PriorityQueue::teardown() { | |
| 162 | 19 | this->teardownInternal(); | |
| 163 | 19 | } | |
| 164 | |||
| 165 | 19 | void PriorityQueue::teardownInternal() { | |
| 166 |
2/2✓ Branch 4 taken 10 times.
✓ Branch 5 taken 9 times.
|
19 | if (this->m_handle.m_data != nullptr) { |
| 167 | // All backing arrays are allocated together in create() | ||
| 168 | 10 | FW_ASSERT(this->m_handle.m_indices != nullptr); | |
| 169 | 10 | FW_ASSERT(this->m_handle.m_sizes != nullptr); | |
| 170 | 10 | FW_ASSERT(this->m_handle.m_heap_pointer != nullptr); | |
| 171 | 10 | const FwEnumStoreType identifier = this->m_handle.m_id; | |
| 172 |
2/2✓ Branch 3 taken 10 times.
✓ Branch 6 taken 10 times.
|
20 | Fw::MemAllocator& allocator = Fw::MemAllocatorRegistry::getInstance().getAnAllocator( |
| 173 | 10 | Fw::MemoryAllocation::MemoryAllocatorType::OS_GENERIC_PRIORITY_QUEUE); | |
| 174 | 10 | allocator.deallocate(identifier, this->m_handle.m_data); | |
| 175 | 10 | allocator.deallocate(identifier, this->m_handle.m_indices); | |
| 176 | 10 | allocator.deallocate(identifier, this->m_handle.m_sizes); | |
| 177 | 10 | this->m_handle.m_heap.teardown(); | |
| 178 | 10 | allocator.deallocate(identifier, this->m_handle.m_heap_pointer); | |
| 179 | |||
| 180 | // Set these pointers to nullptr | ||
| 181 | 10 | this->m_handle.m_data = nullptr; | |
| 182 | 10 | this->m_handle.m_indices = nullptr; | |
| 183 | 10 | this->m_handle.m_sizes = nullptr; | |
| 184 | } | ||
| 185 | 19 | } | |
| 186 | |||
| 187 | 14225 | QueueInterface::Status PriorityQueue::send(const U8* buffer, | |
| 188 | FwSizeType size, | ||
| 189 | FwQueuePriorityType priority, | ||
| 190 | QueueInterface::BlockingType blockType) { | ||
| 191 | // Check for sizing problem before locking | ||
| 192 |
1/2✗ Branch 4 not taken.
✓ Branch 5 taken 14225 times.
|
14225 | 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 |
1/1✓ Branch 5 taken 14225 times.
|
14225 | Os::ScopeLock lock(this->m_handle.m_data_lock); |
| 198 |
7/7✓ Branch 4 taken 14225 times.
✓ Branch 6 taken 2714 times.
✓ Branch 7 taken 11511 times.
✓ Branch 8 taken 2713 times.
✓ Branch 9 taken 1 times.
✓ Branch 10 taken 2713 times.
✓ Branch 11 taken 11512 times.
|
14225 | if (this->m_handle.m_heap.isFull() and blockType == BlockingType::NONBLOCKING) { |
| 199 | 2713 | return QueueInterface::Status::FULL; | |
| 200 | } | ||
| 201 | // Will loop and block until full is false | ||
| 202 | // @non-terminating@: condition-variable wait loop | ||
| 203 |
3/3✓ Branch 4 taken 11513 times.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 11512 times.
|
11513 | while (this->m_handle.m_heap.isFull()) { |
| 204 |
1/1✓ Branch 9 taken 1 times.
|
1 | this->m_handle.m_full.wait(this->m_handle.m_data_lock); |
| 205 | } | ||
| 206 |
1/1✓ Branch 4 taken 11512 times.
|
11512 | FwSizeType index = this->m_handle.find_index(); |
| 207 | |||
| 208 | // Space must exist, push must work | ||
| 209 |
1/1✓ Branch 4 taken 11512 times.
|
11512 | const bool pushed = this->m_handle.m_heap.push(priority, index); |
| 210 | 11512 | FW_ASSERT(pushed); | |
| 211 |
1/1✓ Branch 4 taken 11512 times.
|
11512 | this->m_handle.store_data(index, buffer, size); |
| 212 | 11512 | this->m_handle.m_sizes[index] = size; | |
| 213 |
1/1✓ Branch 8 taken 11512 times.
|
11512 | this->m_handle.m_highMark = std::max(this->m_handle.m_highMark, this->getMessagesAvailable()); |
| 214 | 14225 | } | |
| 215 | 11512 | this->m_handle.m_empty.notify(); | |
| 216 | 11512 | return QueueInterface::Status::OP_OK; | |
| 217 | } | ||
| 218 | |||
| 219 | 14150 | QueueInterface::Status PriorityQueue::receive(U8* destination, | |
| 220 | FwSizeType capacity, | ||
| 221 | QueueInterface::BlockingType blockType, | ||
| 222 | FwSizeType& actualSize, | ||
| 223 | FwQueuePriorityType& priority) { | ||
| 224 | { | ||
| 225 |
1/1✓ Branch 5 taken 14150 times.
|
14150 | Os::ScopeLock lock(this->m_handle.m_data_lock); |
| 226 |
7/7✓ Branch 4 taken 14150 times.
✓ Branch 6 taken 2748 times.
✓ Branch 7 taken 11402 times.
✓ Branch 8 taken 2747 times.
✓ Branch 9 taken 1 times.
✓ Branch 10 taken 2747 times.
✓ Branch 11 taken 11403 times.
|
14150 | if (this->m_handle.m_heap.isEmpty() and blockType == BlockingType::NONBLOCKING) { |
| 227 | 2747 | return QueueInterface::Status::EMPTY; | |
| 228 | } | ||
| 229 | // Loop and lock while empty | ||
| 230 | // @non-terminating@: condition-variable wait loop | ||
| 231 |
3/3✓ Branch 4 taken 11404 times.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 11403 times.
|
11404 | while (this->m_handle.m_heap.isEmpty()) { |
| 232 |
1/1✓ Branch 9 taken 1 times.
|
1 | this->m_handle.m_empty.wait(this->m_handle.m_data_lock); |
| 233 | } | ||
| 234 | |||
| 235 | 11403 | FwSizeType index; | |
| 236 | // Message must exist, so pop must pass and size must be valid | ||
| 237 |
1/1✓ Branch 4 taken 11403 times.
|
11403 | const bool popped = this->m_handle.m_heap.pop(priority, index); |
| 238 | 11403 | FW_ASSERT(popped); | |
| 239 | 11403 | actualSize = this->m_handle.m_sizes[index]; | |
| 240 | 11403 | FW_ASSERT(actualSize <= capacity); | |
| 241 |
1/1✓ Branch 5 taken 11403 times.
|
11403 | this->m_handle.load_data(index, destination, actualSize); |
| 242 |
1/1✓ Branch 4 taken 11403 times.
|
11403 | this->m_handle.return_index(index); |
| 243 | 14150 | } | |
| 244 | 11403 | this->m_handle.m_full.notify(); | |
| 245 | 11403 | return QueueInterface::Status::OP_OK; | |
| 246 | } | ||
| 247 | |||
| 248 | 98171 | FwSizeType PriorityQueue::getMessagesAvailable() const { | |
| 249 | 98171 | return this->m_handle.m_heap.getSize(); | |
| 250 | } | ||
| 251 | |||
| 252 | 84073 | FwSizeType PriorityQueue::getMessageHighWaterMark() const { | |
| 253 | // Safe to cast away const in this context because scope lock will restore unlocked state on return | ||
| 254 |
1/1✓ Branch 7 taken 84073 times.
|
84073 | Os::ScopeLock lock(const_cast<Mutex&>(this->m_handle.m_data_lock)); |
| 255 | 168146 | return this->m_handle.m_highMark; | |
| 256 | 84073 | } | |
| 257 | |||
| 258 | ✗ | QueueHandle* PriorityQueue::getHandle() { | |
| 259 | ✗ | return &this->m_handle; | |
| 260 | } | ||
| 261 | |||
| 262 | } // namespace Generic | ||
| 263 | } // namespace Os | ||
| 264 |