[This post was revised on May 23, 2005.]
Resource Acquisition Is Initialization
I first learned about the “Resource acquisition is initialization” (RAII) technique from Bjarne Stroustrup’s The C++ Programming Language (3rd Edition). RAII changed the way I write C++ code.
The meaning behind the phrase “Resource acquisition is initialization” is that acquiring a resource is performed by constructing an appropriate object. The object manages the lifetime of the resource and the object’s destructor ensures the resource is ‘un-acquired’.
A resource, for the purposes of this discussion, is something that must be explicitly acquired and released. For example, heap memory acquired by new
and released by delete
is a resource.
Managing the lifetime of a resource can be difficult. RAII’s power and simplicity comes from making managing the lifetime of a resource the same as managing the lifetime of an object. Managing object lifetimes is comparatively easy. The language is designed for managing objects and RAII objects are often local automatics.
Deterministic Destruction
C++ guarantees an object’s destructor will be executed when the object either goes out of scope or is deleted. Because destruction depends on either scope or when delete
is invoked, it can be known when an object will be destructed. This is termed “deterministic destruction.”
Not all languages provide deterministic destruction and it’s usually essential for RAII. Why? Resources are limited and have temporal constraints. A resource that is both scarce and in high demand can’t be lazily released.
Without deterministic destruction an object that wraps a resource should expose a cleanup method and consumers of the object are responsible for calling that method. This is a very useful pattern (sometimes referred to as the “dispose pattern”) but it isn’t RAII and it introduces problems that RAII doesn’t suffer. First, because cleanup is separated from destruction, the object must guard against cleanup being performed more than once and against other methods being called after cleanup has been performed. Second, the abstraction created by the class type is compromised by requiring external knowledge that something within needs cleanup.
One-Stage Versus Two-Stage Construction
The Microsoft Foundation Class Library (MFC) is heavily invested in two-stage construction. Two-stage construction is when an object’s constructor does little or nothing and a consumer of the object must call a separate initialization method before the object is usable.
MFC uses two-stage construction because MFC was designed before exceptions became a standard part of the language. Prior to exceptions there was no mechanism for a constructor to signal a failure. Therefore it was prudent to never perform an action that could fail in a constructor.
C++ was standardized six years ago and Standard C++ includes exceptions and defines semantics for constructors that throw.
I favor one-stage construction. I want to make my job easier. I want to minimize the number of methods on a class and I want to minimize the opportunities for silly mistakes like forgetting to call an initialization method.
Two-stage done right would guard against attempts to use the object in an un-initialized state. (I want my objects to tell me when I’m being a bozo.) But guarding against un-initialized use adds complexity. (MFC classes don’t guard but also generally have no encapsulation.)
With one-stage construction the constructor either successfully creates and fully initializes the object or there is no object. It’s not possible to have an object (that enforces invariants) in an unusable state.
Exceptions And Exception Safety
There are some who will question how one-stage construction can make things easier since it depends on using exceptions and exceptions are notoriously dangerous when thrown through code that is not exception safe.
I have a short smart-alecky answer: Exception unsafe code hurts, so stop writing it.
A longer answer is probably worthy of its own blog post but the essential issue is that exceptions contaminate a codebase. If the potential for exceptions exists in a codebase (in the present or in the future) it’s prudent to assume that an exception could happen nearly anywhere and nearly all code should be written to defend against exceptions.
Exceptions are not evil and there are plenty of ways that don’t involve exceptions to create buggy leaky code. However the presence of exceptions does impact how code should be written. The good news is that most of the techniques for writing exception safe code are good practices in their own right. Which comes back to the topic at hand: RAII is indispensable for ensuring resources are not leaked when an exception is thrown and the stack is unwound.
RAII is DRY
RAII supports the DRY principle. DRY stands for “Don’t Repeat Yourself.” (See my previous post: constructive nonconformist: A Single Point of Correctness.)
An RAII class provides a single expression of how to correctly acquire and release a given resource. Creating an instance of an RAII object can replace copying and pasting boiler plate code for acquisition and release. In this way RAII protects against programmer error and reduces code clutter.
RAII Example
RAII may sound great in concept but “the proof of the pudding is in the eating.” So the following is a concrete example of using RAII.
To support critical sections, the Microsoft Windows Win32 API includes a specialized critical section synchronization object. A CRITICAL_SECTION
struct must be initialized before use and deleted after use by the InitializeCriticalSection()
and DeleteCriticalSection()
APIs respectively. This fits the resource usage pattern of an RAII class.
Here’s an RAII class for wrapping a CRITICAL_SECTION
:
class CriticalSection
{
public:
CriticalSection()
{ ::InitializeCriticalSection(&m_rep); }
~CriticalSection()
{ ::DeleteCriticalSection(&m_rep); }
void Enter()
{ ::EnterCriticalSection(&m_rep); }
void Leave()
{ ::LeaveCriticalSection(&m_rep); }
private:
// copy ops are private to prevent copying
CriticalSection(const CriticalSection&);
CriticalSection& operator=(const CriticalSection&);
CRITICAL_SECTION m_rep;
};
Copying a CriticalSection
object doesn’t make sense so copying is disallowed. See constructive nonconformist: Disallowing Copying in a C++ Class for details.
Another RAII class can be used to create a critical section synchronization lock.
class CSLock
{
public:
CSLock(CriticalSection& a_section)
: m_section(a_section) { m_section.Enter(); }
~CSLock()
{ m_section.Leave(); }
private:
// copy ops are private to prevent copying
CSLock(const CSLock&);
CSLock& operator=(const CSLock&);
CriticalSection& m_section;
};
Together the CriticalSection
and CSLock
classes can be used to synchronize thread access to the state of an object:
class Example
{
public:
bool Process(
);
private:
CriticalSection m_criticalsection;
};
bool Example::Process(
)
{
CSLock lock(m_critsection);
// do some stuff
}
Only one thread at a time may acquire a critical section object. A second thread entering Example::Process()
will block on the construction of the lock
object until the first thread exits the function. If an exception is thrown in or through Example::Process()
the lock
object will still release.
By wrapping the raw Win32 structs and APIs, the RAII classes in this example allow coding at a higher level of abstraction.
Related Reading
Programming with Exceptions By Bjarne Stroustrup
Stroustrup: C++ Style and Technique FAQ: Why doesn’t C++ provide a “finally” construct?
Abstraction and Efficiency, A Conversation with Bjarne Stroustrup, Part III
I totally agree on the RAII idiom and the Lock is a "beautiful" example BUT:
Having a function static
static CriticalSection s_criticalsection;
in your multithreaded code how does that work??? You need a lock around the constructor of that one and it's catch 22. Isn't function statics initialized on the first "entry" of a thread?
So when I use this idiom, I make the CriticalSection "file" static so the constructor will be run by the CRT init.
I don't know if the above is different between compilers?
Thanks
/Jonas
Posted by: Jonas | February 03, 2005 at 03:14 AM
Thanks Jonas.
You are completely correct. I wanted to keep the example simple but I made it too simple. Its bad code and I need to change it.
Thanks for seeing the error and telling me about it. I appreciate the constructive feedback.
Posted by: Jonathan Dodds | February 03, 2005 at 10:41 PM
Hi Jonathan
great work, can you please update the example code with "static functions"and with "static CriticalSection s_criticalsection;" like Jonas's above comment..
good work,very helpful
thanks a lot
Posted by: anon_coder | July 04, 2005 at 11:56 PM
anon_coder,
I updated the example code in May to remove the function static.
The original example code was something like:
bool Process( )
{
static CriticalSection s_criticalsection;
CSLock lock(s_criticalsection);
// do some stuff
}
Using a function static like this is unsafe with multiple threads. There is a potential for a race condition. The s_criticalsection object is constructed on the first entry into the function. If two (or more) threads enter the function concurrently for the first entry each thread may construct its own s_criticalsection object.
A solution is to make s_criticalsection a file static instead of a function static:
static CriticalSection s_criticalsection;
bool Process( )
{
CSLock lock(s_criticalsection);
// do some stuff
}
In the file static version you pay for the construction of s_criticalsection regardless of whether Process() is called.
When I revised the example code I used a class example because I think it's a more interesting case and because I had originally intended to make a point about lazy construction.
Posted by: Jonathan Dodds | July 06, 2005 at 09:14 PM
Congratulations, very good article! You even make copy constructor and operator= private. Every C++ programmer should know that. Found this article through a link in another RAII article: http://www.codeproject.com/cpp/RAIIFactory.asp
Posted by: Arno Nymous | September 12, 2005 at 05:23 AM