Resource management is one of the more painful things that we have to deal with in software. Invariably, when we acquire something we have to release it. Garbage collected languages pull this burden away from us with respect to memory management, but if we have GC we still have to manually manage open file handles, database connections, and caches.
Recently, I got involved in a discussion of resource management with some C++ programmers, and I was struck by their attachment to RAII, a resource management idiom that is pervasive in C++ code. Some of them were of the opinion that RAII is an essential language feature. That's fair enough. I program in C++ and I'm attached to many of its features, but the thing that shocked me was the fact that I think RAII is a neat trick, but one with some significant downsides compared to other possible language features.
So, what is RAII and what are its downsides?
RAII stands for Resource Acquisition Is Initialization. It’s a technique which leverages the semantics of stack-based objects to tackle the resource management problem. Here’s a very simple example of RAII with error checking: removed for clarity:
class writeablefile {
writeablefile(char *filename) : fp(fopen(filename, “w”) {}
~writablefile() { fclose(fp); }
void write(char *text) { fprintf(fp, "%s", text); }
private:
FILE *fp;
};
To use a writeable file, you create it on the stack like this:
writeablefile outfile(“out.txt”);
And call write:
outfile.write(“Hello there..\n”);
The thing that makes this nice is that the file is closed automatically when the outfile object goes out of scope: you don’t have to call a close function.
In C++, we use RAII for all sorts of things. We use it for file I/O, locks, smart pointers, just about any resource, and it’s a good fit, but it’s definitely not the only way to approach resource management. Let’s look at an alternative. Here’s some Ruby code that performs the same operation:
File.open("out.txt", "w+") do |outfile|
outfile.write("hello there..\n")
end
In this code, the function open receives a block (all of the code between do and end). It then opens a file, passes a file object to the block, executes the block, and then closes the file.
This pattern is often known as Execute Around Method (EAM), and it’s a great way of approaching resource management problems. You put acquisition and release in one method and delegate to another which does the guts of what you need to do.
RAII and EAM.. Is there any reason to prefer one to the other? When I work in C++, I use RAII because it is idiomatic. There are a few places where EAM shows up naturally but they are rare. EAM is painful in languages which don’t have blocks, closures, or anonymous functions. In those languages you have to create new classes or objects to pass along. Both C++ and Java are getting closures in their next editions, so there is a chance that EAM will become more popular and I hope it does.
RAII is elegant, but I think that it has two warts. One is lack of explicitness. RAII makes resource acquisition a side effect of object creation. When you want to take advantage of it, you are often left struggling for a good class name that will make the RAII-ness of an object clear. Here’s an example. People often write RAII objects to manage semaphores and mutexes. You can create a lock on the stack, it acquires a mutex and then releases it when it goes out of scope i.e., when the function exits:
void some_function() {
Lock lock(&mutex);
...
}
It’s fine. It works, but it is hard to argue that it is as clear as:
void some_function() {
Lock lock(&mutex);
lock.acquire();
...
lock.release();
}
Does the name Lock really tell us that we are declaring something that locks a scope automatically? The boost libraries have an abstraction that does this called lock_guard. The name is a bit better, but it's a shame that it came so late. There are many RAII classes with names that don't adequately convey the semantics.
Cases where an abstraction supplies a manual acquisition protocol often let do more questions:
void some_function() {
Lock lock(&mutex);
lock.acquire();
...
// no release, is the destructor handling it or is it a bug?
}
In code where RAII is used sporadically, you often have to hunt to figure out whether you are using an RAII enabled abstraction.
The second wart is way that RAII affects scope and maintenance. This is a more subtle point. When you use EAM, you create a new scope. You create a block that will execute inside the EAM, and, chances are, it will have a distinct responsibility. Yes, you can mix concerns by nesting EAMs like this:
File.open("out.txt", "w+") do |outfile|
outfile.write("hello there..\n")
...
File.open("out2.txt", "w+") do |outfile2|
outfile2.write("hello other file..\n")
end
...
end
But luckily, that sort of thing bothers most people. The RAII case, on the other hand, doesn’t seem to bother as many people:
void some_function()
{
writeablefile outfile(“out.txt”);
writeablefile outfile2(“out2.txt”);
// mixed code to write to both files
...
}
It’s nice when a language feature makes it more natural to focus on one thing at a time.
RAII definitely has advantages in C++. One of them is exception safety. If you throw an exception in a function that uses RAII to manage a lock, the destructor will be called. To do get the same behavior in a language like Java or Ruby, you have to use a try-finally or ensure block in an EAM method.
The fact that C++ doesn’t have a finally mechanism for exceptions makes RAII the tool of choice. I can’t help thinking, however, that as useful as RAII is, it was a pun built upon the semantics of stack-based objects in C++, and that EAM is a better general purpose mechanism.

Comments (9)
As someone who has spend a lot of time with both C++ and Ruby, a few points:
1. My biggest annoyance with EAM is that sometimes you really DO want to allocate a number of resources at once, use them, and then automatically have them cleaned up. With EAM this means ugly multi-level nesting. Where the nesting is often arbitrary, not actually indicative of which resource belongs "inside" the lifetime of another resource.
For instance, if you are writing to two parallel log files - for redundancy, or maybe they log two different kinds of events - you really do want both of them open at the same time, but which one do you nest inside the other? And how do you scale this to N log files? EAM doesn't scale to N cleanly.
2. A related issue is the one where you want to turn an EAM block "inside out" so that you can take control of a resource's lifetime. This is what the Generator library in Ruby did, but with the fate of continuations uncertain, this may not be a viable option going forward.
3. We usually used the term "Guard" to make it clearer that the RAII pattern was being used. Maybe it's an ACE thing, although I think I've seen the C++ guard pattern discussed elsewhere.
Posted by Avdi Grimm | April 18, 2008 3:25 PM
I have the impression that EAM (which boils down to CPS) works only for simple cases where the allocation and deallocation of a resource are nicely symmetric in a single scope.
Consider the following C++0x code (using lambda expressions, though it could have been written C++03 style with either some bind()'s or separate functor classes) which does not exhibit such simplicity:
void register_callbacks(registry & r) { shared_ptr<ofstream> f(new ofstream("bla")); r.on_foo_do([=]{ *f << "foo happened!\n"; }); r.on_bar_do([=]{ *f << "bar happened!\n"; }); }Here, the file will be automatically closed once both callbacks (and any copies made by the registry) are gone, which could be long after register_callbacks returns. I don't see how EAM could do something like this. How would you do this in Ruby?
(This may be related to Avdi's second point.)
Posted by Eelis | April 18, 2008 4:49 PM
There's one important design issue that RAII improves over garbage collected systems in my opinion.
When discussing this issue with fans of GC in the past, they've always argued that "finally" (or whatever it happens to be called in their language) solves that resource handling/exception issue.
In my view, finally has the fatal flaw of violating DRY. RAII allows the creator of the class to write the cleanup code once. Most of the GC solutions, instead require the cleanup code to be written N times by the users of the class.
This is one of the things that convinces me that RAII is a good thing. (Although I will definitely grant you the naming problem.)
Posted by G. Wade | April 18, 2008 8:27 PM
@Eelis: I'm not sure whether your example even counts as an instance of the RAII idiom strictly speaking. To me it feels more similar to leaving resource release to the GC, although a shared_ptr using reference counting is deterministic, of course.
Posted by Michael Schuerig | April 19, 2008 4:27 AM
G. Wade: I think that's what we see in practice all the time, but it doesn't have to be that way. You can use 'finally' in an EAM to guarantee cleanup in the face of exceptions. I ran across a library of EAMs for file handling in Java a while back an they used that technique.
Posted by Michael Feathers | April 19, 2008 8:34 AM
@Michael Schuerig:
For its resource management, my example code relies completely on proper RAII semantics for ofstream's, shared_ptr's, and closures' destructors. If the use of shared_ptr (which owes its existence to RAII) looks like cheating to you, then that is merely a testament to the versatility of RAII.
I'm still wondering how people would do this in languages without RAII :-).
Posted by Eelis | April 19, 2008 9:45 AM
@Eelis Nice, but I'm not sure I'd want to do that. Looks like that would hold references to an open file for as long as a callback is registered. Sends shivers down my spine.
I do admit it is neat, though.
Posted by Michael Feathers | April 19, 2008 11:25 AM
Michael: My point is that RAII provides a useful idiom that helps ensure that cleanup is called at the right time. I don't have much experience with the EAM approach, so I don't know if it would cover all of my concerns.
My experience with languages without the RAII approach has always resulted cleanup code scattered throughout the code (in 'finally' type blocks if we're lucky). RAII seemed to me a relatively nice way to "pair up" startup/shutdown, acquire/release, allocate/deallocate, etc. kinds of code. It also has the advantage of being relatively easy to use.
From your description of the EAM approach, it sounds like a useful technique to explore.
Thanks for the thought-provoking post.
Posted by G. Wade | April 21, 2008 11:33 PM
One problem my team has encountered with RAII is handling errors that may occur during cleanup. Because cleanup is done in the destructor, we don't want to throw an exception. Does anyone have advice for properly handling this case? Simply ignoring errors during cleanup doesn't seem like a good solution.
Posted by Andrew Young | April 30, 2008 5:01 PM