Hi all.
I was faced with the requirement for a singleton class where the object
has to be constructed dynamically and where I wanted to assure that the
object is destructed automatically when no longer needed.
A solution using shared_ptr and weak_ptr immediately came to mind and
after a short google I found
http://lists.boost.org/boost-users/2002/10/2014.php.
However, if you have to assure that construction and destruction never
overlap if you have multiple (sequential) initializations/destructions
it gets a bit more complicated :)
Well. Here follows what I have come up with and it seems to work pretty
well.
Any comments welcome.
br,
Martin
Singleton using boost weak_ptr and shared_ptr
---------------------------------------------
Requirement: Singleton that is constructed on first use (not on process
start) and destroyed after the last "client-code" has finished with it.
Note: It is therefore possible that more that one Singleton instances
exist within a process's lifetime, BUT there must only be at most one
Object active at any given time (Construction must not run before
destruction has finished.
Starting point: http://lists.boost.org/boost-users/2002/10/2014.php
Problem of the simple solution: No protection against multiple
initialization and against simultaneous deletion and construction.
Solution: The construction and destruction of the singleton instance(s)
has to be protected additionally.
[CODE:dynamic_singleton.h]
#pragma once
//////////////////////////////////////////////////////////////////////
#include
#include
#include
//////////////////////////////////////////////////////////////////////
class dynamic_singleton : private boost::noncopyable
{
public:
typedef boost::shared_ptr shared_t;
static shared_t get_instance();
// Interface:
void example(int cookie);
// ...
private:
dynamic_singleton();
virtual ~dynamic_singleton();
struct impl;
typedef boost::scoped_ptr<impl> impl_t;
impl_t pimpl;
struct deleter;
friend struct deleter;
};
[/CODE]
[CODE:dynamic_singleton.cpp]
#include "dynamic_singleton.h"
#include
#include
#include
//////////////////////////////////////////////////////////////////////
#define MESG(msg) \
printf("%s\n", msg); \
/**/
//////////////////////////////////////////////////////////////////////
class atomic_bool : protected boost::noncopyable
{
public:
atomic_bool() : b_(false) {}
explicit atomic_bool(bool b) : b_(b) {}
inline operator bool() const {
boost::recursive_mutex::scoped_lock lock(sync_);
return b_;
}
inline bool operator=(bool b) {
boost::recursive_mutex::scoped_lock lock(sync_);
b_ = b;
return b_;
}
private:
bool b_;
mutable boost::recursive_mutex sync_;
};
//////////////////////////////////////////////////////////////////////
struct dynamic_singleton::impl : private boost::noncopyable
{
impl() {}
~impl() {}
static void start_construction()
{
boost::xtime spin_time;
spin_time.sec = 1;
while(the_object_exists) {
boost::thread::sleep(spin_time);
}
}
static void finish_construction()
{
assert(!the_object_exists);
the_object_exists = true;
}
static void finish_destruction()
{
assert(the_object_exists);
the_object_exists = false;
}
typedef boost::weak_ptr internal_shared_t;
static internal_shared_t the_object;
static boost::recursive_mutex sync_init;
static atomic_bool the_object_exists;
};
//////////////////////////////////////////////////////////////////////
dynamic_singleton::impl::internal_shared_t
dynamic_singleton::impl::the_object;
boost::recursive_mutex
dynamic_singleton::impl::sync_init;
atomic_bool
dynamic_singleton::impl::the_object_exists;
//////////////////////////////////////////////////////////////////////
struct dynamic_singleton::deleter
{
void operator() (dynamic_singleton* p)
{
assert(p);
delete p;
impl::finish_destruction();
}
};
//////////////////////////////////////////////////////////////////////
dynamic_singleton::shared_t dynamic_singleton::get_instance()
{
// Syncronise Initialization:
boost::recursive_mutex::scoped_lock lock(impl::sync_init);
MESG(__FUNCTION__);
// Acquire singleton pointer:
shared_t object_ptr = impl::the_object.lock();
if(!object_ptr.use_count()) {
impl::start_construction();
object_ptr.reset(new dynamic_singleton(), dynamic_singleton::deleter());
impl::the_object = object_ptr;
impl::finish_construction();
}
return object_ptr;
}
//////////////////////////////////////////////////////////////////////
dynamic_singleton::dynamic_singleton()
{
pimpl.reset(new impl());
MESG(__FUNCTION__);
// For example open a unique system or process global resource
printf(" >> Singleton opens the global resouce.\n");
}
//////////////////////////////////////////////////////////////////////
dynamic_singleton::~dynamic_singleton()
{
MESG(__FUNCTION__);
// For example close a unique system or process global resource
printf(" << Singleton closes the global resouce.\n");
}
//////////////////////////////////////////////////////////////////////
void dynamic_singleton::example(int cookie)
{
printf("%s(%d)\n", __FUNCTION__, cookie);
}
[/CODE]
[CODE:main.cpp]
#include "dynamic_singleton.h"
#include <iostream>
#include
struct singleton_user
{
explicit singleton_user(int num)
: num_(num)
{ }
void operator()()
{
using namespace std;
printf("%d uses singleton ...\n", num_);
dynamic_singleton::shared_t s = dynamic_singleton::get_instance();
s->example(num_);
}
int num_;
};
int main(int argc, char* argv[])
{
boost::thread t1( singleton_user(1) );
boost::thread t2( singleton_user(2) );
boost::thread t3( singleton_user(3) );
boost::thread t4( singleton_user(4) );
boost::thread t5( singleton_user(5) );
t1.join();
t2.join();
t3.join();
t4.join();
t5.join();
return 0;
}
[/CODE]