QP/C++ 8.1.2
Real-Time Event Framework
Loading...
Searching...
No Matches
qf_mem.cpp
Go to the documentation of this file.
1//============================================================================
2// QP/C++ Real-Time Event Framework (RTEF)
3//
4// Copyright (C) 2005 Quantum Leaps, LLC. All rights reserved.
5//
6// Q u a n t u m L e a P s
7// ------------------------
8// Modern Embedded Software
9//
10// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial
11//
12// This software is dual-licensed under the terms of the open-source GNU
13// General Public License (GPL) or under the terms of one of the closed-
14// source Quantum Leaps commercial licenses.
15//
16// Redistributions in source code must retain this top-level comment block.
17// Plagiarizing this software to sidestep the license obligations is illegal.
18//
19// NOTE:
20// The GPL does NOT permit the incorporation of this code into proprietary
21// programs. Please contact Quantum Leaps for commercial licensing options,
22// which expressly supersede the GPL and are designed explicitly for
23// closed-source distribution.
24//
25// Quantum Leaps contact information:
26// <www.state-machine.com/licensing>
27// <info@state-machine.com>
28//============================================================================
29#define QP_IMPL // this is QP implementation
30#include "qp_port.hpp" // QP port
31#include "qp_pkg.hpp" // QP package-scope interface
32#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem
33#ifdef Q_SPY // QS software tracing enabled?
34 #include "qs_port.hpp" // QS port
35 #include "qs_pkg.hpp" // QS package-scope internal interface
36#else
37 #include "qs_dummy.hpp" // disable the QS software tracing
38#endif // Q_SPY
39
40// unnamed namespace for local definitions with internal linkage
41namespace {
42Q_DEFINE_THIS_MODULE("qf_mem")
43} // unnamed namespace
44
45namespace QP {
46
47//............................................................................
49 : m_start(nullptr), // no storage initially
50 m_end(nullptr), // no storage
51 m_freeHead(nullptr), // no storage
52 m_blockSize(0U), // zero block
53 m_nTot(0U), // no blocks in the pool
54 m_nFree(0U), // no free blocks
55 m_nMin(0U) // minimum free blocks so far
56{}
57//............................................................................
59 void * const poolSto,
60 std::uint_fast32_t const poolSize,
61 std::uint_fast16_t const blockSize) noexcept
62{
65
66 // the pool storage must be provided
67 Q_REQUIRE_INCRIT(100, poolSto != nullptr);
68
69 m_start = static_cast<void * *>(poolSto);
71
72 // find # free links in a memory block, see NOTE1
73 m_blockSize = static_cast<QMPoolSize>(2U * sizeof(void *));
74 std::uint_fast16_t index = 2U; // index of the next block
75 while (m_blockSize < static_cast<QMPoolSize>(blockSize)) {
76 m_blockSize += static_cast<QMPoolSize>(sizeof(void *));
77 ++index;
78 }
79
80 // the pool buffer must fit at least one rounded-up block
81 Q_ASSERT_INCRIT(110, poolSize >= m_blockSize);
82
83 // start at the head of the free list
84 void * *pfb = m_freeHead; // pointer to free block
85 std::uint32_t nTot = 1U; // the last block already in the list
86
87 // chain all blocks together in a free-list...
88 for (std::uint_fast32_t size = poolSize - m_blockSize;
89 size >= static_cast<std::uint_fast32_t>(m_blockSize);
90 size -= static_cast<std::uint_fast32_t>(m_blockSize))
91 {
92 pfb[0] = &pfb[index]; // set the next link to next free block
93#ifndef Q_UNSAFE
94 pfb[1] = pfb[0]; // update Duplicate Storage (NOT inverted)
95#endif
96 pfb = static_cast<void * *>(pfb[0]); // advance to the next block
97 ++nTot; // one more free block in the pool
98 }
99 pfb[0] = nullptr; // the last link points to NULL
100
101 // the total number of blocks must fit in the configured dynamic range
102#if (QF_MPOOL_CTR_SIZE == 1U)
103 Q_ASSERT_INCRIT(160, nTot < 0xFFU);
104#elif (QF_MPOOL_CTR_SIZE == 2U)
105 Q_ASSERT_INCRIT(160, nTot < 0xFFFFU);
106#endif
107
108 m_nTot = static_cast<QMPoolCtr>(nTot);
109 m_nFree = m_nTot; // all blocks are free
110 m_end = pfb; // the last block in this pool
111 m_nMin = m_nTot; // the minimum # free blocks
112
113#ifndef Q_UNSAFE
114 pfb[1] = pfb[0]; // update Duplicate Storage (NOT inverted)
115#endif
116
117 QF_CRIT_EXIT();
118}
119
120//............................................................................
122 std::uint_fast16_t const margin,
123 std::uint_fast8_t const qsId) noexcept
124{
125#ifndef Q_SPY
126 Q_UNUSED_PAR(qsId);
127#endif
128
131
132 // get members into temporaries
133 void * *pfb = m_freeHead; // pointer to free block
134 QMPoolCtr nFree = m_nFree; // get member into temporary
135
136 // have more free blocks than the requested margin?
137 if (nFree > static_cast<QMPoolCtr>(margin)) {
138 // the free block pointer must be valid
139 Q_ASSERT_INCRIT(330, pfb != nullptr);
140
141 // fast temporary
142 void * * const pfb_next = static_cast<void * *>(pfb[0]);
143
144 // the free block must have integrity (Duplicate Storage, NOT inverted)
145 Q_INVARIANT_INCRIT(342, pfb_next == pfb[1]);
146
147 --nFree; // one less free block
148 if (nFree == 0U) { // is the pool becoming empty?
149 // pool is becoming empty, so the next free block must be NULL
150 Q_ASSERT_INCRIT(350, pfb_next == nullptr);
151
152 m_nFree = 0U; // no more free blocks
153 m_nMin = 0U; // remember that the pool got empty
154 }
155 else { // the pool is NOT empty
156
157 // the next free-block pointer must be in range
158 Q_ASSERT_INCRIT(360,
159 (m_start <= pfb_next) && (pfb_next <= m_end));
160
161 m_nFree = nFree; // update the original
162 if (m_nMin > nFree) { // is this the new minimum?
163 m_nMin = nFree; // remember the minimum so far
164 }
165 }
166
167 m_freeHead = pfb_next; // set the head to the next free block
168
169 // change the allocated block contents so that it is different
170 // than a free block inside the pool.
171 pfb[0] = &m_end[1]; // invalid location beyond the end
172#ifndef Q_UNSAFE
173 pfb[1] = nullptr; // invalidate the duplicate storage
174#endif
175
177 QS_TIME_PRE(); // timestamp
178 QS_OBJ_PRE(this); // this memory pool
179 QS_MPC_PRE(nFree); // # free blocks in the pool
180 QS_MPC_PRE(m_nMin); // min # free blocks ever in the pool
181 QS_END_PRE()
182 }
183 else { // don't have enough free blocks at this point
184 pfb = nullptr;
185
187 QS_TIME_PRE(); // timestamp
188 QS_OBJ_PRE(this); // this memory pool
189 QS_MPC_PRE(nFree); // # free blocks in the pool
190 QS_MPC_PRE(margin); // the requested margin
191 QS_END_PRE()
192 }
193
194 QF_CRIT_EXIT();
195
196 return static_cast<void *>(pfb); // return the block or nullptr
197}
198
199//............................................................................
201 void * const block,
202 std::uint_fast8_t const qsId) noexcept
203{
204#ifndef Q_SPY
205 Q_UNUSED_PAR(qsId);
206#endif
207
208 void * * const pfb = static_cast<void * *>(block); // ptr to free block
209
212
213 // the block returned to the pool must be valid
214 Q_REQUIRE_INCRIT(400, pfb != nullptr);
215
216 // the block must be in range of this pool (block from a different pool?)
217 Q_REQUIRE_INCRIT(410, (m_start <= pfb) && (pfb <= m_end));
218
219 // the block must NOT be in the pool already (double free?)
220 // NOTE: a block in the pool already matches the duplicate storage,
221 // so the block not in the pool must NOT match the Duplicate Storage
222 Q_INVARIANT_INCRIT(422, pfb[0] != pfb[1]);
223
224 // get members into temporaries
225 void * * const freeHead = m_freeHead;
226 QMPoolCtr nFree = m_nFree;
227
228 // the number of free blocks must be below the total because
229 // one more block is just being returned to the pool
230 Q_REQUIRE_INCRIT(450, nFree < m_nTot);
231
232 ++nFree; // one more free block in this pool
233
234 m_freeHead = pfb; // set as new head of the free list
235 m_nFree = nFree;
236 pfb[0] = freeHead; // link into the list
237#ifndef Q_UNSAFE
238 pfb[1] = freeHead; // update Duplicate Storage (NOT inverted)
239#endif
240
242 QS_TIME_PRE(); // timestamp
243 QS_OBJ_PRE(this); // this memory pool
244 QS_MPC_PRE(nFree); // the # free blocks in the pool
245 QS_END_PRE()
246
247 QF_CRIT_EXIT();
248}
249
250//............................................................................
251std::uint16_t QMPool::getUse() const noexcept {
252 // NOTE: this function does NOT apply critical section, so it can
253 // be safely called from an already established critical section.
254 return static_cast<std::uint16_t>(m_nTot - m_nFree);
255}
256//............................................................................
257std::uint16_t QMPool::getFree() const noexcept {
258 // NOTE: this function does NOT apply critical section, so it can
259 // be safely called from an already established critical section.
260 return static_cast<std::uint16_t>(m_nFree);
261}
262//............................................................................
263std::uint16_t QMPool::getMin() const noexcept {
264 // NOTE: this function does NOT apply critical section, so it can
265 // be safely called from an already established critical section.
266 return static_cast<std::uint16_t>(m_nMin);
267}
268//............................................................................
270 // NOTE: this function does NOT apply critical section, so it can
271 // be safely called from an already established critical section.
272 return m_blockSize;
273}
274
275} // namespace QP
276
277//============================================================================
278// NOTE1:
279// The memory buffer for the pool is organized as an array of void* pointers
280// (see void * data type). These pointers are used to form a linked-list
281// of free blocks in the pool. The first location pfb[0] is the actual link.
282// The second location pfb[1] is used in SafeQP as the redundant Duplicate
283// Storage (NOT inverted) for the link at pfb[0]. Therefore, the minimum
284// number of void* pointers (void * data type) inside a memory block is 2.
void * get(std::uint_fast16_t const margin, std::uint_fast8_t const qsId) noexcept
Obtain a memory block from a memory pool.
Definition qf_mem.cpp:121
void init(void *const poolSto, std::uint_fast32_t const poolSize, std::uint_fast16_t const blockSize) noexcept
Initializes the native QF memory pool.
Definition qf_mem.cpp:58
QMPoolSize m_blockSize
Memory block size [bytes] held by this fixed-size pool.
Definition qmpool.hpp:72
QMPoolSize getBlockSize() const noexcept
Definition qf_mem.cpp:269
QMPool() noexcept
Default constructor of QP::QMPool.
Definition qf_mem.cpp:48
void ** m_freeHead
Definition qmpool.hpp:71
QMPoolCtr m_nFree
Number of free memory blocks remaining in the pool at this point.
Definition qmpool.hpp:74
void put(void *const block, std::uint_fast8_t const qsId) noexcept
Recycles a memory block back to a memory pool.
Definition qf_mem.cpp:200
std::uint16_t getMin() const noexcept
Definition qf_mem.cpp:263
std::uint16_t getFree() const noexcept
Definition qf_mem.cpp:257
QMPoolCtr m_nMin
Minimum number of free blocks ever present in this pool.
Definition qmpool.hpp:75
void ** m_end
End of the memory managed by this memory pool.
Definition qmpool.hpp:70
std::uint16_t getUse() const noexcept
Definition qf_mem.cpp:251
void ** m_start
Start of the memory managed by this memory pool.
Definition qmpool.hpp:69
QMPoolCtr m_nTot
Total number of memory blocks in this pool.
Definition qmpool.hpp:73
QP/C++ Framework namespace.
Definition qequeue.hpp:36
std::uint16_t QMPoolSize
The data type to store the block-size based on the macro QF_MPOOL_SIZ_SIZE.
Definition qmpool.hpp:49
@ QS_QF_MPOOL_GET
a memory block was removed from memory pool
Definition qs.hpp:88
@ QS_QF_MPOOL_GET_ATTEMPT
attempt to get a memory block failed
Definition qs.hpp:123
@ QS_QF_MPOOL_PUT
a memory block was returned to memory pool
Definition qs.hpp:89
std::uint16_t QMPoolCtr
The data type to store the block-counter based on the macro QF_MPOOL_CTR_SIZE.
Definition qmpool.hpp:59
#define Q_UNUSED_PAR(par_)
Helper macro to mark unused parameters of functions.
Definition qp.hpp:89
QP/C++ Framework in C++ internal (package-scope) interface.
Sample QP/C++ port.
#define QS_TIME_PRE()
Definition qs.hpp:362
QS (QP/Spy software tracing) internal (package-scope) interface.
#define QS_OBJ_PRE(obj_)
Output pre-formatted object pointer element.
Definition qs_pkg.hpp:91
#define QS_MPC_PRE(ctr_)
Output pre-formatted memory pool counter data element.
Definition qs_pkg.hpp:147
#define QS_END_PRE()
Pre-formatted QS trace record end.
Definition qs_pkg.hpp:79
#define QS_BEGIN_PRE(rec_, qsId_)
Pre-formatted QS trace record begin.
Definition qs_pkg.hpp:73
Sample QS/C++ port.
QP Functional Safety (FuSa) Subsystem.
#define QF_CRIT_ENTRY()
Definition qsafe.h:40
#define Q_ASSERT_INCRIT(id_, expr_)
General-purpose assertion with user-specified ID number (in critical section).
Definition qsafe.h:54
#define Q_INVARIANT_INCRIT(id_, expr_)
Assertion for checking a postcondition (in critical section).
Definition qsafe.h:108
#define QF_CRIT_EXIT()
Definition qsafe.h:44
#define Q_REQUIRE_INCRIT(id_, expr_)
Assertion for checking a precondition (in critical section).
Definition qsafe.h:98
#define QF_CRIT_STAT
Definition qsafe.h:36