Skip to content

Commit e8cdb15

Browse files
skb666AniruddhaKanhere
authored andcommitted
[Feature] Support pvPortRealloc
1 parent 14b30f2 commit e8cdb15

File tree

6 files changed

+497
-0
lines changed

6 files changed

+497
-0
lines changed

examples/coverity/FreeRTOSConfig.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272

7373
#define configSUPPORT_STATIC_ALLOCATION 1
7474
#define configSUPPORT_DYNAMIC_ALLOCATION 1
75+
#define configSUPPORT_HEAP_REALLOC 0
7576
#define configTOTAL_HEAP_SIZE 4096U
7677
#define configAPPLICATION_ALLOCATED_HEAP 1
7778
#define configSTACK_ALLOCATION_FROM_SEPARATE_HEAP 0

examples/template_configuration/FreeRTOSConfig.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,11 @@
283283
* https://www.freertos.org/Static_Vs_Dynamic_Memory_Allocation.html. */
284284
#define configSUPPORT_DYNAMIC_ALLOCATION 1
285285

286+
/* Set configSUPPORT_HEAP_REALLOC to 1 to include FreeRTOS API functions
287+
* that support reallocating memory blocks in the build. Set to 0 to exclude
288+
* realloc support from the build. Defaults to 0 if left undefined. */
289+
#define configSUPPORT_HEAP_REALLOC 0
290+
286291
/* Sets the total size of the FreeRTOS heap, in bytes, when heap_1.c, heap_2.c
287292
* or heap_4.c are included in the build. This value is defaulted to 4096 bytes
288293
* but it must be tailored to each application. Note the heap will appear in

include/FreeRTOS.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2840,6 +2840,14 @@
28402840
#define configSUPPORT_DYNAMIC_ALLOCATION 1
28412841
#endif
28422842

2843+
#ifndef configSUPPORT_HEAP_REALLOC
2844+
#define configSUPPORT_HEAP_REALLOC 0
2845+
#endif
2846+
2847+
#if ( ( configSUPPORT_HEAP_REALLOC > 0 ) && ( configSUPPORT_DYNAMIC_ALLOCATION != 1 ) )
2848+
#error configSUPPORT_HEAP_REALLOC cannot be used without dynamic allocation, but configSUPPORT_HEAP_REALLOC is not set to 1.
2849+
#endif
2850+
28432851
#if ( ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) && ( configSUPPORT_DYNAMIC_ALLOCATION != 1 ) )
28442852
#error configUSE_STATS_FORMATTING_FUNCTIONS cannot be used without dynamic allocation, but configSUPPORT_DYNAMIC_ALLOCATION is not set to 1.
28452853
#endif

include/portable.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,10 @@ void vPortGetHeapStats( HeapStats_t * pxHeapStats );
189189
void * pvPortMalloc( size_t xWantedSize ) PRIVILEGED_FUNCTION;
190190
void * pvPortCalloc( size_t xNum,
191191
size_t xSize ) PRIVILEGED_FUNCTION;
192+
#if ( configSUPPORT_HEAP_REALLOC == 1 )
193+
void *pvPortRealloc( void *pv,
194+
size_t xWantedSize ) PRIVILEGED_FUNCTION;
195+
#endif
192196
void vPortFree( void * pv ) PRIVILEGED_FUNCTION;
193197
void vPortInitialiseBlocks( void ) PRIVILEGED_FUNCTION;
194198
size_t xPortGetFreeHeapSize( void ) PRIVILEGED_FUNCTION;

portable/MemMang/heap_4.c

Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,244 @@ void vPortFree( void * pv )
410410
}
411411
/*-----------------------------------------------------------*/
412412

