Memory Pools

It seems that when it rains it pours. A couple days before the 4th of July the garage opener stopped working. We called the garage service company that installed the current unit. The technician showed up and determined that the issue was a cracked controller board. We paid in advanced for the board which had to be ordered. In the meantime the unit was detached so we could open and close the door by hand.

Last week the door stopped working. It got stuck. Called the service company and they told us that the board had arrived (was probably delivered by snail mail) and they could get all done last Friday or Saturday. My wife and I were going to be out of town Thursday through Sunday. Last evening when we arrived, the door was still partially open.

This morning we called the service company and they said they were going to be here today. If they do not show up by noon will call a different company. Both our cars are being sequestered by the garage door which we cannot open by hand.

This post addresses the implementation of a simple memory pool. The requirements for the memory pool in the embedded system are as follows:

o Initialize the pool when the system boots.

o The pool will contain a set of pools of the same size.

o During operation, clients will allocate memory from a specified pool as needed.

o Clients will not free memory.

o When the system exits release all the memory allocated to all the pools.

Freeing memory when the system shuts down may not be too useful especially when the system is embedded. When it comes up it will clear memory and the process will repeat. In this case we should just make sure that the same memory allocated for the pools will be released at the end.

If the different pools are used in a sequential fashion by the same process, there is no need to control access to the function that allocates memory. In this case I am assuming that a memory pool can be used by threads on different processes. I am running out of time so will just leave it with a mutex but will only test with a single thread. If requested I will update the code to use two or three threads.

Let’s take a look at the test code:

// **** Memory Pool related ****
#define	MAX_MEMORY_POOLS	8
#define	MAX_POOL_CAPACITY	(1024 * 1)

HANDLE			mpMutex		= (HANDLE)NULL;

unsigned long	usedCapacity[MAX_MEMORY_POOLS];

void			*MemoryPools[MAX_MEMORY_POOLS];


int __stdcall	MemoryPool	(
							)

//	***************************************************************@@
//	- 
//	*****************************************************************

{

int				memPoolID,									// memory pool ID
				retVal,										// returned by this function
				status;										// returned by function calls

unsigned long	size;										// size of memory to allocate

void			*memory;									// memory to be allocated

// **** initialization ****
retVal		= 0;											// hope all goes well

memory		= NULL;											// for starters

// **** initialize the complete memory pool ****
for (memPoolID = -1; memPoolID <= MAX_MEMORY_POOLS; memPoolID++) {
	status = MPInit(memPoolID);
	if (status != 0)
		EventLog(EVENT_ERROR,
		"MemoryPool <<< MPInit status: %d memPoolID: %d line: %d file ==>%s<==\n",
		status, memPoolID, __LINE__, __FILE__);
}

// **** for the looks ****
printf("\n");

// **** allocate memory from the specified memory pool ****
memPoolID = 7;
for (int i = 0; i < 5; i++)
{

	// **** compute size of memory to allocate ****
	size = 0x100;

	// **** allocate memory ****
	status = MPAlloc(	memPoolID,
						size,
						&memory);
	if (status != 0)
		EventLog(EVENT_ERROR,
		"MemoryPool <<< MPAlloc status: %d memPoolID: %d size: %lu line: %d file ==>%s<==\n",
		status, memPoolID, size, __LINE__, __FILE__);

	// **** inform the user what is going on ****
	EventLog(EVENT_INFO,
	"MemoryPool <<< memory: 0x%08lx line: %d file ==>%s<==\n",
	memory, __LINE__, __FILE__);
}

// **** for the looks ****
printf("\n");

// **** free the complete memory pool ****
for (memPoolID = -1; memPoolID <= MAX_MEMORY_POOLS; memPoolID++)
{
	status = MPFree(memPoolID);
	if (status != 0)
		EventLog(EVENT_ERROR,
		"MemoryPool <<< MPFree status: %d memPoolID: %d line: %d file ==>%s<==\n",
		status, memPoolID, __LINE__, __FILE__);
}

// **** clean up ****
done:

// **** inform the user what is going on ****
if ((retVal != 0) || (traceExecution != 0))
	EventLog(EVENT_INFO, "MemoryPool <<< retVal: %d line: %d file ==>%s<==\n", retVal, __LINE__, __FILE__);

// **** inform the caller what went on ****
return retVal;
}

