ChibiOS/RT 7.0.6
chmemheaps.c
Go to the documentation of this file.
1/*
2 ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
3
4 This file is part of ChibiOS.
5
6 ChibiOS is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation version 3 of the License.
9
10 ChibiOS is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19/**
20 * @file oslib/src/chmemheaps.c
21 * @brief Memory heaps code.
22 *
23 * @addtogroup oslib_memheaps
24 * @details Heap Allocator related APIs.
25 * <h2>Operation mode</h2>
26 * The heap allocator implements a first-fit strategy and its APIs
27 * are functionally equivalent to the usual @p malloc() and @p free()
28 * library functions. The main difference is that the OS heap APIs
29 * are guaranteed to be thread safe and there is the ability to
30 * return memory blocks aligned to arbitrary powers of two.<br>
31 * @pre In order to use the heap APIs the @p CH_CFG_USE_HEAP option must
32 * be enabled in @p chconf.h.
33 * @note Compatible with RT and NIL.
34 * @{
35 */
36
37#include "ch.h"
38
39#if (CH_CFG_USE_HEAP == TRUE) || defined(__DOXYGEN__)
40
41/*===========================================================================*/
42/* Module local definitions. */
43/*===========================================================================*/
44
45/*
46 * Defaults on the best synchronization mechanism available.
47 */
48#if (CH_CFG_USE_MUTEXES == TRUE) || defined(__DOXYGEN__)
49#define H_LOCK(h) chMtxLock(&(h)->mtx)
50#define H_UNLOCK(h) chMtxUnlock(&(h)->mtx)
51#else
52#define H_LOCK(h) (void) chSemWait(&(h)->sem)
53#define H_UNLOCK(h) chSemSignal(&(h)->sem)
54#endif
55
56#define H_BLOCK(hp) ((hp) + 1U)
57
58#define H_LIMIT(hp) (H_BLOCK(hp) + H_PAGES(hp))
59
60#define H_NEXT(hp) ((hp)->free.next)
61
62#define H_PAGES(hp) ((hp)->free.pages)
63
64#define H_HEAP(hp) ((hp)->used.heap)
65
66#define H_SIZE(hp) ((hp)->used.size)
67
68/*
69 * Number of pages between two pointers in a MISRA-compatible way.
70 */
71#define NPAGES(p1, p2) \
72 /*lint -save -e9033 [10.8] The cast is safe.*/ \
73 ((size_t)((p1) - (p2))) \
74 /*lint -restore*/
75
76/*===========================================================================*/
77/* Module exported variables. */
78/*===========================================================================*/
79
80/*===========================================================================*/
81/* Module local types. */
82/*===========================================================================*/
83
84/*===========================================================================*/
85/* Module local variables. */
86/*===========================================================================*/
87
88/**
89 * @brief Default heap descriptor.
90 */
92
93/*===========================================================================*/
94/* Module local functions. */
95/*===========================================================================*/
96
97/*===========================================================================*/
98/* Module exported functions. */
99/*===========================================================================*/
100
101/**
102 * @brief Initializes the default heap.
103 *
104 * @notapi
105 */
106void __heap_init(void) {
107
109 H_NEXT(&default_heap.header) = NULL;
110 H_PAGES(&default_heap.header) = 0;
111#if (CH_CFG_USE_MUTEXES == TRUE) || defined(__DOXYGEN__)
113#else
115#endif
116}
117
118/**
119 * @brief Initializes a memory heap from a static memory area.
120 * @note The heap buffer base and size are adjusted if the passed buffer
121 * is not aligned to @p CH_HEAP_ALIGNMENT. This mean that the
122 * effective heap size can be less than @p size.
123 *
124 * @param[out] heapp pointer to the memory heap descriptor to be initialized
125 * @param[in] buf heap buffer base
126 * @param[in] size heap size
127 *
128 * @init
129 */
130void chHeapObjectInit(memory_heap_t *heapp, void *buf, size_t size) {
132
133 chDbgCheck((heapp != NULL) && (size > 0U));
134
135 /* Adjusting the size in case the initial block was not correctly
136 aligned.*/
137 /*lint -save -e9033 [10.8] Required cast operations.*/
138 size -= (size_t)((uint8_t *)hp - (uint8_t *)buf);
139 /*lint -restore*/
140
141 /* Initializing the heap header.*/
142 heapp->provider = NULL;
143 H_NEXT(&heapp->header) = hp;
144 H_PAGES(&heapp->header) = 0;
145 H_NEXT(hp) = NULL;
146 H_PAGES(hp) = (size - sizeof (heap_header_t)) / CH_HEAP_ALIGNMENT;
147#if (CH_CFG_USE_MUTEXES == TRUE) || defined(__DOXYGEN__)
148 chMtxObjectInit(&heapp->mtx);
149#else
150 chSemObjectInit(&heapp->sem, (cnt_t)1);
151#endif
152}
153
154/**
155 * @brief Allocates a block of memory from the heap by using the first-fit
156 * algorithm.
157 * @details The allocated block is guaranteed to be properly aligned to the
158 * specified alignment.
159 *
160 * @param[in] heapp pointer to a heap descriptor or @p NULL in order to
161 * access the default heap.
162 * @param[in] size the size of the block to be allocated. Note that the
163 * allocated block may be a bit bigger than the requested
164 * size for alignment and fragmentation reasons.
165 * @param[in] align desired memory alignment
166 * @return A pointer to the aligned allocated block.
167 * @retval NULL if the block cannot be allocated.
168 *
169 * @api
170 */
171void *chHeapAllocAligned(memory_heap_t *heapp, size_t size, unsigned align) {
172 heap_header_t *qp, *hp, *ahp;
173 size_t pages;
174
175 chDbgCheck((size > 0U) && MEM_IS_VALID_ALIGNMENT(align));
176
177 /* If an heap is not specified then the default system header is used.*/
178 if (heapp == NULL) {
179 heapp = &default_heap;
180 }
181
182 /* Minimum alignment is constrained by the heap header structure size.*/
183 if (align < CH_HEAP_ALIGNMENT) {
184 align = CH_HEAP_ALIGNMENT;
185 }
186
187 /* Size is converted in number of elementary allocation units, checking
188 for overflow in the alignment rounding.*/
189 {
190 size_t asize = MEM_ALIGN_NEXT(size, CH_HEAP_ALIGNMENT);
191 if (asize < size) {
192 return NULL;
193 }
194 pages = asize / CH_HEAP_ALIGNMENT;
195 }
196
197 /* Taking heap mutex/semaphore.*/
198 H_LOCK(heapp);
199
200 /* Start of the free blocks list.*/
201 qp = &heapp->header;
202 while (H_NEXT(qp) != NULL) {
203
204 /* Next free block.*/
205 hp = H_NEXT(qp);
206
207 /* Pointer aligned to the requested alignment.*/
208 ahp = (heap_header_t *)MEM_ALIGN_NEXT(H_BLOCK(hp), align) - 1U;
209
210 if ((ahp < H_LIMIT(hp)) && (pages <= NPAGES(H_LIMIT(hp), ahp + 1U))) {
211 /* The block is large enough to contain a correctly aligned area
212 of sufficient size.*/
213
214 if (ahp > hp) {
215 /* The block is not properly aligned, must split it.*/
216 size_t bpages;
217
218 bpages = NPAGES(H_LIMIT(hp), H_BLOCK(ahp));
219 H_PAGES(hp) = NPAGES(ahp, H_BLOCK(hp));
220 if (bpages > pages) {
221 /* The block is bigger than required, must split the excess.*/
222 heap_header_t *fp;
223
224 /* Creating the excess block.*/
225 fp = H_BLOCK(ahp) + pages;
226 H_PAGES(fp) = (bpages - pages) - 1U;
227
228 /* Linking the excess block.*/
229 H_NEXT(fp) = H_NEXT(hp);
230 H_NEXT(hp) = fp;
231 }
232
233 hp = ahp;
234 }
235 else {
236 /* The block is already properly aligned.*/
237
238 if (H_PAGES(hp) == pages) {
239 /* Exact size, getting the whole block.*/
240 H_NEXT(qp) = H_NEXT(hp);
241 }
242 else {
243 /* The block is bigger than required, must split the excess.*/
244 heap_header_t *fp;
245
246 fp = H_BLOCK(hp) + pages;
247 H_NEXT(fp) = H_NEXT(hp);
248 H_PAGES(fp) = NPAGES(H_LIMIT(hp), H_BLOCK(fp));
249 H_NEXT(qp) = fp;
250 }
251 }
252
253 /* Setting in the block owner heap and size.*/
254 H_SIZE(hp) = size;
255 H_HEAP(hp) = heapp;
256
257 /* Releasing heap mutex/semaphore.*/
258 H_UNLOCK(heapp);
259
260 /*lint -save -e9087 [11.3] Safe cast.*/
261 return (void *)H_BLOCK(hp);
262 /*lint -restore*/
263 }
264
265 /* Next in the free blocks list.*/
266 qp = hp;
267 }
268
269 /* Releasing heap mutex/semaphore.*/
270 H_UNLOCK(heapp);
271
272 /* More memory is required, tries to get it from the associated provider
273 else fails.*/
274 if (heapp->provider != NULL) {
275 ahp = heapp->provider(pages * CH_HEAP_ALIGNMENT,
276 align,
277 sizeof (heap_header_t));
278 if (ahp != NULL) {
279 hp = ahp - 1U;
280 H_HEAP(hp) = heapp;
281 H_SIZE(hp) = size;
282
283 /*lint -save -e9087 [11.3] Safe cast.*/
284 return (void *)ahp;
285 /*lint -restore*/
286 }
287 }
288
289 return NULL;
290}
291
292/**
293 * @brief Frees a previously allocated memory block.
294 *
295 * @param[in] p pointer to the memory block to be freed
296 *
297 * @api
298 */
299void chHeapFree(void *p) {
300 heap_header_t *qp, *hp;
301 memory_heap_t *heapp;
302
303 chDbgCheck((p != NULL) && MEM_IS_ALIGNED(p, CH_HEAP_ALIGNMENT));
304
305 /*lint -save -e9087 [11.3] Safe cast.*/
306 hp = (heap_header_t *)p - 1U;
307 /*lint -restore*/
308 heapp = H_HEAP(hp);
309 qp = &heapp->header;
310
311 /* Size is converted in number of elementary allocation units.*/
312 H_PAGES(hp) = MEM_ALIGN_NEXT(H_SIZE(hp),
314
315 /* Taking heap mutex/semaphore.*/
316 H_LOCK(heapp);
317
318 while (true) {
319 chDbgAssert((hp < qp) || (hp >= H_LIMIT(qp)), "within free block");
320
321 if (((qp == &heapp->header) || (hp > qp)) &&
322 ((H_NEXT(qp) == NULL) || (hp < H_NEXT(qp)))) {
323 /* Insertion after qp.*/
324 H_NEXT(hp) = H_NEXT(qp);
325 H_NEXT(qp) = hp;
326 /* Verifies if the newly inserted block should be merged.*/
327 if (H_LIMIT(hp) == H_NEXT(hp)) {
328 /* Merge with the next block.*/
329 H_PAGES(hp) += H_PAGES(H_NEXT(hp)) + 1U;
330 H_NEXT(hp) = H_NEXT(H_NEXT(hp));
331 }
332 if ((H_LIMIT(qp) == hp)) {
333 /* Merge with the previous block.*/
334 H_PAGES(qp) += H_PAGES(hp) + 1U;
335 H_NEXT(qp) = H_NEXT(hp);
336 }
337 break;
338 }
339 qp = H_NEXT(qp);
340 }
341
342 /* Releasing heap mutex/semaphore.*/
343 H_UNLOCK(heapp);
344}
345
346/**
347 * @brief Reports the heap status.
348 * @note This function is meant to be used in the test suite, it should
349 * not be really useful for the application code.
350 *
351 * @param[in] heapp pointer to a heap descriptor or @p NULL in order to
352 * access the default heap.
353 * @param[in] totalp pointer to a variable that will receive the total
354 * fragmented free space or @p NULL
355 * @param[in] largestp pointer to a variable that will receive the largest
356 * free free block found space or @p NULL
357 * @return The number of fragments in the heap.
358 *
359 * @api
360 */
361size_t chHeapStatus(memory_heap_t *heapp, size_t *totalp, size_t *largestp) {
362 heap_header_t *qp;
363 size_t n, tpages, lpages;
364
365 if (heapp == NULL) {
366 heapp = &default_heap;
367 }
368
369 H_LOCK(heapp);
370 tpages = 0U;
371 lpages = 0U;
372 n = 0U;
373 qp = &heapp->header;
374 while (H_NEXT(qp) != NULL) {
375 size_t pages = H_PAGES(H_NEXT(qp));
376
377 /* Updating counters.*/
378 n++;
379 tpages += pages;
380 if (pages > lpages) {
381 lpages = pages;
382 }
383
384 qp = H_NEXT(qp);
385 }
386
387 /* Writing out fragmented free memory.*/
388 if (totalp != NULL) {
389 *totalp = tpages * CH_HEAP_ALIGNMENT;
390 }
391
392 /* Writing out unfragmented free memory.*/
393 if (largestp != NULL) {
394 *largestp = lpages * CH_HEAP_ALIGNMENT;
395 }
396 H_UNLOCK(heapp);
397
398 return n;
399}
400
401#endif /* CH_CFG_USE_HEAP == TRUE */
402
403/** @} */
ChibiOS/RT main include file.
#define chDbgAssert(c, r)
Condition assertion.
Definition chdebug.h:143
#define chDbgCheck(c)
Function parameters check.
Definition chdebug.h:117
#define MEM_IS_VALID_ALIGNMENT(a)
Returns whatever a constant is a valid alignment.
Definition chalign.h:98
#define MEM_IS_ALIGNED(p, a)
Returns whatever a pointer or memory size is aligned.
Definition chalign.h:90
#define MEM_ALIGN_NEXT(p, a)
Aligns to the next aligned memory address.
Definition chalign.h:79
void chMtxObjectInit(mutex_t *mp)
Initializes s mutex_t structure.
Definition chmtx.c:102
int32_t cnt_t
Definition chearly.h:91
#define chCoreAllocAlignedWithOffset
Allocates a memory block.
Definition chmemcore.h:109
#define H_PAGES(hp)
Definition chmemheaps.c:62
void chHeapFree(void *p)
Frees a previously allocated memory block.
Definition chmemheaps.c:299
void __heap_init(void)
Initializes the default heap.
Definition chmemheaps.c:106
void chHeapObjectInit(memory_heap_t *heapp, void *buf, size_t size)
Initializes a memory heap from a static memory area.
Definition chmemheaps.c:130
#define H_NEXT(hp)
Definition chmemheaps.c:60
#define H_BLOCK(hp)
Definition chmemheaps.c:56
struct memory_heap memory_heap_t
Type of a memory heap.
Definition chmemheaps.h:71
#define H_UNLOCK(h)
Definition chmemheaps.c:50
size_t chHeapStatus(memory_heap_t *heapp, size_t *totalp, size_t *largestp)
Reports the heap status.
Definition chmemheaps.c:361
#define H_LIMIT(hp)
Definition chmemheaps.c:58
static memory_heap_t default_heap
Default heap descriptor.
Definition chmemheaps.c:91
void * chHeapAllocAligned(memory_heap_t *heapp, size_t size, unsigned align)
Allocates a block of memory from the heap by using the first-fit algorithm.
Definition chmemheaps.c:171
union heap_header heap_header_t
Type of a memory heap header.
Definition chmemheaps.h:76
#define CH_HEAP_ALIGNMENT
Minimum alignment used for heap.
Definition chmemheaps.h:41
#define H_HEAP(hp)
Definition chmemheaps.c:64
#define NPAGES(p1, p2)
Definition chmemheaps.c:71
#define H_LOCK(h)
Definition chmemheaps.c:49
#define H_SIZE(hp)
Definition chmemheaps.c:66
void chSemObjectInit(semaphore_t *sp, cnt_t n)
Initializes a semaphore with the specified counter value.
Definition chsem.c:96
memgetfunc2_t provider
Memory blocks provider for this heap.
Definition chmemheaps.h:96
mutex_t mtx
Heap access mutex.
Definition chmemheaps.h:100
heap_header_t header
Free blocks list header.
Definition chmemheaps.h:98