413+
#if ( configSUPPORT_HEAP_REALLOC == 1 )
414+
/*
415+
* pvPortRealloc - Reallocate memory block size
416+
*
417+
* Description: Resize an allocated memory block, attempting to expand or shrink
418+
* the block in place. If in-place resize is not possible, allocate a new block
419+
* and copy the data.
420+
*
421+
* Parameters:
422+
* pv - Pointer to the previously allocated memory block
423+
* xWantedSize - New requested size of user data (in bytes)
424+
*
425+
* Return Value:
426+
* On success: Pointer to the new memory block (may be the same as original)
427+
* On failure: NULL
428+
*
429+
* Behavior:
430+
* - When pv is NULL, equivalent to pvPortMalloc(xWantedSize)
431+
* - When xWantedSize is 0, equivalent to vPortFree(pv)
432+
* - Resize strategy:
433+
* 1. If new size <= original size, attempt to shrink the block
434+
* 2. If new size > original size, attempt to expand by merging with adjacent free block
435+
* 3. If in-place resize fails, allocate new block and copy data
436+
*/
437+
void *pvPortRealloc( void *pv,
438+
size_t xWantedSize )
439+
{
440+
BlockLink_t *pxBlock;
441+
BlockLink_t *pxPreviousBlock;
442+
BlockLink_t *pxNewBlockLink;
443+
BlockLink_t *pxAdjacentFreeBlock;
444+
void *pvReturn = NULL;
445+
size_t xOriginalSize;
446+
size_t xNewBlockSize;
447+
size_t xAdditionalRequiredSize;
448+
size_t xCopySize;
449+
450+
/* Handle NULL pointer case - equivalent to malloc */
451+
if( pv == NULL )
452+
{
453+
return pvPortMalloc( xWantedSize );
454+
}
455+
456+
/* Handle zero size case - equivalent to free */
457+
if( xWantedSize == 0 )
458+
{
459+
vPortFree( pv );
460+
return NULL;
461+
}
462+
463+
/* Calculate new block size with overhead (header size and alignment) */
464+
xNewBlockSize = xWantedSize;
465+
if( heapADD_WILL_OVERFLOW( xNewBlockSize, xHeapStructSize ) == 0 )
466+
{
467+
xNewBlockSize += xHeapStructSize;
468+
if( ( xNewBlockSize & portBYTE_ALIGNMENT_MASK ) != 0x00 )
469+
{
470+
xAdditionalRequiredSize = portBYTE_ALIGNMENT - ( xNewBlockSize & portBYTE_ALIGNMENT_MASK );
471+
if( heapADD_WILL_OVERFLOW( xNewBlockSize, xAdditionalRequiredSize ) == 0 )
472+
{
473+
xNewBlockSize += xAdditionalRequiredSize;
474+
}
475+
else
476+
{
477+
return NULL;
478+
}
479+
}
480+
else
481+
{
482+
mtCOVERAGE_TEST_MARKER();
483+
}
484+
}
485+
else
486+
{
487+
return NULL;
488+
}
489+
490+
/* Get the block header from the user pointer and validate it */
491+
pxBlock = ( BlockLink_t * )( ( uint8_t * )pv - xHeapStructSize );
492+
heapVALIDATE_BLOCK_POINTER( pxBlock );
493+
if( ( heapBLOCK_IS_ALLOCATED( pxBlock ) == 0 ) || ( pxBlock->pxNextFreeBlock != heapPROTECT_BLOCK_POINTER( NULL ) ) )
494+
{
495+
return NULL;
496+
}
497+
498+
/* Calculate the original block size (without the allocated bit)
499+
* Check if there's enough free memory to expand the block */
500+
xOriginalSize = pxBlock->xBlockSize & ~heapBLOCK_ALLOCATED_BITMASK;
501+
if( ( xOriginalSize < xNewBlockSize ) && ( xFreeBytesRemaining < ( xNewBlockSize - xOriginalSize ) ) )
502+
{
503+
/* Not enough memory to expand the block */
504+
return NULL;
505+
}
506+
507+
/* Calculate the amount of user data to copy (excluding the block header).
508+
* The user data size is the block size minus the header size.
509+
* Limit the copy size to the requested size to avoid copying too much data. */
510+
configASSERT( heapSUBTRACT_WILL_UNDERFLOW( xOriginalSize, xHeapStructSize ) == 0 );
511+
xCopySize = xOriginalSize - xHeapStructSize;
512+
if( xWantedSize < xCopySize )
513+
{
514+
xCopySize = xWantedSize;
515+
}
516+
517+
/* Enter critical section - protect heap structure from concurrent access */
518+
vTaskSuspendAll();
519+
{
520+
/* Case 1: Shrink the block (new size is smaller than or equal to original)
521+
* Check if the remaining space is large enough to create a separate free block */
522+
if( xNewBlockSize <= xOriginalSize )
523+
{
524+
/* Create a new free block from the remaining space */
525+
if( ( xOriginalSize - xNewBlockSize ) > heapMINIMUM_BLOCK_SIZE )
526+
{
527+
pxNewBlockLink = ( BlockLink_t * )( ( ( uint8_t * )pxBlock ) + xNewBlockSize );
528+
configASSERT( ( ( ( size_t )pxNewBlockLink ) & portBYTE_ALIGNMENT_MASK ) == 0 );
529+
pxNewBlockLink->xBlockSize = xOriginalSize - xNewBlockSize;
530+
xFreeBytesRemaining += pxNewBlockLink->xBlockSize;
531+
prvInsertBlockIntoFreeList( pxNewBlockLink );
532+
heapFREE_BLOCK( pxBlock );
533+
pxBlock->xBlockSize = xNewBlockSize;
534+
heapALLOCATE_BLOCK( pxBlock );
535+
}
536+
else
537+
{
538+
mtCOVERAGE_TEST_MARKER();
539+
}
540+
pvReturn = pv;
541+
}
542+
else
543+
{
544+
/* Case 2: Try to expand by merging with next free block */
545+
pxAdjacentFreeBlock = ( BlockLink_t * )( ( ( uint8_t * )pxBlock ) + xOriginalSize );
546+
configASSERT( ( ( ( size_t )pxAdjacentFreeBlock ) & portBYTE_ALIGNMENT_MASK ) == 0 );
547+
548+
/* Traverse the free list to find if the adjacent block is actually free.
549+
* The free list is ordered by address, so we can search efficiently.*/
550+
pxPreviousBlock = &xStart;
551+
while( ( pxPreviousBlock->pxNextFreeBlock != heapPROTECT_BLOCK_POINTER( pxAdjacentFreeBlock ) ) &&
552+
( pxPreviousBlock->pxNextFreeBlock != heapPROTECT_BLOCK_POINTER( NULL ) ) )
553+
{
554+
pxPreviousBlock = heapPROTECT_BLOCK_POINTER( pxPreviousBlock->pxNextFreeBlock );
555+
heapVALIDATE_BLOCK_POINTER( pxPreviousBlock );
556+
}
557+
558+
if( pxPreviousBlock->pxNextFreeBlock == heapPROTECT_BLOCK_POINTER( pxAdjacentFreeBlock ) )
559+
{
560+
configASSERT( heapBLOCK_SIZE_IS_VALID( pxAdjacentFreeBlock->xBlockSize ) );
561+
if( !heapADD_WILL_OVERFLOW( xOriginalSize, pxAdjacentFreeBlock->xBlockSize ) )
562+
{
563+
/* Found a suitable adjacent free block that can provide enough space. */
564+
if( ( xOriginalSize + pxAdjacentFreeBlock->xBlockSize ) >= xNewBlockSize )
565+
{
566+
/* Remove the adjacent free block from the free list and merge it with the allocated block. */
567+
pxPreviousBlock->pxNextFreeBlock = pxAdjacentFreeBlock->pxNextFreeBlock;
568+
xFreeBytesRemaining -= pxAdjacentFreeBlock->xBlockSize;
569+
heapFREE_BLOCK( pxBlock );
570+
pxBlock->xBlockSize = xOriginalSize + pxAdjacentFreeBlock->xBlockSize;
571+
572+
/* If the merged block is larger than needed, split the excess space
573+
* into a new free block. */
574+
if( ( pxBlock->xBlockSize - xNewBlockSize ) > heapMINIMUM_BLOCK_SIZE )
575+
{
576+
pxNewBlockLink = ( BlockLink_t * )( ( ( uint8_t * )pxBlock ) + xNewBlockSize );
577+
configASSERT( ( ( ( size_t )pxNewBlockLink ) & portBYTE_ALIGNMENT_MASK ) == 0 );
578+
pxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xNewBlockSize;
579+
xFreeBytesRemaining += pxNewBlockLink->xBlockSize;
580+
prvInsertBlockIntoFreeList( pxNewBlockLink );
581+
pxBlock->xBlockSize = xNewBlockSize;
582+
}
583+
else
584+
{
585+
mtCOVERAGE_TEST_MARKER();
586+
}
587+
588+
heapALLOCATE_BLOCK( pxBlock );
589+
pvReturn = pv;
590+
591+
/* Update minimum free size statistic if memory was consumed */
592+
if( xFreeBytesRemaining < xMinimumEverFreeBytesRemaining )
593+
{
594+
xMinimumEverFreeBytesRemaining = xFreeBytesRemaining;
595+
}
596+
else
597+
{
598+
mtCOVERAGE_TEST_MARKER();
599+
}
600+
}
601+
else
602+
{
603+
mtCOVERAGE_TEST_MARKER();
604+
}
605+
}
606+
else
607+
{
608+
mtCOVERAGE_TEST_MARKER();
609+
}
610+
}
611+
else
612+
{
613+
mtCOVERAGE_TEST_MARKER();
614+
}
615+
}
616+
}
617+
/* Exit critical section - heap structure modification complete */
618+
( void ) xTaskResumeAll();
619+
620+
/* Case 3: If in-place resize failed, allocate a new block and move the data.
621+
* This is more expensive as it involves:
622+
* 1. Allocating a new block with the requested size
623+
* 2. Copying the user data from the old block to the new block
624+
* 3. Freeing the old block
625+
* Note: Statistics are updated by the called functions (malloc and free). */
626+
if( pvReturn == NULL )
627+
{
628+
pvReturn = pvPortMalloc( xWantedSize );
629+
if( pvReturn != NULL )
630+
{
631+
/* Copy user data from old block to new block (up to the smaller of old or new size) */
632+
( void )memcpy( pvReturn, pv, xCopySize );
633+
vPortFree( pv );
634+
}
635+
else
636+
{
637+
mtCOVERAGE_TEST_MARKER();
638+
}
639+
}
640+
else
641+
{
642+
mtCOVERAGE_TEST_MARKER();
643+
}
644+
645+
configASSERT( ( ( ( size_t )pvReturn ) & ( size_t )portBYTE_ALIGNMENT_MASK ) == 0 );
646+
return pvReturn;
647+
}
648+
#endif /* if ( configSUPPORT_HEAP_REALLOC == 1 ) */
649+
/*-----------------------------------------------------------*/
650+
413651
size_t xPortGetFreeHeapSize( void )
414652
{
415653
return xFreeBytesRemaining;

0 commit comments

Comments
 (0)