Use Scope Guards to Prevent Resource Leaks

Modern C++ has many neat RAII-based solutions for managing resources. However, every now and then one needs to manually make sure that some code is run when a resource is not needed anymore. One great solution to this problem is a scope guard. The concept is already very old, however I have surprisingly rarely seen them used (and used them too rarely myself). This article describes the concept, shows how to use it and gives some background on scope guards.

I’ll start by explaining the problem (essentially: manual resource management sucks). If you’re already convinced and just want to know how scope guards work to solve the problem, you can skip ahead to the second section. The final section contains an overview over available implementations.

The Problem

In modern C++, whenever one uses resources (memory, file handles, sockets, …), it is good practice to use wrapper classes around these resources that manage the lifetime of the resource following the Resource Acquisition Is Initialization (RAII) idiom. This ties the resource management itself (e.g. allocation and deallocation) to the lifetime of the wrapper object and prevents resource leaks. The most famous examples of this technique are probably std::shared_ptr and std::unique_ptr.

However, sometimes one needs to work directly with “raw” resources. This may be because you are working directly with e.g. network sockets, or because you are working with an old “C-style” library that requires manual initialization and deinitialization of its structures. If you pair this with the usual structure of “set-up functions”, which need to perform a lot of set-up steps, each of which can fail, this often results in ugly code like this:

 1bool doSomething() {
 2    MyFirstResource * first = acquireFirst();
 3    if (!first) return false;
 4
 5    MySecondResource second;
 6    initializeSecond(&second);
 7    if (!isValid(&second)) {
 8        delete first;
 9        return false;
10    }
11
12    MyThirdResource third;
13    initializeThird(third);
14    if (!third.isValid) {
15        delete first;
16        freeSecond(&second);
17        return false;
18    }
19
20    // … do something useful with all the allocated resources …
21
22    // If we didn't pass ownership of the resources somewhere,
23    // clean them up!
24    delete first;
25    freeSecond(&second);
26    freeThird(third);
27
28    return true;
29}

This is very error prone. If you forget to free any of the previously allocated structures in one of the “still everything okay?” check blocks, you are leaking resources in the case of an error. Also, what happens if an exception is thrown after resources were allocated? You need a try / catch block to avoid resource leaks. But is one try / catch enough? How do you know whether you need to call freeSecond? Maybe the exception was thrown from initializeSecond?

This is a mess, let’s see if we can do better.

Scope Guards

Wouldn’t it be great if, in the above example, freeSecond would automatically be called when the lifetime of the second object ends? It’s not that easy since we can not modify MySecondResource, but we can create a “helper” object and tie the deallocation of second to the lifetime of that helper object. This helper object is called a “scope guard”, since it guards second with its (the helper’s) own scope.

A very basic scope guard class could look as simple as this:

1template<class Func>
2class ScopeGuard {
3public:
4  ScopeGuard(Func f) : storedFunc(f) {}
5  ~ScopeGuard() { storedFunc(); }
6
7private:
8  Func storedFunc;
9};

It does nothing but take a callable in its constructor, store it, and call that callable when it is destroyed. With this, we can rewrite the example from above as:

 1bool doSomething() {
 2  MyFirstResource *first = acquireFirst();
 3  if (!first)
 4    return false;
 5  auto firstGuard = ScopeGuard([&first]() { delete first; });
 6
 7  MySecondResource second;
 8  initializeSecond(&second);
 9  if (!isValid(&second))
10    return false;
11  auto secondGuard = ScopeGuard([&second]() { freeSecond(second); });
12
13  MyThirdResource third;
14  initializeThird(third);
15  auto thirdGuard = ScopeGuard([&third]() { freeThird(third); });
16  if (!third.isValid)
17    return false;
18
19  // … do something useful with all the allocated resources …
20
21  // No clean-up necessary. Also, no try/catch is necessary here, at least not
22  // for resource management reasons.
23  return true;
24}

This is a nice solution to the problem. However, it comes with some caveats which I’ll discuss in the next sections.

Moving? Copying?

