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.