We will use a maximum of 8 pools each with a 1024 byte capacity. The numbers are small in order to simplify testing.

We start by initializing all the pools. Note that we attempt to initialize non-existing pools at the start and end of the loop.

After randomly selecting a pool, ea loop allocating memory until a request fails.

When done we free all the pools. Note that the memory addresses are display to make sure we free what we allocated.

The contents of the console follows:

MPInit <<< UNEXPECTED mpName: -1 line: 59484 file ==>..\..\..\SencorSource\source\testlib.c<==
MPInit <<< retVal: -1050 line: 59528 file ==>..\..\..\SencorSource\source\testlib.c<==
MemoryPool <<< MPInit status: -1050 memPoolID: -1 line: 59712 file ==>..\..\..\SencorSource\source\testlib.c<==
MPInit <<< MemoryPools[0]: 0x00b11b68 line: 59506 file ==>..\..\..\SencorSource\source\testlib.c<==
MPInit <<< MemoryPools[1]: 0x00b11b78 line: 59506 file ==>..\..\..\SencorSource\source\testlib.c<==
MPInit <<< MemoryPools[2]: 0x00b11a68 line: 59506 file ==>..\..\..\SencorSource\source\testlib.c<==
MPInit <<< MemoryPools[3]: 0x00b11aa8 line: 59506 file ==>..\..\..\SencorSource\source\testlib.c<==
MPInit <<< MemoryPools[4]: 0x00b11bf8 line: 59506 file ==>..\..\..\SencorSource\source\testlib.c<==
MPInit <<< MemoryPools[5]: 0x00b11a88 line: 59506 file ==>..\..\..\SencorSource\source\testlib.c<==
MPInit <<< MemoryPools[6]: 0x00b11ab8 line: 59506 file ==>..\..\..\SencorSource\source\testlib.c<==
MPInit <<< MemoryPools[7]: 0x00b11b18 line: 59506 file ==>..\..\..\SencorSource\source\testlib.c<==
MPInit <<< UNEXPECTED mpName: 8 line: 59484 file ==>..\..\..\SencorSource\source\testlib.c<==
MPInit <<< retVal: -1050 line: 59528 file ==>..\..\..\SencorSource\source\testlib.c<==
MemoryPool <<< MPInit status: -1050 memPoolID: 8 line: 59712 file ==>..\..\..\SencorSource\source\testlib.c<==

MemoryPool <<< memory: 0x00b11b18 line: 59738 file ==>..\..\..\SencorSource\source\testlib.c<==
MemoryPool <<< memory: 0x00b11c18 line: 59738 file ==>..\..\..\SencorSource\source\testlib.c<==
MemoryPool <<< memory: 0x00b11d18 line: 59738 file ==>..\..\..\SencorSource\source\testlib.c<==
MemoryPool <<< memory: 0x00b11e18 line: 59738 file ==>..\..\..\SencorSource\source\testlib.c<==
MPAlloc <<< UNEXPECTED size: 256 line: 59649 file ==>..\..\..\SencorSource\source\testlib.c<==
MPAlloc <<< retVal: -1052 line: 59677 file ==>..\..\..\SencorSource\source\testlib.c<==
MemoryPool <<< MPAlloc status: -1052 memPoolID: 7 size: 256 line: 59733 file ==>..\..\..\SencorSource\source\testlib.c<==
MemoryPool <<< memory: 0x00000000 line: 59738 file ==>..\..\..\SencorSource\source\testlib.c<==