If defined as above, the ScopeGuard can be copied and moved.1 This requires some attention since:

  • if we accidentally copy the ScopeGuard, it will be destroyed twice and the contained (copied) callable will be called twice and
  • if we accidentally move the ScopeGuard, we must make sure the moved-from instance does not try to call the moved-from callable.

To make copying possible one would probably need to implement some kind of reference counting (as std::shared_ptr does). I’m not going to go into that. Moving would be a lot easier, one would just need to prevent the callable in the moved-from scope guard to be called. However, I find that I actually never need it to be movable, so I just forbid everything:

1template<class Func>
2class ScopeGuard {
3public:
4  ScopeGuard(const ScopeGuard<Func> &) = delete;
5  ScopeGuard<Func> &operator=(const ScopeGuard<Func> &) = delete;
6  ScopeGuard(ScopeGuard<Func> &&) = delete;
7  ScopeGuard<Func> &operator=(ScopeGuard<Func> &&) = delete;
8  
9};

Exception Handling

One reason for using scope guards is cleaning up when exception handling unwinds the stack. That means that any exception thrown from the stored callable can become problematic. The question “what happens if an exception is thrown during exception handling” is tricky and the behavior is often misunderstood. The short version is: Everything is fine if the second exception is thrown while the first exception has already been caught and is being handled, i.e., while the catch block is running. If on the other hand the second exception is thrown before the first exception has been caught, the program terminates.

What happens before the first exception is caught, i.e., before the catch block runs? Stack unwinding. At which point is our storedFunc executed? During stack unwinding! Basically, if anything in storedFunc throws an exception, that’s the end of our program.

So it makes sense to make sure that storedFunc does not throw (or at least claims not to throw). In C++20, it’s good practice to constrain template parameters via concepts, so we can do something like this:

 1template<typename T>
 2concept CallableInScopeGuard = requires(const T& callable) {
 3  // The callable needs to be callable with no arguments, be noexcept, and
 4  // should return void since we're discarding the return value.
 5  { callable() } noexcept -> std::same_as<void>;
 6};
 7
 8template<CallableInScopeGuard Func>
 9class ScopeGuard {
10  // …
11private:
12  Func storedFunc;
13};

(You can try it here at Godbolt.)

This not only makes sure that only noexcept qualified callables are being used, but also nicely communicates that whatever is used as callable must be invocable with no arguments and should not return anything. Note that with this, you need to pass a lambda to the scope guard explicitly marked as noexcept, like this:

1char * foo = new char[100];
2auto guard = ScopeGuard([&foo]() noexcept { delete[] foo; });

Performance

A final consideration may be performance. How much slower is

1auto someGuard = ScopeGuard([&something]() {
2  delete something; });
3
4// Implicit on scope end:
5~someGuard();

compared to just:

1
2// at the end of the scope
3delete something;

The answer is: With a modern compiler and even at low optimization levels, there should be no difference since this is compiled to the same machine code (at least with GCC on -O1), as you can see here on Godbolt.

Implementations for Use in Practice

Personally, I basically use the ScopeGuard class as introduced above, with the two tweaks as explained, in practice. You can find the full copy-and-paste-ready source below:

Full Source Code for the ScopeGuard I use
 1#include <memory>
 2
 3template<typename T>
 4concept CallableInScopeGuard = requires(const T& callable) {
 5    { callable() } noexcept -> std::same_as<void>;
 6};
 7
 8template<CallableInScopeGuard Func>
 9class ScopeGuard {
10public:
11  ScopeGuard(Func f) : storedFunc(f) {}
12  ~ScopeGuard() { storedFunc(); }
13
14  ScopeGuard(const ScopeGuard<Func>&) = delete;
15  ScopeGuard<Func>& operator=(const ScopeGuard<Func>&) = delete;
16  ScopeGuard(ScopeGuard<Func>&&) = delete;
17  ScopeGuard<Func>& operator=(ScopeGuard<Func>&&) = delete;
18private:
19  Func storedFunc;
20};

(You can find it to play around with at Godbolt.)

However, I’m certainly not the first or only one to provide a possible implementation.

One of the First Scope Guards

