Resource Acquisition Is Initialization is
a programming idiom used in several object-oriented
languages, most prominently C++, where it
originated, but also D, Ada, Vala, and Rust.
The technique was developed for exception-safe
resource management in C++ during 1984–89,
primarily by Bjarne Stroustrup and Andrew
Koenig, and the term itself was coined by
Stroustrup.
RAII is generally pronounced as an initialism,
sometimes pronounced as "R, A, double I".
In RAII, holding a resource is tied to object
lifetime: resource allocation is done during
object creation, by the constructor, while
resource deallocation is done during object
destruction, by the destructor.
If objects are destructed properly, resource
leaks do not occur.
Other names for this idiom include Constructor
Acquires, Destructor Releases and one particular
style of use is called Scope-based Resource
Management.
This latter term is for the special case of
automatic variables.
RAII ties resources to object lifetime, which
may not coincide with entry and exit of a
scope.
However, using RAII for automatic variables
is the most common use case.
C++11 example
The following C++11 example demonstrates usage
of RAII for file access and mutex locking:
This code is exception-safe because C++ guarantees
that all stack objects are destroyed at the
end of the enclosing scope, known as stack
unwinding.
The destructors of both the lock and file
objects are therefore guaranteed to be called
when returning from the function, whether
an exception has been thrown or not.
Local variables allow easy management of multiple
resources within a single function: they are
destroyed in the reverse order of their construction,
and an object is destroyed only if fully constructed—that
is, if no exception propagates from its constructor.
Using RAII greatly simplifies resource management,
reduces overall code size and helps ensure
program correctness.
RAII is therefore highly recommended in C++,
and most of the C++ standard library follows
the idiom.
Benefits
The advantages of RAII as a resource management
technique are that it provides encapsulation,
exception safety, and locality.
Encapsulation is provided because resource
management logic is defined once in the class,
not at each call site.
Exception safety is provided for stack resources
by tying holding the resource to the lifetime
of a stack variable: if an exception is thrown,
and proper exception handling is in place,
the only code that will be executed when exiting
the current scope are the destructors of objects
declared in that scope.
Finally, locality of definition is provided
by writing the constructor and destructor
definitions next to each other in the class
definition.
Resource management therefore needs to be
tied to the lifespan of suitable objects in
order to gain automatic allocation and reclamation.
Resources are acquired during initialization,
when there is no chance of them being used
before they are available, and released with
the destruction of the same objects, which
is guaranteed to take place even in case of
errors.
Comparing RAII with the finally construct
used in Java, Stroustrup wrote that “In
realistic systems, there are far more resource
acquisitions than kinds of resources, so the
"resource acquisition is initialization" technique
leads to less code than use of a "finally"
construct.”
Typical uses
The RAII design is often used for controlling
mutex locks in multi-threaded applications.
In that use, the object releases the lock
when destroyed.
Without RAII in this scenario the potential
for deadlock would be high and the logic to
lock the mutex would be far from the logic
to unlock it.
With RAII, the code that locks the mutex essentially
includes the logic that the lock will be released
when execution leaves the scope of the RAII
object.
Another typical example is interacting with
files: We could have an object that represents
a file that is open for writing, wherein the
file is opened in the constructor and closed
when execution leaves the object's scope.
In both cases, RAII ensures only that the
resource in question is released appropriately;
care must still be taken to maintain exception
safety.
If the code modifying the data structure or
file is not exception-safe, the mutex could
be unlocked or the file closed with the data
structure or file corrupted.
Ownership of dynamically allocated objects
can also be controlled with RAII, such that
the object is released when the RAII object
is destroyed.
For this purpose, the C++11 standard library
defines the smart pointer classes std::unique_ptr
for single-owned objects and std::shared_ptr
for objects with shared ownership.
Similar classes are also available through
std::auto_ptr in C++98, and boost::shared_ptr
in the Boost libraries.
GCC "cleanup" extension for C
The GNU Compiler Collection implements a non-standard
extension to the C language to support RAII:
the "cleanup" variable attribute.
The following macro allows to annotate a variable
with a given destructor function that it will
call when the variable goes out of scope:
This macro can then be used as follows:
In this example, the compiler arranges for
the fclosep function to be called before example_usage
returns.
Limitations
RAII only works for resources acquired and
released by stack-allocated objects.
Heap-allocated objects which themselves acquire
and release resources are encountered however
even in languages like C++ and stack-allocated
objects are unavailable in some languages,
most notably in Java.
For RAII to be possible for a heap-based object,
in contemporary languages the programmer must
implicitly or explicitly delete the object
along all possible execution paths, in order
to trigger its resource-releasing destructor.
There are number of possible workarounds and
proposed extensions of RAII to heap-allocated
objects.
Reference counting
Perl, Python, and PHP manage object lifetime
by reference counting, which makes it possible
to use RAII.
Objects that are no longer referenced are
immediately destructed or finalized and released,
so a destructor or finalizer can release the
resource at that time.
However, it is not always idiomatic in such
languages, and is specifically discouraged
in Python.
However, object lifetimes are not necessarily
bound to any scope, and objects may be destroyed
non-deterministically or not at all.
This makes it possible to accidentally leak
resources that should have been released at
the end of some scope.
Objects stored in a static variable may not
be finalized when the program terminates,
so their resources are not released; CPython
makes no guarantee of finalizing such objects,
for instance.
Further, objects with circular references
will not be collected by a simple reference
counter, and will live indeterminately long;
even if collected, destruction time and destruction
order will be non-deterministic.
In CPython there is a cycle detector which
detects cycles and finalizes the objects in
the cycle, though prior to CPython 3.4, cycles
are not collected if any object in the cycle
has a finalizer.
Also, in the case of Python, the garbage collection
strategy is an implementation detail, so running
with an alternative interpreter could result
in the RAII implementation not working.
Notes
References
External links
Sample Chapter "Gotcha #67: Failure to Employ
Resource Acquisition Is Initialization" by
Stephen Dewhurst
Interview "A Conversation with Bjarne Stroustrup"
by Bill Venners
Article "The Law of The Big Two" by Bjorn
Karlsson and Matthew Wilson
Article "Implementing the 'Resource Acquisition
is Initialization' Idiom" by Danny Kalev
Article "RAII, Dynamic Objects, and Factories
in C++" by Roland Pibinger
RAII in Delphi "One-liner RAII in Delphi"
by Barry Kelly
