]> begriffs open source - freertos/blob - portable/MemMang/heap_5.c
Add SPDX-License-Identifier: MIT to MIT licensed files.
[freertos] / portable / MemMang / heap_5.c
1 /*\r
2  * FreeRTOS Kernel <DEVELOPMENT BRANCH>\r
3  * Copyright (C) 2021 Amazon.com, Inc. or its affiliates.  All Rights Reserved.\r
4  *\r
5  * SPDX-License-Identifier: MIT
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy of\r
8  * this software and associated documentation files (the "Software"), to deal in\r
9  * the Software without restriction, including without limitation the rights to\r
10  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\r
11  * the Software, and to permit persons to whom the Software is furnished to do so,\r
12  * subject to the following conditions:\r
13  *\r
14  * The above copyright notice and this permission notice shall be included in all\r
15  * copies or substantial portions of the Software.\r
16  *\r
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\r
19  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\r
20  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
21  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
23  *\r
24  * https://www.FreeRTOS.org\r
25  * https://github.com/FreeRTOS\r
26  *\r
27  */\r
28 \r
29 /*\r
30  * A sample implementation of pvPortMalloc() that allows the heap to be defined\r
31  * across multiple non-contigous blocks and combines (coalescences) adjacent\r
32  * memory blocks as they are freed.\r
33  *\r
34  * See heap_1.c, heap_2.c, heap_3.c and heap_4.c for alternative\r
35  * implementations, and the memory management pages of https://www.FreeRTOS.org\r
36  * for more information.\r
37  *\r
38  * Usage notes:\r
39  *\r
40  * vPortDefineHeapRegions() ***must*** be called before pvPortMalloc().\r
41  * pvPortMalloc() will be called if any task objects (tasks, queues, event\r
42  * groups, etc.) are created, therefore vPortDefineHeapRegions() ***must*** be\r
43  * called before any other objects are defined.\r
44  *\r
45  * vPortDefineHeapRegions() takes a single parameter.  The parameter is an array\r
46  * of HeapRegion_t structures.  HeapRegion_t is defined in portable.h as\r
47  *\r
48  * typedef struct HeapRegion\r
49  * {\r
50  *  uint8_t *pucStartAddress; << Start address of a block of memory that will be part of the heap.\r
51  *  size_t xSizeInBytes;      << Size of the block of memory.\r
52  * } HeapRegion_t;\r
53  *\r
54  * The array is terminated using a NULL zero sized region definition, and the\r
55  * memory regions defined in the array ***must*** appear in address order from\r
56  * low address to high address.  So the following is a valid example of how\r
57  * to use the function.\r
58  *\r
59  * HeapRegion_t xHeapRegions[] =\r
60  * {\r
61  *  { ( uint8_t * ) 0x80000000UL, 0x10000 }, << Defines a block of 0x10000 bytes starting at address 0x80000000\r
62  *  { ( uint8_t * ) 0x90000000UL, 0xa0000 }, << Defines a block of 0xa0000 bytes starting at address of 0x90000000\r
63  *  { NULL, 0 }                << Terminates the array.\r
64  * };\r
65  *\r
66  * vPortDefineHeapRegions( xHeapRegions ); << Pass the array into vPortDefineHeapRegions().\r
67  *\r
68  * Note 0x80000000 is the lower address so appears in the array first.\r
69  *\r
70  */\r
71 #include <stdlib.h>\r
72 \r
73 /* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining\r
74  * all the API functions to use the MPU wrappers.  That should only be done when\r
75  * task.h is included from an application file. */\r
76 #define MPU_WRAPPERS_INCLUDED_FROM_API_FILE\r
77 \r
78 #include "FreeRTOS.h"\r
79 #include "task.h"\r
80 \r
81 #undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE\r
82 \r
83 #if ( configSUPPORT_DYNAMIC_ALLOCATION == 0 )\r
84     #error This file must not be used if configSUPPORT_DYNAMIC_ALLOCATION is 0\r
85 #endif\r
86 \r
87 /* Block sizes must not get too small. */\r
88 #define heapMINIMUM_BLOCK_SIZE    ( ( size_t ) ( xHeapStructSize << 1 ) )\r
89 \r
90 /* Assumes 8bit bytes! */\r
91 #define heapBITS_PER_BYTE         ( ( size_t ) 8 )\r
92 \r
93 /* Define the linked list structure.  This is used to link free blocks in order\r
94  * of their memory address. */\r
95 typedef struct A_BLOCK_LINK\r
96 {\r
97     struct A_BLOCK_LINK * pxNextFreeBlock; /*<< The next free block in the list. */\r
98     size_t xBlockSize;                     /*<< The size of the free block. */\r
99 } BlockLink_t;\r
100 \r
101 /*-----------------------------------------------------------*/\r
102 \r
103 /*\r
104  * Inserts a block of memory that is being freed into the correct position in\r
105  * the list of free memory blocks.  The block being freed will be merged with\r
106  * the block in front it and/or the block behind it if the memory blocks are\r
107  * adjacent to each other.\r
108  */\r
109 static void prvInsertBlockIntoFreeList( BlockLink_t * pxBlockToInsert );\r
110 \r
111 /*-----------------------------------------------------------*/\r
112 \r
113 /* The size of the structure placed at the beginning of each allocated memory\r
114  * block must by correctly byte aligned. */\r
115 static const size_t xHeapStructSize = ( sizeof( BlockLink_t ) + ( ( size_t ) ( portBYTE_ALIGNMENT - 1 ) ) ) & ~( ( size_t ) portBYTE_ALIGNMENT_MASK );\r
116 \r
117 /* Create a couple of list links to mark the start and end of the list. */\r
118 static BlockLink_t xStart, * pxEnd = NULL;\r
119 \r
120 /* Keeps track of the number of calls to allocate and free memory as well as the\r
121  * number of free bytes remaining, but says nothing about fragmentation. */\r
122 static size_t xFreeBytesRemaining = 0U;\r
123 static size_t xMinimumEverFreeBytesRemaining = 0U;\r
124 static size_t xNumberOfSuccessfulAllocations = 0;\r
125 static size_t xNumberOfSuccessfulFrees = 0;\r
126 \r
127 /* Gets set to the top bit of an size_t type.  When this bit in the xBlockSize\r
128  * member of an BlockLink_t structure is set then the block belongs to the\r
129  * application.  When the bit is free the block is still part of the free heap\r
130  * space. */\r
131 static size_t xBlockAllocatedBit = 0;\r
132 \r
133 /*-----------------------------------------------------------*/\r
134 \r
135 void * pvPortMalloc( size_t xWantedSize )\r
136 {\r
137     BlockLink_t * pxBlock, * pxPreviousBlock, * pxNewBlockLink;\r
138     void * pvReturn = NULL;\r
139 \r
140     /* The heap must be initialised before the first call to\r
141      * prvPortMalloc(). */\r
142     configASSERT( pxEnd );\r
143 \r
144     vTaskSuspendAll();\r
145     {\r
146         /* Check the requested block size is not so large that the top bit is\r
147          * set.  The top bit of the block size member of the BlockLink_t structure\r
148          * is used to determine who owns the block - the application or the\r
149          * kernel, so it must be free. */\r
150         if( ( xWantedSize & xBlockAllocatedBit ) == 0 )\r
151         {\r
152             /* The wanted size is increased so it can contain a BlockLink_t\r
153              * structure in addition to the requested amount of bytes. */\r
154             if( ( xWantedSize > 0 ) &&\r
155                 ( ( xWantedSize + xHeapStructSize ) >  xWantedSize ) ) /* Overflow check */\r
156             {\r
157                 xWantedSize += xHeapStructSize;\r
158 \r
159                 /* Ensure that blocks are always aligned */\r
160                 if( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) != 0x00 )\r
161                 {\r
162                     /* Byte alignment required. Check for overflow */\r
163                     if( ( xWantedSize + ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) ) ) >\r
164                          xWantedSize )\r
165                     {\r
166                         xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) );\r
167                     }\r
168                     else\r
169                     {\r
170                         xWantedSize = 0;\r
171                     }\r
172                 }\r
173                 else\r
174                 {\r
175                     mtCOVERAGE_TEST_MARKER();\r
176                 }\r
177             }\r
178             else\r
179             {\r
180                 xWantedSize = 0;\r
181             }\r
182 \r
183             if( ( xWantedSize > 0 ) && ( xWantedSize <= xFreeBytesRemaining ) )\r
184             {\r
185                 /* Traverse the list from the start (lowest address) block until\r
186                  * one of adequate size is found. */\r
187                 pxPreviousBlock = &xStart;\r
188                 pxBlock = xStart.pxNextFreeBlock;\r
189 \r
190                 while( ( pxBlock->xBlockSize < xWantedSize ) && ( pxBlock->pxNextFreeBlock != NULL ) )\r
191                 {\r
192                     pxPreviousBlock = pxBlock;\r
193                     pxBlock = pxBlock->pxNextFreeBlock;\r
194                 }\r
195 \r
196                 /* If the end marker was reached then a block of adequate size\r
197                  * was not found. */\r
198                 if( pxBlock != pxEnd )\r
199                 {\r
200                     /* Return the memory space pointed to - jumping over the\r
201                      * BlockLink_t structure at its start. */\r
202                     pvReturn = ( void * ) ( ( ( uint8_t * ) pxPreviousBlock->pxNextFreeBlock ) + xHeapStructSize );\r
203 \r
204                     /* This block is being returned for use so must be taken out\r
205                      * of the list of free blocks. */\r
206                     pxPreviousBlock->pxNextFreeBlock = pxBlock->pxNextFreeBlock;\r
207 \r
208                     /* If the block is larger than required it can be split into\r
209                      * two. */\r
210                     if( ( pxBlock->xBlockSize - xWantedSize ) > heapMINIMUM_BLOCK_SIZE )\r
211                     {\r
212                         /* This block is to be split into two.  Create a new\r
213                          * block following the number of bytes requested. The void\r
214                          * cast is used to prevent byte alignment warnings from the\r
215                          * compiler. */\r
216                         pxNewBlockLink = ( void * ) ( ( ( uint8_t * ) pxBlock ) + xWantedSize );\r
217 \r
218                         /* Calculate the sizes of two blocks split from the\r
219                          * single block. */\r
220                         pxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xWantedSize;\r
221                         pxBlock->xBlockSize = xWantedSize;\r
222 \r
223                         /* Insert the new block into the list of free blocks. */\r
224                         prvInsertBlockIntoFreeList( ( pxNewBlockLink ) );\r
225                     }\r
226                     else\r
227                     {\r
228                         mtCOVERAGE_TEST_MARKER();\r
229                     }\r
230 \r
231                     xFreeBytesRemaining -= pxBlock->xBlockSize;\r
232 \r
233                     if( xFreeBytesRemaining < xMinimumEverFreeBytesRemaining )\r
234                     {\r
235                         xMinimumEverFreeBytesRemaining = xFreeBytesRemaining;\r
236                     }\r
237                     else\r
238                     {\r
239                         mtCOVERAGE_TEST_MARKER();\r
240                     }\r
241 \r
242                     /* The block is being returned - it is allocated and owned\r
243                      * by the application and has no "next" block. */\r
244                     pxBlock->xBlockSize |= xBlockAllocatedBit;\r
245                     pxBlock->pxNextFreeBlock = NULL;\r
246                     xNumberOfSuccessfulAllocations++;\r
247                 }\r
248                 else\r
249                 {\r
250                     mtCOVERAGE_TEST_MARKER();\r
251                 }\r
252             }\r
253             else\r
254             {\r
255                 mtCOVERAGE_TEST_MARKER();\r
256             }\r
257         }\r
258         else\r
259         {\r
260             mtCOVERAGE_TEST_MARKER();\r
261         }\r
262 \r
263         traceMALLOC( pvReturn, xWantedSize );\r
264     }\r
265     ( void ) xTaskResumeAll();\r
266 \r
267     #if ( configUSE_MALLOC_FAILED_HOOK == 1 )\r
268         {\r
269             if( pvReturn == NULL )\r
270             {\r
271                 extern void vApplicationMallocFailedHook( void );\r
272                 vApplicationMallocFailedHook();\r
273             }\r
274             else\r
275             {\r
276                 mtCOVERAGE_TEST_MARKER();\r
277             }\r
278         }\r
279     #endif /* if ( configUSE_MALLOC_FAILED_HOOK == 1 ) */\r
280 \r
281     return pvReturn;\r
282 }\r
283 /*-----------------------------------------------------------*/\r
284 \r
285 void vPortFree( void * pv )\r
286 {\r
287     uint8_t * puc = ( uint8_t * ) pv;\r
288     BlockLink_t * pxLink;\r
289 \r
290     if( pv != NULL )\r
291     {\r
292         /* The memory being freed will have an BlockLink_t structure immediately\r
293          * before it. */\r
294         puc -= xHeapStructSize;\r
295 \r
296         /* This casting is to keep the compiler from issuing warnings. */\r
297         pxLink = ( void * ) puc;\r
298 \r
299         /* Check the block is actually allocated. */\r
300         configASSERT( ( pxLink->xBlockSize & xBlockAllocatedBit ) != 0 );\r
301         configASSERT( pxLink->pxNextFreeBlock == NULL );\r
302 \r
303         if( ( pxLink->xBlockSize & xBlockAllocatedBit ) != 0 )\r
304         {\r
305             if( pxLink->pxNextFreeBlock == NULL )\r
306             {\r
307                 /* The block is being returned to the heap - it is no longer\r
308                  * allocated. */\r
309                 pxLink->xBlockSize &= ~xBlockAllocatedBit;\r
310 \r
311                 vTaskSuspendAll();\r
312                 {\r
313                     /* Add this block to the list of free blocks. */\r
314                     xFreeBytesRemaining += pxLink->xBlockSize;\r
315                     traceFREE( pv, pxLink->xBlockSize );\r
316                     prvInsertBlockIntoFreeList( ( ( BlockLink_t * ) pxLink ) );\r
317                     xNumberOfSuccessfulFrees++;\r
318                 }\r
319                 ( void ) xTaskResumeAll();\r
320             }\r
321             else\r
322             {\r
323                 mtCOVERAGE_TEST_MARKER();\r
324             }\r
325         }\r
326         else\r
327         {\r
328             mtCOVERAGE_TEST_MARKER();\r
329         }\r
330     }\r
331 }\r
332 /*-----------------------------------------------------------*/\r
333 \r
334 size_t xPortGetFreeHeapSize( void )\r
335 {\r
336     return xFreeBytesRemaining;\r
337 }\r
338 /*-----------------------------------------------------------*/\r
339 \r
340 size_t xPortGetMinimumEverFreeHeapSize( void )\r
341 {\r
342     return xMinimumEverFreeBytesRemaining;\r
343 }\r
344 /*-----------------------------------------------------------*/\r
345 \r
346 static void prvInsertBlockIntoFreeList( BlockLink_t * pxBlockToInsert )\r
347 {\r
348     BlockLink_t * pxIterator;\r
349     uint8_t * puc;\r
350 \r
351     /* Iterate through the list until a block is found that has a higher address\r
352      * than the block being inserted. */\r
353     for( pxIterator = &xStart; pxIterator->pxNextFreeBlock < pxBlockToInsert; pxIterator = pxIterator->pxNextFreeBlock )\r
354     {\r
355         /* Nothing to do here, just iterate to the right position. */\r
356     }\r
357 \r
358     /* Do the block being inserted, and the block it is being inserted after\r
359      * make a contiguous block of memory? */\r
360     puc = ( uint8_t * ) pxIterator;\r
361 \r
362     if( ( puc + pxIterator->xBlockSize ) == ( uint8_t * ) pxBlockToInsert )\r
363     {\r
364         pxIterator->xBlockSize += pxBlockToInsert->xBlockSize;\r
365         pxBlockToInsert = pxIterator;\r
366     }\r
367     else\r
368     {\r
369         mtCOVERAGE_TEST_MARKER();\r
370     }\r
371 \r
372     /* Do the block being inserted, and the block it is being inserted before\r
373      * make a contiguous block of memory? */\r
374     puc = ( uint8_t * ) pxBlockToInsert;\r
375 \r
376     if( ( puc + pxBlockToInsert->xBlockSize ) == ( uint8_t * ) pxIterator->pxNextFreeBlock )\r
377     {\r
378         if( pxIterator->pxNextFreeBlock != pxEnd )\r
379         {\r
380             /* Form one big block from the two blocks. */\r
381             pxBlockToInsert->xBlockSize += pxIterator->pxNextFreeBlock->xBlockSize;\r
382             pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock->pxNextFreeBlock;\r
383         }\r
384         else\r
385         {\r
386             pxBlockToInsert->pxNextFreeBlock = pxEnd;\r
387         }\r
388     }\r
389     else\r
390     {\r
391         pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock;\r
392     }\r
393 \r
394     /* If the block being inserted plugged a gab, so was merged with the block\r
395      * before and the block after, then it's pxNextFreeBlock pointer will have\r
396      * already been set, and should not be set here as that would make it point\r
397      * to itself. */\r
398     if( pxIterator != pxBlockToInsert )\r
399     {\r
400         pxIterator->pxNextFreeBlock = pxBlockToInsert;\r
401     }\r
402     else\r
403     {\r
404         mtCOVERAGE_TEST_MARKER();\r
405     }\r
406 }\r
407 /*-----------------------------------------------------------*/\r
408 \r
409 void vPortDefineHeapRegions( const HeapRegion_t * const pxHeapRegions )\r
410 {\r
411     BlockLink_t * pxFirstFreeBlockInRegion = NULL, * pxPreviousFreeBlock;\r
412     size_t xAlignedHeap;\r
413     size_t xTotalRegionSize, xTotalHeapSize = 0;\r
414     BaseType_t xDefinedRegions = 0;\r
415     size_t xAddress;\r
416     const HeapRegion_t * pxHeapRegion;\r
417 \r
418     /* Can only call once! */\r
419     configASSERT( pxEnd == NULL );\r
420 \r
421     pxHeapRegion = &( pxHeapRegions[ xDefinedRegions ] );\r
422 \r
423     while( pxHeapRegion->xSizeInBytes > 0 )\r
424     {\r
425         xTotalRegionSize = pxHeapRegion->xSizeInBytes;\r
426 \r
427         /* Ensure the heap region starts on a correctly aligned boundary. */\r
428         xAddress = ( size_t ) pxHeapRegion->pucStartAddress;\r
429 \r
430         if( ( xAddress & portBYTE_ALIGNMENT_MASK ) != 0 )\r
431         {\r
432             xAddress += ( portBYTE_ALIGNMENT - 1 );\r
433             xAddress &= ~portBYTE_ALIGNMENT_MASK;\r
434 \r
435             /* Adjust the size for the bytes lost to alignment. */\r
436             xTotalRegionSize -= xAddress - ( size_t ) pxHeapRegion->pucStartAddress;\r
437         }\r
438 \r
439         xAlignedHeap = xAddress;\r
440 \r
441         /* Set xStart if it has not already been set. */\r
442         if( xDefinedRegions == 0 )\r
443         {\r
444             /* xStart is used to hold a pointer to the first item in the list of\r
445              *  free blocks.  The void cast is used to prevent compiler warnings. */\r
446             xStart.pxNextFreeBlock = ( BlockLink_t * ) xAlignedHeap;\r
447             xStart.xBlockSize = ( size_t ) 0;\r
448         }\r
449         else\r
450         {\r
451             /* Should only get here if one region has already been added to the\r
452              * heap. */\r
453             configASSERT( pxEnd != NULL );\r
454 \r
455             /* Check blocks are passed in with increasing start addresses. */\r
456             configASSERT( xAddress > ( size_t ) pxEnd );\r
457         }\r
458 \r
459         /* Remember the location of the end marker in the previous region, if\r
460          * any. */\r
461         pxPreviousFreeBlock = pxEnd;\r
462 \r
463         /* pxEnd is used to mark the end of the list of free blocks and is\r
464          * inserted at the end of the region space. */\r
465         xAddress = xAlignedHeap + xTotalRegionSize;\r
466         xAddress -= xHeapStructSize;\r
467         xAddress &= ~portBYTE_ALIGNMENT_MASK;\r
468         pxEnd = ( BlockLink_t * ) xAddress;\r
469         pxEnd->xBlockSize = 0;\r
470         pxEnd->pxNextFreeBlock = NULL;\r
471 \r
472         /* To start with there is a single free block in this region that is\r
473          * sized to take up the entire heap region minus the space taken by the\r
474          * free block structure. */\r
475         pxFirstFreeBlockInRegion = ( BlockLink_t * ) xAlignedHeap;\r
476         pxFirstFreeBlockInRegion->xBlockSize = xAddress - ( size_t ) pxFirstFreeBlockInRegion;\r
477         pxFirstFreeBlockInRegion->pxNextFreeBlock = pxEnd;\r
478 \r
479         /* If this is not the first region that makes up the entire heap space\r
480          * then link the previous region to this region. */\r
481         if( pxPreviousFreeBlock != NULL )\r
482         {\r
483             pxPreviousFreeBlock->pxNextFreeBlock = pxFirstFreeBlockInRegion;\r
484         }\r
485 \r
486         xTotalHeapSize += pxFirstFreeBlockInRegion->xBlockSize;\r
487 \r
488         /* Move onto the next HeapRegion_t structure. */\r
489         xDefinedRegions++;\r
490         pxHeapRegion = &( pxHeapRegions[ xDefinedRegions ] );\r
491     }\r
492 \r
493     xMinimumEverFreeBytesRemaining = xTotalHeapSize;\r
494     xFreeBytesRemaining = xTotalHeapSize;\r
495 \r
496     /* Check something was actually defined before it is accessed. */\r
497     configASSERT( xTotalHeapSize );\r
498 \r
499     /* Work out the position of the top bit in a size_t variable. */\r
500     xBlockAllocatedBit = ( ( size_t ) 1 ) << ( ( sizeof( size_t ) * heapBITS_PER_BYTE ) - 1 );\r
501 }\r
502 /*-----------------------------------------------------------*/\r
503 \r
504 void vPortGetHeapStats( HeapStats_t * pxHeapStats )\r
505 {\r
506     BlockLink_t * pxBlock;\r
507     size_t xBlocks = 0, xMaxSize = 0, xMinSize = portMAX_DELAY; /* portMAX_DELAY used as a portable way of getting the maximum value. */\r
508 \r
509     vTaskSuspendAll();\r
510     {\r
511         pxBlock = xStart.pxNextFreeBlock;\r
512 \r
513         /* pxBlock will be NULL if the heap has not been initialised.  The heap\r
514          * is initialised automatically when the first allocation is made. */\r
515         if( pxBlock != NULL )\r
516         {\r
517             do\r
518             {\r
519                 /* Increment the number of blocks and record the largest block seen\r
520                  * so far. */\r
521                 xBlocks++;\r
522 \r
523                 if( pxBlock->xBlockSize > xMaxSize )\r
524                 {\r
525                     xMaxSize = pxBlock->xBlockSize;\r
526                 }\r
527 \r
528                 /* Heap five will have a zero sized block at the end of each\r
529                  * each region - the block is only used to link to the next\r
530                  * heap region so it not a real block. */\r
531                 if( pxBlock->xBlockSize != 0 )\r
532                 {\r
533                     if( pxBlock->xBlockSize < xMinSize )\r
534                     {\r
535                         xMinSize = pxBlock->xBlockSize;\r
536                     }\r
537                 }\r
538 \r
539                 /* Move to the next block in the chain until the last block is\r
540                  * reached. */\r
541                 pxBlock = pxBlock->pxNextFreeBlock;\r
542             } while( pxBlock != pxEnd );\r
543         }\r
544     }\r
545     ( void ) xTaskResumeAll();\r
546 \r
547     pxHeapStats->xSizeOfLargestFreeBlockInBytes = xMaxSize;\r
548     pxHeapStats->xSizeOfSmallestFreeBlockInBytes = xMinSize;\r
549     pxHeapStats->xNumberOfFreeBlocks = xBlocks;\r
550 \r
551     taskENTER_CRITICAL();\r
552     {\r
553         pxHeapStats->xAvailableHeapSpaceInBytes = xFreeBytesRemaining;\r
554         pxHeapStats->xNumberOfSuccessfulAllocations = xNumberOfSuccessfulAllocations;\r
555         pxHeapStats->xNumberOfSuccessfulFrees = xNumberOfSuccessfulFrees;\r
556         pxHeapStats->xMinimumEverFreeBytesRemaining = xMinimumEverFreeBytesRemaining;\r
557     }\r
558     taskEXIT_CRITICAL();\r
559 }\r