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