MPFree <<< UNEXPECTED mpName: -1 line: 59558 file ==>..\..\..\SencorSource\source\testlib.c<==
MPFree <<< retVal: -1050 line: 59585 file ==>..\..\..\SencorSource\source\testlib.c<==
MemoryPool <<< MPFree status: -1050 memPoolID: -1 line: 59751 file ==>..\..\..\SencorSource\source\testlib.c<==
MPFree <<< MemoryPools[0]: 0x00b11b68 line: 59572 file ==>..\..\..\SencorSource\source\testlib.c<==
MPFree <<< MemoryPools[1]: 0x00b11b78 line: 59572 file ==>..\..\..\SencorSource\source\testlib.c<==
MPFree <<< MemoryPools[2]: 0x00b11a68 line: 59572 file ==>..\..\..\SencorSource\source\testlib.c<==
MPFree <<< MemoryPools[3]: 0x00b11aa8 line: 59572 file ==>..\..\..\SencorSource\source\testlib.c<==
MPFree <<< MemoryPools[4]: 0x00b11bf8 line: 59572 file ==>..\..\..\SencorSource\source\testlib.c<==
MPFree <<< MemoryPools[5]: 0x00b11a88 line: 59572 file ==>..\..\..\SencorSource\source\testlib.c<==
MPFree <<< MemoryPools[6]: 0x00b11ab8 line: 59572 file ==>..\..\..\SencorSource\source\testlib.c<==
MPFree <<< MemoryPools[7]: 0x00b11b18 line: 59572 file ==>..\..\..\SencorSource\source\testlib.c<==
MPFree <<< UNEXPECTED mpName: 8 line: 59558 file ==>..\..\..\SencorSource\source\testlib.c<==
MPFree <<< retVal: -1050 line: 59585 file ==>..\..\..\SencorSource\source\testlib.c<==
MemoryPool <<< MPFree status: -1050 memPoolID: 8 line: 59751 file ==>..\..\..\SencorSource\source\testlib.c<==

The EventLog() function writes to the console and to log files. We could just have displayed the messages to the console.

The contents of the log file follows:

