Singletons

Singletons are evil

I will start off with a word of caution that singletons should be avoided. Singleton is the object-oriented equivalent of a global variable – a piece of state which anyone can grab and modify, which makes it hard to reason locally about code and generates ugly dependency graphs. That being said, in a large system there are situations where there is a legitimate need for global state or some component that exposes a service to other components.

Implementation

This CppCon lightning talk by Arno Lepisk covers some implementation alternatives, suggesting that in most cases using a namespace and flat functions is the simplest and best way to implement a singleton:

namespace Foo {

void DoFoo();

}

instead of:

class Foo
{
public:
    void DoFoo();
};

Foo& UseFoo();

I completely agree with this, with the caveat that sometimes we do want to inject dependencies and work against an interface instead of the actual implementation, in which case the above approach might be insufficient. Note that unless dependency injection is needed, the default should be a namespace and flat functions.

Dependency Injection

Given an interface, an implementation, and a function to retrieve the singleton like the following:

struct IFoo
{
    virtual void DoFoo() = 0;

    virtual ~IFoo() = default;
};

struct Foo : IFoo
{
    void DoFoo() override { /*...*/ };
};

IFoo& UseFoo();

a common mistake I see is components directly calling the function like this:

class Component
{
public:
    void Bar()
    {
        UseFoo().DoFoo();
    }
};

If the goal is to inject the dependency, for example have tests run against MockFoo, this approach is not ideal. Code explicitly calls UseFoo so the only way to switch implementation is to modify UseFoo and provide some internal mechanism to change its return value. A better approach is to have the client simply require an interface and provide that at construction time:

class Component
{
public:
    Component(IFoo& foo = UseFoo())
        : m_foo(foo)
    {
    }

    void Bar()
    {
        m_foo.DoFoo();
    }

private:
    IFoo& m_foo;
};

Note that in the above example we can create Component with a MockFoo implementation of IFoo or some other implementation, which is a better decoupling than directly calling UseFoo inside the member functions of Component.

Magic Statics

By definition, a singleton should represent a unique object, so our UseFoo needs to return the same reference on each call. Ensuring that concurrent calls from multiple threads don’t cause problems was non-trivial until C++11, which introduced “magic statics”. Quote from the C++ standard section 6.7:

… such a variable [with static storage] is initialized the first time control passes through its declaration; such a variable is considered initialized upon the completion of its initialization. If the initialization exits by throwing an exception, the initialization is not complete, so it will be tried again the next time control enters the declaration. If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization.

The standard now guarantees that a static would only ever be created once, and the simple way to implement a singleton (for example according to Scott Meyer’s Effective Modern C++) is:

IFoo& UseFoo()
{
    static Foo instance;
    return instance;
}

Or the heap-allocated version:

IFoo& UseFoo()
{
    static auto instance = make_unique<Foo>();
    return *instance;
}

Deterministic shutdown

A more interesting problem which the above implementation doesn’t cover is deterministic shutdown. A local static, once created, will be live for the duration of the program, which might not always be desirable. Building on the previous implementation, here is a singleton which we can also shutdown on demand:

template <typename T>
class Singleton
{
public:
    static T& Use()
    {
        static auto& instance = []() -> T& {
            m_instance = make_unique<T>();
            return *m_instance;
        }();

        return instance;
    }

    static void Free() noexcept
    {
        m_instance.reset();
    }

private:
    static unique_ptr<T> m_instance;
};

template <typename T> unique_ptr<T> Singleton<T>::m_instance;

/* ... */

IFoo& UseFoo()
{
    return Singleton<Foo>::Use();
}

void FreeFoo()
{
    Singleton<Foo>::Free();
}

Using this implementation, we can deterministically free the singleton on demand via the Free function as opposed to having to wait for the program to get unloaded, which can be useful in certain cases.

Atomics

Magic statics provide an easy way to guarantee we end up with a single object, but the code generated to support this is non-trivial. Disassembly of the UseFoo above as compiled by Clang 4.0.0 with -O3 flag:

UseFoo():                             # @UseFoo()
        push    rbx
        mov     al, byte ptr [rip + guard variable for Singleton<Foo>::Use()::instance]
        test    al, al
        jne     .LBB0_6
        mov     edi, guard variable for Singleton<Foo>::Use()::instance
        call    __cxa_guard_acquire
        test    eax, eax
        je      .LBB0_6
        mov     edi, 8
        call    operator new(unsigned long)
        mov     qword ptr [rax], vtable for Foo+16
        mov     rdi, qword ptr [rip + Singleton<Foo>::m_instance]
        mov     qword ptr [rip + Singleton<Foo>::m_instance], rax
        test    rdi, rdi
        je      .LBB0_5
        mov     rax, qword ptr [rdi]
        call    qword ptr [rax + 8]
        mov     rax, qword ptr [rip + Singleton<Foo>::m_instance]
