Introduction
The memory allocation and deallocation routines of C++, i.e. new
and delete
are used widely for allocating / deallocating memory from the free store. In general, the first line of main
is written as:
int main()
{
_set_new_handler ( MyHandler );
return 0;
}
which is a good idea because you don't want your program to crash in case you run out of memory. But this has had its own drawback as it will arbitrarily transfer the control of the program to the function registered by calling _set_new_handler
. This may not be the desirable case for each and every scenario. What you might be willing to do is to free some memory and try again.
To save the program from arbitrarily jumping from one place to another, many developers overload the new
operator or catch
the std::bad_alloc
exception thrown from the new
operator when it is unable to allocate the required memory and handle the non allocation of the memory.
Of the above two approaches, if you are using the second approach then you need to write the code in a way so that you can test your code against allocation failure, i.e. you handled all allocation failures in your code. I am giving here the way to simulate memory failures in your source code to test against non allocation of memory.
Handling Memory Allocation Failure
If you are using the second option then handling for memory allocation failure is necessary. This can be understood by looking at the code below. Suppose you overloaded you new
operator to return NULL
in case it fails to allocate the desired memory.
void SimpleFunction ( )
{
char *Mem1 = new char [ 32 ];
strcpy ( Mem1 , "First Memory" );
}
If the new
fails to allocate memory then it will return NULL
and copying anything to NULL
will lead to crash or core dump. So the above code must be written as:
void SimpleFunction ( )
{
char *Mem1 = new char [ 32 ];
if ( Mem1 != NULL )
strcpy ( Mem1 , "First Memory" );
}
That's safe enough, you are not going to copy anything to NULL
. This is an immediate observation, but in a big code, you may easily forget to do so. So while doing your unit test, you must be able to check your code for such bugs. I will show you how to do it. You need to overload the new
operator and write some debugging code along with your normal code.
Overloading the operator new
You need to overload the new
operator in a way so that it returns NULL
in case it fails to allocate the desired memory. The overloaded new
operator will take an additional parameter which will direct it whether to allocate memory or simulate a failure by returning NULL
. Here is the code that shows how to overload the new
operator:
void *operator new ( size_t size , bool Alloc )
{
if ( Alloc )
return NULL;
else
{
try
{
void *Memory = ::operator new ( size );
return Memory;
}
catch ( std::bad_alloc )
{
return NULL;
}
}
return NULL;
}
The second parameter to the operator is a boolean and the memory will be allocated by calling actual new
only when this boolean is false
. The code that calls actual new
operator is encapsulated under try
and catch
block so that the exception std::bad_alloc
can be handled to return NULL
. Since we are overloading the new
operator with an additional parameter, it leads to a problem. We must have placement delete
corresponding to the new
operator so that memory can be freed up if constructor throws an exception. If you won't provide placement delete
nothing will happen. (MSVC6.0 gives a warning for this particular case). Here is the code containing the placement delete
for the overloaded new
operator:
void operator delete ( void * Memory , bool Alloc )
{
::delete Memory;
}
Now, you have to write the code in a way so that you can simulate memory allocation failures in each function. We can run the same function multiple times to check the behavior of failed memory. The checking can be done in two ways.
- Simulate failure of memory one by one, i.e. first allocation gets failed in first pass, second allocation in second pass and so on. You need to remember how many allocations are there in the function.
- Simulate failure of all the memory that is inside that function in a single pass.
Code to simulate memory allocation failure
You can use a counter in the function that guides which particular memory allocation to fail. On the first pass, the value of counter will be zero and it will fail first memory allocation. On the second pass, the value of the counter will be one and it will fail the second memory allocation, and so on.
The function will also have a boolean variable that will simulate failure of all the allocation routines. Here is the code of SimpleFunction
that shows how to simulate memory failure:
void SimpleFunction ( int MemCounter , bool AllFail )
{
int LocalCounter = 0;
char *Mem1 = new ( ( MemCounter == LocalCounter ++ ) || AllFail ) char [ 32 ];
if ( Mem1 != NULL )
strcpy ( Mem1 , "First Memory" );
char *Mem2 = new ( ( MemCounter == LocalCounter ++ ) || AllFail ) char [ 32 ];
if ( Mem2 != NULL )
strcpy ( Mem2 , "Second Memory" );
int NumTimesLoop = 10;
int LoopCounter = 0;
char **InnerMemory = new ( ( MemCounter == LocalCounter ++ )
|| AllFail ) char * [ NumTimesLoop ];
for ( LoopCounter = 0; LoopCounter < NumTimesLoop ; LoopCounter ++ )
{
InnerMemory [ LoopCounter ] = new ( ( MemCounter == LocalCounter ++ )
|| AllFail ) char [ 32 ];
}
}
The function takes two parameters. First parameter decides which allocation routines to fail and the second parameter if true
will fail all the memory allocations. The only thing that remains is the test stub to call the function multiple times. This is written in the main
code below:
int main()
{
for ( int counter = 0; counter < 12; counter ++ )
SimpleFunction ( counter , false );
SimpleFunction ( -1 , true );
return 0;
}
The main
function for
loop will call the function 12 times which will simulate the memory failure in each pass and the code written after the for
loop will fail all memory allocations of the function. You can see that if anywhere in the function you forget to add suitable code for handling memory allocation failure, it will be reported by this method.
History
Last updated on Feb 15, 2004.
A programmer by heart since 1998. Written code in C++, Java, JavaScript, Python & Ruby, Worked on Stack Development to Web Development. Data Specialist with SQL and NoSQL DBs