Resource Acquisition Is Initialization (RAII) is a programming idiom used in C++ for resource management. To understand it clearly let us look at a common programming problem.
Consider a method that needs to be synchronized across multiple threads. If you were programming against Win32 and if you were using Critical Sections as your synchronization primitive, you would call EnterCriticalSection at the beginning of the method and call LeaveCriticalSection at the end.
void DoStuff()
{
EnterCriticalSection(_pcs);
/* Code that should accessed only by one thread at a time. */
LeaveCriticalSection(_pcs);
}
There are two major drawbacks to this approach.
The first is that the programmer might forget to call LeaveCriticalSection at the end of the code. The chances of this happening increases when there are multiple exit points in the function. Even if you take care to exit at a single point and do the resource cleanup it is possible that somebody maintaining your code several years later might add an exit point in the middle of the function thereby missing cleanup. While code reviews will help alleviate this problem to some extend I have personally seen this happening in code that was reviewed by multiple people, unit tested and checked in.
The second problem with manually allocating and cleaning up resources is exceptions. Unless you handle the exception and do cleanup in the exception handler, you will leak your resource. In C++ exceptions are not idiomatic and you rarely see exception handlers. Even though your code itself might not throw exceptions the code that you call into might throw exceptions (for example a third party library). Most implementations nowadays When an exception is thrown after you acquired a resource but before your cleanup code is executed, your resource is now leaked. If we consider the example I mentioned above, your code has now acquired a thread synchronization primitive but never released it. There is a chance you will have a thread waiting forever. That may mean an unresponsive UI and an unhappy customer.
RAII solves this problem. RAII depends on three C++ features – constructors, destructors and the ability to create objects on the stack. The constructor of a class is invoked when an instance of the class is created. The destructor of a class is invoked when the instance is deleted. Objects created on the stack are automatically deleted when it goes out of scope.
This is how we use RAII to solve our problem:
- We need a class whose purpose is to manage the resource. In the example above, the resource is the critical section. Let us create a class named Lock to manage the critical section.
- An instance of the resource manager should be created on the stack.
- The resource is initialized in the constructor of the resource manager. So, in our example, we call EnterCriticalSection() in the constructor of the Lock class.
- We add the code that use the resource. In our case, we add the code that needs to be synchronized between multiple threads.
- The resource is cleaned up in the destructor. Since the object is on the stack, the object is deleted when the function exits and the stack unwinds. In our example, we call LeaveCriticalSection() in the destructor of the Lock class.
// Manages a critical section. class Lock { public: Lock(PCRITICAL_SECTION pcs) : _pcs(pcs) { // Initialize resource in the constructor. EnterCriticalSection(_pcs); } ~Lock() { // Release resource in the destructor. LeaveCriticalSection(_pcs); } private: PCRITICAL_SECTION _pcs; };
Now the DoStuff() method can be re-written as follows:
void DoStuff() { Lock lock(_pcs); /* Create the resource manager on the stack */ /* Code that should accessed only by one thread at a time. */ /* Critical section automatically released when function returns. */ }
Of course this can be wasteful only if a small part of the code needs to be synchronized. The way it is implemented above, the whole function is synchronized. We can get around this limitation by creating a new scope and using the lock only inside that scope.
void DoStuff() { /* Code that need not be synchronized. */ { // Start new scope. Lock lock(_pcs); /* Code that should accessed only by one thread at a time. */ } // End of the scope. lock is destroyed and crtical section is released. /* More code that need not be synchronized. */ } That is it folks. Guaranteed cleanup using RAII.
Pingback: Adding tracing to your code | Mélange
Hey There. I found your weblog using msn. This is a really smartly written article. I’ll be sure to bookmark it and return to read more of your helpful information. Thanks for the post. I will definitely comeback.
Thanks , I have just been searching for information about this topic for ages and yours is the best I have discovered till now. But, what about the conclusion? Are you sure about the source?