07/22/19 09:52:05 0x00003b64 - MPInit <<< UNEXPECTED mpName: -1 line: 59484 file ==>..\..\..\SencorSource\source\testlib.c<==
07/22/19 09:52:05 0x00003b64 - MPInit <<< retVal: -1050 line: 59528 file ==>..\..\..\SencorSource\source\testlib.c<==
07/22/19 09:52:05 0x00003b64 - MemoryPool <<< MPInit status: -1050 memPoolID: -1 line: 59712 file ==>..\..\..\SencorSource\source\testlib.c<==
07/22/19 09:52:05 0x00003b64 - MPInit <<< MemoryPools[0]: 0x00b11b68 line: 59506 file ==>..\..\..\SencorSource\source\testlib.c<==
07/22/19 09:52:05 0x00003b64 - MPInit <<< MemoryPools[1]: 0x00b11b78 line: 59506 file ==>..\..\..\SencorSource\source\testlib.c<==
07/22/19 09:52:05 0x00003b64 - MPInit <<< MemoryPools[2]: 0x00b11a68 line: 59506 file ==>..\..\..\SencorSource\source\testlib.c<==
07/22/19 09:52:05 0x00003b64 - MPInit <<< MemoryPools[3]: 0x00b11aa8 line: 59506 file ==>..\..\..\SencorSource\source\testlib.c<==
07/22/19 09:52:05 0x00003b64 - MPInit <<< MemoryPools[4]: 0x00b11bf8 line: 59506 file ==>..\..\..\SencorSource\source\testlib.c<==
07/22/19 09:52:05 0x00003b64 - MPInit <<< MemoryPools[5]: 0x00b11a88 line: 59506 file ==>..\..\..\SencorSource\source\testlib.c<==
07/22/19 09:52:05 0x00003b64 - MPInit <<< MemoryPools[6]: 0x00b11ab8 line: 59506 file ==>..\..\..\SencorSource\source\testlib.c<==
07/22/19 09:52:05 0x00003b64 - MPInit <<< MemoryPools[7]: 0x00b11b18 line: 59506 file ==>..\..\..\SencorSource\source\testlib.c<==
07/22/19 09:52:05 0x00003b64 - MPInit <<< UNEXPECTED mpName: 8 line: 59484 file ==>..\..\..\SencorSource\source\testlib.c<==
07/22/19 09:52:05 0x00003b64 - MPInit <<< retVal: -1050 line: 59528 file ==>..\..\..\SencorSource\source\testlib.c<==
07/22/19 09:52:05 0x00003b64 - MemoryPool <<< MPInit status: -1050 memPoolID: 8 line: 59712 file ==>..\..\..\SencorSource\source\testlib.c<==
07/22/19 09:52:05 0x00003b64 - MemoryPool <<< memory: 0x00b11b18 line: 59738 file ==>..\..\..\SencorSource\source\testlib.c<==
07/22/19 09:52:05 0x00003b64 - MemoryPool <<< memory: 0x00b11c18 line: 59738 file ==>..\..\..\SencorSource\source\testlib.c<==
07/22/19 09:52:05 0x00003b64 - MemoryPool <<< memory: 0x00b11d18 line: 59738 file ==>..\..\..\SencorSource\source\testlib.c<==
07/22/19 09:52:05 0x00003b64 - MemoryPool <<< memory: 0x00b11e18 line: 59738 file ==>..\..\..\SencorSource\source\testlib.c<==
07/22/19 09:52:05 0x00003b64 - MPAlloc <<< UNEXPECTED size: 256 line: 59649 file ==>..\..\..\SencorSource\source\testlib.c<==
07/22/19 09:52:05 0x00003b64 - MPAlloc <<< retVal: -1052 line: 59677 file ==>..\..\..\SencorSource\source\testlib.c<==
07/22/19 09:52:05 0x00003b64 - MemoryPool <<< MPAlloc status: -1052 memPoolID: 7 size: 256 line: 59733 file ==>..\..\..\SencorSource\source\testlib.c<==
07/22/19 09:52:05 0x00003b64 - MemoryPool <<< memory: 0x00000000 line: 59738 file ==>..\..\..\SencorSource\source\testlib.c<==
07/22/19 09:52:05 0x00003b64 - MPFree <<< UNEXPECTED mpName: -1 line: 59558 file ==>..\..\..\SencorSource\source\testlib.c<==
07/22/19 09:52:05 0x00003b64 - MPFree <<< retVal: -1050 line: 59585 file ==>..\..\..\SencorSource\source\testlib.c<==
07/22/19 09:52:05 0x00003b64 - MemoryPool <<< MPFree status: -1050 memPoolID: -1 line: 59751 file ==>..\..\..\SencorSource\source\testlib.c<==
07/22/19 09:52:05 0x00003b64 - MPFree <<< MemoryPools[0]: 0x00b11b68 line: 59572 file ==>..\..\..\SencorSource\source\testlib.c<==
07/22/19 09:52:05 0x00003b64 - MPFree <<< MemoryPools[1]: 0x00b11b78 line: 59572 file ==>..\..\..\SencorSource\source\testlib.c<==
07/22/19 09:52:05 0x00003b64 - MPFree <<< MemoryPools[2]: 0x00b11a68 line: 59572 file ==>..\..\..\SencorSource\source\testlib.c<==
07/22/19 09:52:05 0x00003b64 - MPFree <<< MemoryPools[3]: 0x00b11aa8 line: 59572 file ==>..\..\..\SencorSource\source\testlib.c<==
07/22/19 09:52:05 0x00003b64 - MPFree <<< MemoryPools[4]: 0x00b11bf8 line: 59572 file ==>..\..\..\SencorSource\source\testlib.c<==
07/22/19 09:52:05 0x00003b64 - MPFree <<< MemoryPools[5]: 0x00b11a88 line: 59572 file ==>..\..\..\SencorSource\source\testlib.c<==
07/22/19 09:52:05 0x00003b64 - MPFree <<< MemoryPools[6]: 0x00b11ab8 line: 59572 file ==>..\..\..\SencorSource\source\testlib.c<==
07/22/19 09:52:05 0x00003b64 - MPFree <<< MemoryPools[7]: 0x00b11b18 line: 59572 file ==>..\..\..\SencorSource\source\testlib.c<==
07/22/19 09:52:05 0x00003b64 - MPFree <<< UNEXPECTED mpName: 8 line: 59558 file ==>..\..\..\SencorSource\source\testlib.c<==
07/22/19 09:52:05 0x00003b64 - MPFree <<< retVal: -1050 line: 59585 file ==>..\..\..\SencorSource\source\testlib.c<==
07/22/19 09:52:05 0x00003b64 - MemoryPool <<< MPFree status: -1050 memPoolID: 8 line: 59751 file ==>..\..\..\SencorSource\source\testlib.c<==

