I actually feel bad posting "yet another singleton"... I wrote the following one many years ago and had recently found another application for it. We had many threads, each running the same function that requires the use of a boost::asio::io_service instance. It was best that all threads shared the same io_service instance and also that that instance be destroyed before main returned. That last requirement (whose validity I now question, but, whatever..) meant no global or static object.
Here's a function that returns an instance of an object wrapped in a shared_ptr. What's special about it though is that we keep an additional reference count so the singleton is eagerly destructed when the last shared_ptr is gone.
For example, if you call the function twice, you have two shared_ptrs, each with a ref-count of 1, but our singleton's ref-count is 2. On the other hand, if you call the function only once and copy the returned shared_ptr, the shared_ptr has a ref-count of 2 but our singleton's ref-count is at one. Either way, the singleton instance gets destructed when the last shared_ptr is destructed.
One disadvantage of this code is that the class is instantiated from its default constructor.
#include <memory>
#include <mutex>
// Deleter function given to the shared_ptr returned by get_shared_singleton.
template<typename T>
void release_shared(std::mutex& m, int& n, T*& p)
{
std::lock_guard<std::mutex> lg(m);
if(!--n)
{
delete p;
p = 0;
}
}
template<typename T>
std::shared_ptr<T> get_shared_singleton()
{
static std::mutex m;
std::lock_guard<std::mutex> lg(m);
static int n = 0; // Ref count.
static T* p = 0;
if(!p) p = new T();
++n;
return std::shared_ptr<T>(p, std::bind(release_shared<T>, std::ref(m), std::ref(n), std::ref(p)));
}
The requirements, more clearly stated:
- You wrote a function
foo()that requires an instance of object X to perform its duty. foo()can be invoked concurrently from different threads.- Concurrent
foo()s must share the same instance of object X. - If no
foo()is running, there must be no instance of object X alive. foo()is in a library, you have no control ofmain()or the lifetime of threads that invokefoo().
What is the best code to provide a shared instance of an object that is both lazily-constructed and eagerly-destructed?