Boost logo

Boost Users :

From: Martin Trappel (0xCDCDCDCD_at_[hidden])
Date: 2008-07-25 11:15:51


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 <boost/noncopyable.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/scoped_ptr.hpp>

//////////////////////////////////////////////////////////////////////
class dynamic_singleton : private boost::noncopyable
{
public:
        typedef boost::shared_ptr<dynamic_singleton> 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 <boost/weak_ptr.hpp>
#include <boost/thread/recursive_mutex.hpp>
#include <boost/thread.hpp>

//////////////////////////////////////////////////////////////////////
#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<dynamic_singleton> 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 <boost/thread.hpp>

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]


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net