configTOTAL_HEAP_SIZE byte exactly with heap_1

Hi,

I use heap_2 memory management in my application. All objects are initialised during init, so I use dynamically allocated RAM. I optimized value of configTOTAL_HEAP_SIZE that in the end it holds exact amount of bytes to setup objects.

Let us use following values as complicity:

configTOTAL_HEAP_SIZE = 12 (assume RTOS objects will use 8 bytes (let us say 2 objects and each is 4 bytes) and portBYTE_ALIGNMENT is 4 bytes)
configADJUSTED_HEAP_SIZE = 8 (calculated in memory management with portBYTE_ALIGNMENT )

After init is done value of xFreeBytesRemaining is 0 -> fine because it counts down.

Now I replaced heap_2 handling with heap_1.

With same example from above I see the following Problem during init when the second object (4 bytes) is created in function pvPortMalloc of heap_1 :

	/* Check there is enough room left for the allocation. */
	if( ( ( xNextFreeByte + xWantedSize ) < configADJUSTED_HEAP_SIZE ) &&
		( ( xNextFreeByte + xWantedSize ) > xNextFreeByte )	)/* Check for overflow. */

First if condition with the example would lead into:

if( ( (4 + 4) < 8)

So it will not create the object. But in my opinion the heap is allocated correctly with 8 bytes for the two objects and the statement must be less than or equal instead just less than.

Am I missing something?

Thanks in advance!

Is the problem you are reporting that the heap_1 claims to be empty when it still has one byte left in it? If so I would not bee too concerned about it - especially as it is not possible to allocate one byte because each allocation also includes some metadata, so requesting 1 byte will actually remove more than 1 byte from the available heap space - could that be the cause of the issue?

The whole heap size in my application is something around 32k bytes (tasks, mutex, queues). When the TCB for the last task is created it needs 88 bytes and I have 88 bytes left, but it fails with mentioned if-statement.

1 Like

I think his problem is that if the allocation will exactly remove the last byte in the heap memory pool, because xNextFreeByte + xWantedSize == configADJUSTED_HEAP_SIZE, then the allocation fails, so you need to over allocate you heap to allow for at least a single unused byte at the end.

Allocate one additional byte (e.g. configTOTAL_HEAP_SIZE +1) is of course a working solution, but in my opinion a waste of 1 byte if the heap is allocated correctly and it just fails because of the if-statement.

1 Like

I agree with you,I think it’s a bug. It should be
if( ( ( xNextFreeByte + xWantedSize ) <= configADJUSTED_HEAP_SIZE ) && ( xWantedSize > 0 ) )
The code implementation wastes space, not very clear.
I ThinkThe best step is:
1.ucHeap Align to portBYTE_ALIGNMENT,head wasted. if ucHeap is already align to portBYTE_ALIGNMENT then head wasted=0.
2.Then (configTOTAL_HEAP_SIZE - head wasted) Align to portBYTE_ALIGNMENT.Tail wasted.
if (configTOTAL_HEAP_SIZE - head wasted) is already align to portBYTE_ALIGNMENT,then Tail wasted=0.
3.configADJUSTED_HEAP_SIZE = configTOTAL_HEAP_SIZE - head wasted - Tail wasted.
and pucAlignedHeap = ucHeap - head wasted.
configADJUSTED_HEAP_SIZE = ( configTOTAL_HEAP_SIZE - portBYTE_ALIGNMENT ) is not suitable. because portBYTE_ALIGNMENT is not always equal to head wasted plus Tail wasted.
pucAlignedHeap = ( uint8_t * ) ( ( ( portPOINTER_SIZE_TYPE ) &ucHeap[ portBYTE_ALIGNMENT ] ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) ); also is not suitable.
when ucHeap is already align to portBYTE_ALIGNMENT,This wastes space with a size of portBYTE_ALIGNMENT.

/*
 * FreeRTOS Kernel V10.3.1
 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * http://www.FreeRTOS.org
 * http://aws.amazon.com/freertos
 *
 * 1 tab == 4 spaces!
 */


/*
 * The simplest possible implementation of pvPortMalloc().  Note that this
 * implementation does NOT allow allocated memory to be freed again.
 *
 * See heap_2.c, heap_3.c and heap_4.c for alternative implementations, and the
 * memory management pages of http://www.FreeRTOS.org for more information.
 */
//#include <stdlib.h>

/* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining
all the API functions to use the MPU wrappers.  That should only be done when
task.h is included from an application file. */
#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE

#include "FreeRTOS.h"
#include "task.h"

#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE

#if( configSUPPORT_DYNAMIC_ALLOCATION == 0 )
	#error This file must not be used if configSUPPORT_DYNAMIC_ALLOCATION is 0
#endif

/* A few bytes might be lost to byte aligning the heap start address. */
//#define configADJUSTED_HEAP_SIZE	( configTOTAL_HEAP_SIZE - portBYTE_ALIGNMENT )
                                     
/* Allocate the memory for the heap. */
#if( configAPPLICATION_ALLOCATED_HEAP == 1 )
	/* The application writer has already defined the array used for the RTOS
	heap - probably so it can be placed in a special segment or address. */
	extern uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
#else
	static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ] __attribute__((at(0x20000AA1)));