One of the first implementations I could find is by Andrei Alexandrescu and Petru Marginean in their article Generic: Change the Way You Write Exception-Safe Code - Forever. The article is from the year 2000 and consequently, their technique could not use any of the nice additions that C++{11,14,17,20} brought us. Thus, while it’s an interesting read, I would not use it verbatim in a modern project today.

A Scope Guard in the Standard?

A more recent off-the-shelf variant (that has a real chance of making it into the standard, as far as I can tell) is currently contained in the Extensions for Library Fundamentals v3 and thus “standardized” in the std::experimental namespace. It seems to be available in GCC 13’s STL implementation, but not in Clang’s libc++ or in GCC 12, as seen here on Godbolt.2

This approach’s equivalent of our ScopeGuard is called std::experimental::scope_exit (see its documentation). It has one major additional feature compared to ‘our’ ScopeGuard: It can be active or inactive. A scope_exit created with a callable is active by default - if it becomes inactive (e.g. by calling release() on it), the stored callable will not be called once the scope_exit goes out of scope. This can be handy if one decides at some point that cleanup is not necessary anymore.

It also comes with two “variants”: std::experimental::scope_fail and std::experimental::scope_success. While std::experimental::scope_fail only calls its stored callable only when the scope is exited via an exception (during stack unwinding), std::experimental::scope_success does the opposite and only calls its stored callable if the scope is exited “normally”.

An interesting thing to note is that the suggested implementation in P0052R2, which is the proposal by Peter Sommerlad and Andrew L. Sandoval that std::experimental::scope_exit and its siblings is based on, would add more interesting functionality: The proposal contains a variant called unique_resource, which bundles the scope guard (i.e., the function to free the resource) with the resource itself. It also comes with a factory method make_unique_resource_checked, which allows to compare the created resource (e.g., a file descriptor) to a “failure value” (e.g. -1 for file descriptors). If the creation of the resource fails, the passed-in cleanup function will never be called. Quite handy, but I have not seen an actual implementation of that yet.

The No-Nonsense Just-Get-It-Done Solution

A solution I have seen used a couple of times, and which has the charm of needing nothing but C++11, is (ab)using an empty std::unique_ptr with a custom deleter like this:

1auto myDeleter = [](void*) { /* do whatever you need to do */ };
2std::unique_ptr<void, decltype(myDeleter)> guard(1, myDeleter);

This feels a bit clumsy: you cannot specify the lambda directly in the argument, you need to use decltype, and what’s that weird 1 doing there? I’ve seen some solutions use class template argument deduction (CTAD) to improve it like this:

1/* WARNING! THIS IS NOT PORTABLE! */
2template<class T>
3using ScopeGuard = std::unique_ptr<void, T>;
4
5void someFunction()
6{
7  ScopeGuard guard((void*)1, [](void*) { /* … whatever … */ });
8}

This looks like a semi-decent way of using the scope guard - there’s still that pesky 1 which has no function, but at least we don’t need to specify the type manually anymore. However, it is not portable! This only works if std::unique_ptr is defined as a class (and not a struct), because CTAD is only defined for classes, not structs. And oh look what Clang’s libc++ does

One could of course get around this by defining a class template which wraps std::unique_ptr or something similar, but I think at this point one can also just use one’s own ScopeGuard class altogether.

Conclusion

Whatever method one chooses, I think adding scope guards to one’s regular C++ vocabulary is an important step towards having less resource leaks. They are certainly not a silver bullet which solves every resource management problem, but where they fit, they provide a nice “fire and forget” approach to cleaning up your resources.


  1. Assuming that you did not use a type as Func which forbids copying and/or moving. This happens e.g. if your Func is a lambda type and your lambda captured a type which cannot be copied. ↩︎

  2. Though as of 2024-01-12, the libstdc++ implementation status page does not list this feature as implemented, so YMMV. ↩︎

Comments

You can use your Mastodon account to reply to this post.

Reply to tinloaf's post

With an account on the Fediverse or Mastodon, you can respond to this post. Since Mastodon is decentralized, you can use your existing account hosted by another Mastodon server or compatible platform if you don't have an account on this one.

Copy and paste this URL into the search field of your favourite Fediverse app or the web interface of your Mastodon server.