Following is the source code for the function that initializes the memory pools:

int __stdcall	MPInit	(
						int		mpName
						)

//	***************************************************************@@
//	- Initialize the specified memory pool.
//	*****************************************************************

{

int			retVal,											// returned by this function
			status;											// returned by function calls

// **** initialization ****
retVal		= 0;											// hope all goes well

// **** perform sanity checks ****
if ((mpName < 0) || (mpName >= MAX_MEMORY_POOLS))
	{
	EventLog(EVENT_ERROR,
	"MPInit <<< UNEXPECTED mpName: %d line: %d file ==>%s<==\n",
	mpName, __LINE__, __FILE__);
	retVal = WAR_INVALID_ARGUMENT_VALUE;
	goto done;
	}

// **** allocate a memory pool ****
MemoryPools[mpName] = calloc((size_t)1,
							 (size_t)sizeof(MAX_POOL_CAPACITY));

// **** check if something went wrong ****
if (MemoryPools[mpName] == (void *)NULL)
{
	EventLog(EVENT_ERROR,
	"MPInit <<< calloc line: %d file ==>%s<==\n",
	__LINE__, __FILE__);
	retVal = WAR_NO_MORE_MEMORY;
	goto done;
}

// **** inform the user what is going on ****
EventLog(EVENT_INFO,
"MPInit <<< MemoryPools[%d]: 0x%08lx line: %d file ==>%s<==\n",
mpName, MemoryPools[mpName], __LINE__, __FILE__);

// **** set the remaining capacity ****
usedCapacity[mpName] = 0;

// **** initialize the mutex used to allocate memory from the pool ****
status = MutexInit(	&mpMutex,
					(BOOL)(0 == 1));
if (status != 0)
{
	EventLog(EVENT_ERROR,
	"MPInit <<< MutexInit status: %d line: %d file ==>%s<==\n",
	status, __LINE__, __FILE__);
	retVal = status;
	goto done;
}

// **** clean up ****
done:

// **** inform the user what is going on ****
if ((retVal != 0) || (traceExecution != 0))
	EventLog(EVENT_INFO, "MPInit <<< retVal: %d line: %d file ==>%s<==\n", retVal, __LINE__, __FILE__);

// **** inform the caller what went on ****
return retVal;
}

We start by performing some checks.

After that we allocate memory for each pool followed by initializing variables and the initialization of a mutex that will be used to grant access to the memory allocation function to avoid rush condition sin multithreaded environments.

The function used to allocate memory follows:

int __stdcall	MPAlloc	(
						int				mpName,
						unsigned long	size,
						void			**memory
						)

//	***************************************************************@@
//	- Allocate memory from the specified memory pool.
//	*****************************************************************