.LBB0_5:
        mov     qword ptr [rip + Singleton<Foo>::Use()::instance], rax
        mov     edi, guard variable for Singleton<Foo>::Use()::instance
        call    __cxa_guard_release
.LBB0_6:
        mov     rax, qword ptr [rip + Singleton<Foo>::Use()::instance]
        pop     rbx
        ret
        mov     rbx, rax
        mov     edi, guard variable for Singleton<Foo>::Use()::instance
        call    __cxa_guard_abort
        mov     rdi, rbx
        call    _Unwind_Resume

A lot of the generated code is the compiler implementing the guarantee that on concurrent calls, a single intialization is performed. The Singleton functions are inlined here since we are compiling with -O3. We can provide a much more efficient implementation using an atomic pointer on architectures where atomics are lock-free and we are not worried about redundantly calling the constructor in the rare cases of concurrent access that requires initialization:

template <typename T>
class Singleton
{
public:
    static T& Use()
    {
        auto instance = m_instance.load();
        if (instance != nullptr)
            return *instance;

        instance = new T();
        T* temp = nullptr;
        if (m_instance.compare_exchange_strong(temp, instance))
            return *instance;

        delete instance;
        return *m_instance.load();
    }

    static void Free() noexcept
    {
        auto instance = m_instance.exchange(nullptr);
        delete instance;
    }

private:
    static atomic<T*> m_instance;
};

template <typename T> atomic<T*> Singleton<T>::m_instance;

/* ... */

IFoo& UseFoo()
{
    return Singleton<Foo>::Use();
}

void FreeFoo()
{
    Singleton<Foo>::Free();
}

The disassembly of the above UseFoo (built with the same compiler and -O3 flag) is:

UseFoo():                             # @UseFoo()
        push    rax
        mov     rcx, qword ptr [rip + Singleton<Foo>::m_instance]
        test    rcx, rcx
        jne     .LBB0_3
        mov     edi, 8
        call    operator new(unsigned long)
        mov     rcx, rax
        mov     qword ptr [rcx], vtable for Foo+16
        xor     eax, eax
        lock
        cmpxchg qword ptr [rip + Singleton<Foo>::m_instance], rcx
        je      .LBB0_3
        mov     rax, qword ptr [rcx]
        mov     rdi, rcx
        call    qword ptr [rax + 8]
        mov     rcx, qword ptr [rip + Singleton<Foo>::m_instance]
.LBB0_3:
        mov     rax, rcx
        pop     rcx
        ret

This code might new the object multiple times, but is guaranteed to always return the same instance and retrieving it is more efficient than relying on statics, since it uses a compare-exchange to guarantee uniqueness. Many thanks to my colleague Vladimir Morozov who suggested this approach.

Tri-state

We now have an efficient way to create and shutdown a singleton. If shutdown, a subsequent call to Use would re-create the object. One optional feature we can add is to enforce that once shutdown, a singleton should never be accessed again. So instead of the two-state not initialized and live, we can use three states: not initialized, live, freed and terminate if an access is attempted in the freed state:

const uintptr_t FreedSingleton = 0xDEADBEEF;

template <typename T>
class Singleton
{
public:
    static T& Use()
    {
        auto instance = Get();

        if (instance == reinterpret_cast<T*>(FreedSingleton))
            terminate();

        return *instance;
    }

    static void Free() noexcept
    {
        auto instance = m_instance.exchange(reinterpret_cast<T*>(FreedSingleton));
        delete instance;
    }

private:
    static T* Get()
    {
        auto instance = m_instance.load();
        if (instance != nullptr)
            return instance;

        instance = new T();
        T* temp = nullptr;
        if (m_instance.compare_exchange_strong(temp, instance))
            return instance;

        delete instance;
        return m_instance.load();
    }

    static atomic<T*> m_instance;
};

template <typename T> atomic<T*> Singleton<T>::m_instance;

We now have an efficient generic singleton which we can shutdown on-demand and ensure clients never call after shutdown.

Summary

  • Try not to use singletons, singletons are evil
  • In most cases, a namespace and flat functions are enough, no need to over-complicate things
  • If dependency injection is required, make sure dependency is properly injected during construction as opposed to member functions directly calling the singleton-retrieving function
  • Magic statics provide an easy way to implement singletons
  • Atomics are more efficient than magic statics when they are lock-free and we aren’t worried about potentially having multiple constructor calls in race cases
  • If needed, singletons can be extended with a shutdown mechanism
  • Three-state singletons can terminate on use-after-shutdown