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