{

BOOL		gotMutex;										// got mutex flag

int			retVal,											// returned by this function
			status;											// returned by function calls

// **** initialization ****
retVal		= 0;											// hope all goes well

gotMutex	= (BOOL)(0 == 1);								// for starters

// **** perform sanity checks ****
if ((mpName < 0) || (mpName >= MAX_MEMORY_POOLS))
{
	EventLog(EVENT_ERROR,
	"MPAlloc <<< UNEXPECTED mpName: %d line: %d file ==>%s<==\n",
	mpName, __LINE__, __FILE__);
	retVal = WAR_INVALID_ARGUMENT_VALUE;
	goto done;
}

// **** ****
if (memory == (void *)NULL)
{
	EventLog(EVENT_ERROR,
	"MPAlloc <<< UNEXPECTED memory: 0x%08lx line: %d file ==>%s<==\n",
	memory, __LINE__, __FILE__);
	retVal = WAR_INVALID_ARGUMENT_VALUE;
	goto done;
}
*memory = (void *)NULL;

// **** get access to the mutex ****
status = MutexLock(&mpMutex);
CDP_CHECK_STATUS("MPAlloc <<< MutexLock", status); // **** flag that we have the mutex **** gotMutex = (BOOL)(1 == 1); // **** **** if ((size == 0) || (size > (MAX_POOL_CAPACITY - usedCapacity[mpName])))
	{
	EventLog(EVENT_ERROR,
	"MPAlloc <<< UNEXPECTED size: %lu line: %d file ==>%s<==\n",
	size, __LINE__, __FILE__);
	retVal = WAR_NO_MORE_MEMORY;
	goto done;
	}

// **** allocate memory from this pool ****
unsigned char *bp	 = MemoryPools[mpName];
bp					+= usedCapacity[mpName];
*memory				 = bp;

// **** update the used capacity in this pool ****
usedCapacity[mpName] += size;

// **** clean up ****
done:

// **** release the mutex (if needed) ****
if (gotMutex)
{
	status = MutexUnLock(&mpMutex);
	if (status != 0)
		EventLog(EVENT_ERROR,
		"MPAlloc <<< MutexUnLock status: %d line: %d file ==>%s<==\n",
		status, __LINE__, __FILE__);
}

// **** inform the user what is going on ****
if ((retVal != 0) || (traceExecution != 0))
	EventLog(EVENT_INFO, "MPAlloc <<< retVal: %d line: %d file ==>%s<==\n", retVal, __LINE__, __FILE__);

// **** inform the caller what went on ****
return retVal;
}

Note the same pattern. We initialize, check arguments and then perform the task at hand.

Note that we need to keep the address of the memory allocated per pool because we will be using them to free the memory when the system shuts down.

Finally the code for the function that frees individual memory pools follows:

int __stdcall	MPFree	(
						int		mpName
						)

	//	***************************************************************@@
	//	- Free the specified memory pool.
	//	*****************************************************************

{

int			retVal,											// returned by this function
			status;											// returned by function calls

// **** initialization ****
retVal		= 0;											// hope all goes well

// **** perform sanity checks ****
if ((mpName < 0) || (mpName >= MAX_MEMORY_POOLS))
{
	EventLog(EVENT_ERROR,
	"MPFree <<< UNEXPECTED mpName: %d line: %d file ==>%s<==\n",
	mpName, __LINE__, __FILE__);
	retVal = WAR_INVALID_ARGUMENT_VALUE;
	goto done;
}

// **** check if this pool has not been allocated****
if (MemoryPools[mpName] == NULL)
{
	goto done;
}

// **** inform the user what is going on ****
EventLog(EVENT_INFO,
"MPFree <<< MemoryPools[%d]: 0x%08lx line: %d file ==>%s<==\n",
mpName, MemoryPools[mpName], __LINE__, __FILE__);

// **** free the specified memory pool ****
free((void *)MemoryPools[mpName]);

// **** flagged that the memory has been released ****
MemoryPools[mpName] = (void *)NULL;

// **** clean up ****
done:

// **** inform the user what is going on ****
if ((retVal != 0) || (traceExecution != 0))
	EventLog(EVENT_INFO, "MPFree <<< retVal: %d line: %d file ==>%s<==\n", retVal, __LINE__, __FILE__);

// **** inform the caller what went on ****
return retVal;
}

If you have comments or questions regarding this or any other post in this blog, or if you would like me to help with any phase in the SDLC (Software Development Life Cycle) of a product or service, please do not hesitate and leave me a note below. Requests for help will remain private.

Keep on reading and experimenting. It is the best way to learn and refresh your knowledge!

John

Follow me on Twitter:  @john_canessa

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.