patterncppMinor
C++ shared_singleton
Viewed 0 times
shared_singletonstackoverflowprogramming
Problem
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
Here's a function that returns an instance of an object wrapped in a
For example, if you call the function twice, you have two
One disadvantage of this code is that the class is instantiated from its default constructor.
The requirements, more clearly stated:
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
#include
// Deleter function given to the shared_ptr returned by get_shared_singleton.
template
void release_shared(std::mutex& m, int& n, T*& p)
{
std::lock_guard lg(m);
if(!--n)
{
delete p;
p = 0;
}
}
template
std::shared_ptr get_shared_singleton()
{
static std::mutex m;
std::lock_guard lg(m);
static int n = 0; // Ref count.
static T* p = 0;
if(!p) p = new T();
++n;
return std::shared_ptr(p, std::bind(release_shared, 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()
Solution
You can use a static
So, the first caller will find a default-initialized weak_ptr counts as expired, and construct a new
While the first caller is active (and keeping its new
A subsequent caller will find the
weak_ptr in the function to cache the shared instance without extending its lifetime:template
std::shared_ptr get_shared_instance()
{
static std::mutex m;
static std::weak_ptr cache;
std::lock_guard lg(m);
std::shared_ptr shared = cache.lock();
if (cache.expired()) {
shared.reset(new T);
cache = shared;
}
return shared;
}So, the first caller will find a default-initialized weak_ptr counts as expired, and construct a new
T.While the first caller is active (and keeping its new
T alive), concurrent callers will get the same object, cached in the weak_ptr. When the last concurrent user is done, the object will be destroyed.A subsequent caller will find the
weak_ptr expired (same as before the first call), and create a new T ... etc. etc.Code Snippets
template<typename T>
std::shared_ptr<T> get_shared_instance()
{
static std::mutex m;
static std::weak_ptr<T> cache;
std::lock_guard<std::mutex> lg(m);
std::shared_ptr<T> shared = cache.lock();
if (cache.expired()) {
shared.reset(new T);
cache = shared;
}
return shared;
}Context
StackExchange Code Review Q#14343, answer score: 7
Revisions (0)
No revisions yet.