Resource Acquisition at Initialisation and Leakproofing Windows Applications

C and C plus plus (CPP) programmers know that it is very easy to leak memory.  All it takes is a malloc (C) or a new (CPP)  that somehow fails to be matched with a free (C) or delete (CPP).  The same is true for resources.  If a file handle is opened or, in Windows, a HANDLE acquired then these resources need to be disposed of properly.  File handles should be closed, Windows HANDLEs should be passed to CloseHandle().  So long as the programmer is careful and accurate it all works beautifully.  I find it just too hard to be that careful and that accurate.  This article describes a template class that helps Windows programmers avoid resource and memory leaks.

The problem

The problem is that the programmer must be careful and accurate.  In particular, if there is cleanup code at the bottom of a function, that clean up code must be repeated if there is an early exit.  For example: 

 

void foo()
{
char * p = malloc(100) ;

// ... code

if (early_exit)
   {
   free(p) ; // duplicated clean up code

   return ;
   }
 

// ... more code

free(p) ;
} ;

It gets rapidly more complicated if the function allocates multiple resources and does not always allocate them all.  Then the clean up code may become different at the different exit points and have conditional logic itself.

It gets worse still in the presence of CPP exceptions.  In the presence of exceptions there may be exit points from the function that do not have a return statement.  For example this version of foo leaks:

void foo()
{
char * p = new char[100] ;

// ... code

int i = 0 ;
int j = 42 ;

throw j; // Causes an exception - Concealed exit - leaks.

// ... more code

delete p ;
} ;

The standard library includes a template class auto_ptr<>. auto_ptr<> implements a 'smart pointer' [1] with 'ownership semantics' [2].  This version of foo does not leak: 

#include
void foo()
{
std::auto_ptr p(new char) ;

// ... code

int i = 0 ;
int j = 42 ;

throw j; // Causes an exception - Concealed exit - NO LEAK!!!

// ... more code

// Delete p NOT required - it's done by p's destructor
} ;


However, there are a number of issues here.  In particular, it's a different function.  Where we used to allocate an array of 100 char we now allocate only one char.  This is because auto_ptr<> is not capable of deleting an array.  It only works with single objects. The same kind of issues arise with resources other than memory.  Here's an example, that leaks, using a Windows HANDLE

#include
void foo()
{
HANDLE hThread ;

// ... code

hThread = ::CreateThead( ... parameters...) ;

// ... code

int i = 0 ;
int j = 42 ;

throw j; // Causes an exception - Concealed exit - leaks

// ... more code

::CloseHandle(hThread) ;
} ;


In this case the resource allocated and leaked is not pure memory it is a Windows kernel object.  The leaked object will remain alive until the process ends and process termination clears up.  You can monitor the Windows handle count used by your program in Task Manager [3]. The CPP standard library provides exactly one solution, auto_ptr<>.  Sadly, real programming, presents us with a range of resource disposal problems.  This is dealt with, very effectively, by Kevlin Henney in a pair of articles he wrote for Application Development Advisor.  Links to the pdf files are - Part 1, Part 2.  You really need to read, at least part 1 before you carry on here.

I have extended the code Kevlin presents to better support Windows programmers.  I took the code for Kevlin's scoped class, without assignment or copying.  I added support for operator& because, however ugly, it is the classic way for a Windows API function to return a HANDLE.  I added a (modest and incomplete) collection of function object structs to support various ways Windows uses to dispose of resource handles.  The support for operator& does destroy encapsulation so this class must be used with care.  Download the code here.

Here's an example of it in use, this time using handles from the Windows Crypto API: 

void foo()
{
scoped hProv ;
scoped hHash ;
scoped hKey ;

// These functions use the classic Windows API technique of

// expecting a pointer to a HANDLE.
bRc = ::CryptAcquireContext(&hProv, NULL, MS_DEF_PROV,
PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
bRc = ::CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash);
bRc = ::CryptHashData(hHash, (BYTE*)key, strlen(key), 0);
bRc = ::CryptDeriveKey(hProv, CALG_RC2, hHash, 0, &hKey);

// ... code. If anything goes wrong in here, including
// exceptions passed up from called functions then the
// three handles are properly disposed of by the
// class scoped destructor

// ... more code

// No cleanup code needed here. The class scoped destructor does it.
}

For this example to work it needs support from a special deleter object customized to properly dispose of the appropriate resource. Here's the one for disposing of an HCRYPTPROV which needs to be disposed of via a CryptReleaseContext() API call. 

struct CryptReleaseContext_deleter
{
   void operator()(HCRYPTPROV to_delete) const
   {
   //std::cout << "The thing going is a " << typeid(to_delete).name() << '\n' ;
   if (to_delete)
     ::CryptReleaseContext(to_delete, 0) ;
   }
} ;

The others are in the download file.  You will see that I chose a naming standard of APIFunctionName_deleter.It was the best I could think of at the time.

Why do I have to specify the deleter type?

A great question.  I wanted the compiler to deduce the deleter type automatically.  Then the programmer does not need to specify it and can't get it wrong.  Sadly, I think the Windows data types are too badly broken for it to be possible.  (Or it might be that my CPP is not good enough.  Call or email if you can make it work.)

If you compile your Windows program with #define STRICT - it's the default now so it's hard to avoid - then Windows types such as HANDLE, HINSTANCE etc, all become different types in the eyes of the compiler.  This means that the compiler can trap you using (say) ::CloseHandle() to dispose of an HINSTANCE.  Sadly Microsoft has not continued this all the way through.  Even though HCRYPTPROV, HCRYPTHASH and HCRYPTKEY  have to be disposed of differently, they  are all typedef'd to the same underlying type.  Therefore the compiler cannot tell them apart. 

Rather than have one rule for one set of types and a bunch of non-obvious exceptions, I thought it safer to make it compulsory.  I did make an exception for Kevlin's templated memory deleter, so we do have one rule for scalar memory and a second rule for everything else.

How do I use it?

It's all in the header file.  Download the code (click here). Just throw it in a directory that your compiler scans and you're done.

If you extend this...

Obviously this is incomplete.  I implemented a bunch of Windows types I needed for the code I was working on there and then.  If you implement any more please email me the new code and I'll add it. - Thanks.

References

[1] Smart pointer is a posh phrase for an object that behaves like a pointer (works with address values, works with the -> operator, etc, but is actually a CPP object).  By being an object it can be 'smarter' than an ordinary built-in pointer.

[2] Ownership semantics is a posh phrase meaning that the object (the smart pointer) believes that it is the sole owner of the bit of memory it points to.  When the object dies, and its destructor runs, it frees the memory it identifies, on the grounds that, as sole owner that is the right thing to do.

[3] Start task manager.  Click on View|Select Columns and choose Handle count.  This can give you a clue if your application has a run away handle leak.

Copyright 2002-15 by Dynamisys Ltd