ChibiOS/RT  6.0.3
chmemheaps.c
Go to the documentation of this file.
1 /*
2  ChibiOS - Copyright (C) 2006..2018 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; either version 3 of the License, or
9  (at your option) any later version.
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 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  */
107 void _heap_init(void) {
108 
109  default_heap.provider = chCoreAllocAlignedWithOffset;
110  H_NEXT(&default_heap.header) = NULL;
111  H_PAGES(&default_heap.header) = 0;
112 #if (CH_CFG_USE_MUTEXES == TRUE) || defined(__DOXYGEN__)
113  chMtxObjectInit(&default_heap.mtx);
114 #else
115  chSemObjectInit(&default_heap.sem, (cnt_t)1);
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  */
131 void 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  */
172 void *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  */
293 void 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  */
357 size_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 /** @} */
void * chCoreAllocAlignedWithOffset(size_t size, unsigned align, size_t offset)
Allocates a memory block.
Definition: chmemcore.c:148
void chHeapFree(void *p)
Frees a previously allocated memory block.
Definition: chmemheaps.c:293
Memory heap block header.
Definition: chmemheaps.h:82
memgetfunc2_t provider
Memory blocks provider for this heap.
Definition: chmemheaps.h:97
heap_header_t header
Free blocks list header.
Definition: chmemheaps.h:99
#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
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
mutex_t mtx
Heap access mutex.
Definition: chmemheaps.h:101
#define chDbgCheck(c)
Function parameters check.
Definition: chdebug.h:101
size_t chHeapStatus(memory_heap_t *heapp, size_t *totalp, size_t *largestp)
Reports the heap status.
Definition: chmemheaps.c:357
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 CH_HEAP_ALIGNMENT
Minimum alignment used for heap.
Definition: chmemheaps.h:42
void chSemObjectInit(semaphore_t *sp, cnt_t n)
Initializes a semaphore with the specified counter value.
Definition: chsem.c:97
#define chDbgAssert(c, r)
Condition assertion.
Definition: chdebug.h:127
union heap_header heap_header_t
Type of a memory heap header.
Definition: chmemheaps.h:77
void chMtxObjectInit(mutex_t *mp)
Initializes s mutex_t structure.
Definition: chmtx.c:103
static memory_heap_t default_heap
Default heap descriptor.
Definition: chmemheaps.c:92
ChibiOS/RT main include file.
void _heap_init(void)
Initializes the default heap.
Definition: chmemheaps.c:107
Structure describing a memory heap.
Definition: chmemheaps.h:96
#define MEM_ALIGN_NEXT(p, a)
Aligns to the next aligned memory address.
Definition: chalign.h:79