#endif /* configAPPLICATION_ALLOCATED_HEAP */

//#define HEAP_HEAD_WASTE_SIZE ( ( ( portPOINTER_SIZE_TYPE ) ucHeap & portBYTE_ALIGNMENT_MASK ) == 0 ? 0 :\
                     ( portBYTE_ALIGNMENT - ( ( portPOINTER_SIZE_TYPE ) ucHeap & portBYTE_ALIGNMENT_MASK ) ) )   
                     
#define HEAP_HEAD_WASTE_SIZE ( ( portBYTE_ALIGNMENT - ( ( portPOINTER_SIZE_TYPE ) ucHeap & portBYTE_ALIGNMENT_MASK ) ) & portBYTE_ALIGNMENT_MASK )
#define HEAP_TAIL_WASTE_SIZE ( ( configTOTAL_HEAP_SIZE - HEAP_HEAD_WASTE_SIZE ) & portBYTE_ALIGNMENT_MASK )
#define configADJUSTED_HEAP_SIZE ( configTOTAL_HEAP_SIZE - HEAP_HEAD_WASTE_SIZE - HEAP_TAIL_WASTE_SIZE )

#define pucAlignedHeap ( uint8_t * ) ( ( portPOINTER_SIZE_TYPE ) ucHeap + HEAP_HEAD_WASTE_SIZE )

/* Index into the ucHeap array. */
static size_t xNextFreeByte = ( size_t ) 0;
//static uint8_t *pucAlignedHeap = NULL;
/*-----------------------------------------------------------*/

void *pvPortMalloc( size_t xWantedSize )
{
void *pvReturn = NULL;

	/* Ensure that blocks are always aligned to the required number of bytes. */
	#if( portBYTE_ALIGNMENT != 1 )
	{
		if( xWantedSize & portBYTE_ALIGNMENT_MASK )
		{
			/* Byte alignment required. */
			xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) );
		}
	}
	#endif

	vTaskSuspendAll();
	{
		//if( pucAlignedHeap == NULL )
		//{
			/* Ensure the heap starts on a correctly aligned boundary. */
			//pucAlignedHeap = ( uint8_t * ) ( ( ( portPOINTER_SIZE_TYPE ) &ucHeap[ portBYTE_ALIGNMENT ] ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) );
            //pucAlignedHeap = ( uint8_t * ) ( ( portPOINTER_SIZE_TYPE ) ucHeap + HEAP_HEAD_WASTE_SIZE );
		//}

		/* Check there is enough room left for the allocation. */
		//if( ( ( xNextFreeByte + xWantedSize ) < configADJUSTED_HEAP_SIZE ) &&
			//( ( xNextFreeByte + xWantedSize ) > xNextFreeByte )	)/* Check for overflow. */
		if( ( ( xNextFreeByte + xWantedSize ) <= configADJUSTED_HEAP_SIZE ) && ( xWantedSize  > 0 ))/* Check for overflow. */
		{
			/* Return the next free byte then increment the index past this
			block. */
			pvReturn = pucAlignedHeap + xNextFreeByte;
			xNextFreeByte += xWantedSize;
		}

		traceMALLOC( pvReturn, xWantedSize );
	}
	( void ) xTaskResumeAll();

	#if( configUSE_MALLOC_FAILED_HOOK == 1 )
	{
		if( pvReturn == NULL )
		{
			extern void vApplicationMallocFailedHook( void );
			vApplicationMallocFailedHook();
		}
	}
	#endif

	return pvReturn;
}
/*-----------------------------------------------------------*/

void vPortFree( void *pv )
{
	/* Memory cannot be freed using this scheme.  See heap_2.c, heap_3.c and
	heap_4.c for alternative implementations, and the memory management pages of
	http://www.FreeRTOS.org for more information. */
	( void ) pv;

	/* Force an assert as it is invalid to call this function. */
	configASSERT( pv == NULL );
}
/*-----------------------------------------------------------*/

void vPortInitialiseBlocks( void )
{
	/* Only required when static memory is not cleared. */
	xNextFreeByte = ( size_t ) 0;
    //pucAlignedHeap = NULL;
}
/*-----------------------------------------------------------*/

size_t xPortGetFreeHeapSize( void )
{
	return ( configADJUSTED_HEAP_SIZE - xNextFreeByte );
}