CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Page 2 of 2 FirstFirst 12
Results 16 to 23 of 23
  1. #16
    Join Date
    Dec 2005
    Posts
    445

    Re: Singleton in multithreaded

    Hi guys,

    I've been thinking about the double checked locking pattern in the last few days, and I couldn't understand why not replace the singleton pointer object check with a static variable.

    Classic DCLP:

    Code:
    Singleton* Singleton::instance() {
    if (pInstance == 0) { 
        Lock lock;
        if (pInstance == 0) 
       {
            pInstance = new Singleton;
       }
    }
    Replace it with:

    Code:
    Singleton* Singleton::instance() {
    
    static bool bInitialized = false;
    
    if (bInitialized == 0) { 
        Lock lock;
        if (bInitialized == 0) 
       {
            try
            {
                pInstance = new Singleton;
                bInitialized  = true;
            }
            catch ( const std::exception& e )
           {
                ...
           }
       }
    }
    
    return pInstance;
    }
    I'm sure there is something wrong with it otherwise people would use it already.
    Can someone see why it may fail??

    Many thanks

  2. #17
    Join Date
    Nov 2003
    Posts
    1,902

    Re: Singleton in multithreaded

    That's just DCL. It's broken due to all the reasons in the PDF you originally linked.

    gg

  3. #18
    Join Date
    Nov 2003
    Posts
    1,902

    Re: Singleton in multithreaded

    I wanted to provide some solutions for on-demand, efficient singleton construction and access for Windows and Posix in a multi-threaded environment. If you don't have a strict requirement for on-demand construction (meaning the singleton is only created if need be) then the best solution is to simply create the singleton prior to thread creation. Another "rule-of-thumb" is to never access a singleton within a global object constructor or destructor.

    As a reusable pattern, I prefer the concept of a "singleton constructor". The first example uses user-supplied memory barriers (membars) to ensure proper ordering of memory accesses. Posix doesn't have explicit membars, so this it more of a Windows approach - but could be ported to other systems:
    Code:
    /*------------------------------------------------------------------------------
    SingletonConstruct - 
        Template class for constructing a singleton in an efficient and thread safe 
        manner. This object must be a static member of the class that you want to be
        a singleton - this ensures m_p will be zero'd out during global construction
        (which is single threaded)
    NOTE: Do not use your singleton in a global object constructor or destructor.
    ------------------------------------------------------------------------------*/
    template <typename T>
    class SingletonConstruct : public no_copy
    {
        T *m_p;
        CriticalSection m_cs;
    
    public:
        SingletonConstruct() : m_p(0) {}
        ~SingletonConstruct() {Unload();}
    
        // NOTE: caller must guarantee that no references are being held
        void Unload()
        {
            delete m_p;
        }//Unload
    
        T& Instance()
        {
            // atomic read with acquire membar semantics
            T *p = InterlockedReadAcquire(&m_p);
            if (!p)
            {
                CriticalSection::scoped_lock lock(m_cs);
                p = m_p; 
                if (!p)
                {
                    p = new T;
                    // atomic write with release membar semantics
                    InterlockedWriteRelease(&m_p, p);
                }//if
            }//if
    
            return *p;
        }//Instance
    };//SingletonConstruct
    
    // Example usage SingletonConstruct
    class foo : public no_copy
    {
        static SingletonConstruct<foo> m_foo_sc;
        friend class SingletonConstruct<foo>;
    
        foo() {}
        ~foo() {}
    
    public:
        static foo& Instance() {return m_foo_sc.Instance();}
        void display() {std::cout << "I'm the foo instance!" << std::endl;}
    };//foo
    
    // m_foo_sc is global which ensures it's construction prior to threading
    SingletonConstruct<foo> foo::m_foo_sc;
    The next example uses thread local storage (TLS) to avoid synchronization when taking the "fast path" of returning the already created singleton. Since TLS memory is unique per-thread, a Posix compliant implementation is possible.
    Code:
    /*------------------------------------------------------------------------------
    tlsSingletonConstruct -
        Demonstrates singleton construction in an efficient and thread safe manner
        using TLS. It is assumed that TLS memory access is more efficient than 
        acquiring a lock. A Posix compliant implementation is possible using this 
        model.
    ------------------------------------------------------------------------------*/
    template <typename T>
    class tlsSingletonConstruct : public no_copy
    {
        T *m_p;
        CriticalSection m_cs;
    
    public:
        tlsSingletonConstruct() : m_p(0) {}
        ~tlsSingletonConstruct() {Unload();}
    
        // NOTE - caller must guarantee that no references are being held
        void Unload()
        {
            delete m_p;
        }//Unload
    
        T& Instance()
        {
            // "__thread" represents TLS which is guaranteed to be initialized 
            // "early". An actual implementation may use compiler extensions to 
            // declare the static TLS here, or acquire TLS storage via API's within 
            // tlsSingletonConstruct's constructor.
            static __thread bool g_created = false;
    
            if (g_created)
                return *m_p;
    
            CriticalSection::scoped_lock lock(m_cs);
            if (!m_p)
                m_p = new T;
            g_created = true;
    
            return *m_p;
        }//Instance
    };//tlsSingletonConstruct
    Last but not least for Posix is to simply use pthread_once(). The downside to this approach is that it's difficult to create a reusable pattern. The reason is because pthread_once() takes a function pointer with external C linkage, taking no arguments. So you can't (portably) use a static class member, and there's no argument to pass a 'this' pointer. But it's easy enough to implement by hand:
    Code:
    extern "C" void bar_create();
    
    class bar
    {
        static bar *g_singleton;
        static pthread_once_t g_once;
        static void create() {g_singleton = new bar;}
        friend void bar_create();
    
        bar() {}
        ~bar() {}
    public:
        static bar& Instance() 
        {
            pthread_once(&g_once, &bar_create);
            return *g_singleton;
        }//Instance
    
        void display() {std::cout << "I'm the bar instance!" << std::endl;}
    };//bar
    
    bar* bar::g_singleton = 0;
    pthread_once_t bar::g_once = PTHREAD_ONCE_INIT;
    
    void bar_create() {bar::create();}
    I've attached a complete implementation of the above code for Windows. Let me know of any questions/concerns.

    gg
    Attached Files Attached Files

  4. #19
    Join Date
    Dec 2005
    Posts
    445

    Re: Singleton in multithreaded

    WOW! thats look amazing !

    Thanks very much!

  5. #20
    Join Date
    Mar 2002
    Location
    St. Petersburg, Florida, USA
    Posts
    12,125

    Re: Singleton in multithreaded

    CodePlug.....One suggestion....

    Convert that post into an Article, and post it for publication
    TheCPUWizard is a registered trademark, all rights reserved. (If this post was helpful, please RATE it!)
    2008, 2009,2010
    In theory, there is no difference between theory and practice; in practice there is.

    * Join the fight, refuse to respond to posts that contain code outside of [code] ... [/code] tags. See here for instructions
    * How NOT to post a question here
    * Of course you read this carefully before you posted
    * Need homework help? Read this first

  6. #21
    Join Date
    Oct 2009
    Posts
    2

    Re: Singleton in multithreaded

    Quote Originally Posted by Codeplug View Post
    I wanted to provide some solutions for on-demand, efficient singleton construction and access for Windows and Posix in a multi-threaded environment. If you don't have a strict requirement for on-demand construction (meaning the singleton is only created if need be) then the best solution is to simply create the singleton prior to thread creation. Another "rule-of-thumb" is to never access a singleton within a global object constructor or destructor.

    As a reusable pattern, I prefer the concept of a "singleton constructor". The first example uses user-supplied memory barriers (membars) to ensure proper ordering of memory accesses. Posix doesn't have explicit membars, so this it more of a Windows approach - but could be ported to other systems:
    Code:
    /*------------------------------------------------------------------------------
    SingletonConstruct - 
        Template class for constructing a singleton in an efficient and thread safe 
        manner. This object must be a static member of the class that you want to be
        a singleton - this ensures m_p will be zero'd out during global construction
        (which is single threaded)
    NOTE: Do not use your singleton in a global object constructor or destructor.
    ------------------------------------------------------------------------------*/
    template <typename T>
    class SingletonConstruct : public no_copy
    {
        T *m_p;
        CriticalSection m_cs;
    
    public:
        SingletonConstruct() : m_p(0) {}
        ~SingletonConstruct() {Unload();}
    
        // NOTE: caller must guarantee that no references are being held
        void Unload()
        {
            delete m_p;
        }//Unload
    
        T& Instance()
        {
            // atomic read with acquire membar semantics
            T *p = InterlockedReadAcquire(&m_p);
            if (!p)
            {
                CriticalSection::scoped_lock lock(m_cs);
                p = m_p; 
                if (!p)
                {
                    p = new T;
                    // atomic write with release membar semantics
                    InterlockedWriteRelease(&m_p, p);
                }//if
            }//if
    
            return *p;
        }//Instance
    };//SingletonConstruct
    
    // Example usage SingletonConstruct
    class foo : public no_copy
    {
        static SingletonConstruct<foo> m_foo_sc;
        friend class SingletonConstruct<foo>;
    
        foo() {}
        ~foo() {}
    
    public:
        static foo& Instance() {return m_foo_sc.Instance();}
        void display() {std::cout << "I'm the foo instance!" << std::endl;}
    };//foo
    
    // m_foo_sc is global which ensures it's construction prior to threading
    SingletonConstruct<foo> foo::m_foo_sc;
    The next example uses thread local storage (TLS) to avoid synchronization when taking the "fast path" of returning the already created singleton. Since TLS memory is unique per-thread, a Posix compliant implementation is possible.
    Code:
    /*------------------------------------------------------------------------------
    tlsSingletonConstruct -
        Demonstrates singleton construction in an efficient and thread safe manner
        using TLS. It is assumed that TLS memory access is more efficient than 
        acquiring a lock. A Posix compliant implementation is possible using this 
        model.
    ------------------------------------------------------------------------------*/
    template <typename T>
    class tlsSingletonConstruct : public no_copy
    {
        T *m_p;
        CriticalSection m_cs;
    
    public:
        tlsSingletonConstruct() : m_p(0) {}
        ~tlsSingletonConstruct() {Unload();}
    
        // NOTE - caller must guarantee that no references are being held
        void Unload()
        {
            delete m_p;
        }//Unload
    
        T& Instance()
        {
            // "__thread" represents TLS which is guaranteed to be initialized 
            // "early". An actual implementation may use compiler extensions to 
            // declare the static TLS here, or acquire TLS storage via API's within 
            // tlsSingletonConstruct's constructor.
            static __thread bool g_created = false;
    
            if (g_created)
                return *m_p;
    
            CriticalSection::scoped_lock lock(m_cs);
            if (!m_p)
                m_p = new T;
            g_created = true;
    
            return *m_p;
        }//Instance
    };//tlsSingletonConstruct
    Last but not least for Posix is to simply use pthread_once(). The downside to this approach is that it's difficult to create a reusable pattern. The reason is because pthread_once() takes a function pointer with external C linkage, taking no arguments. So you can't (portably) use a static class member, and there's no argument to pass a 'this' pointer. But it's easy enough to implement by hand:
    Code:
    extern "C" void bar_create();
    
    class bar
    {
        static bar *g_singleton;
        static pthread_once_t g_once;
        static void create() {g_singleton = new bar;}
        friend void bar_create();
    
        bar() {}
        ~bar() {}
    public:
        static bar& Instance() 
        {
            pthread_once(&g_once, &bar_create);
            return *g_singleton;
        }//Instance
    
        void display() {std::cout << "I'm the bar instance!" << std::endl;}
    };//bar
    
    bar* bar::g_singleton = 0;
    pthread_once_t bar::g_once = PTHREAD_ONCE_INIT;
    
    void bar_create() {bar::create();}
    I've attached a complete implementation of the above code for Windows. Let me know of any questions/concerns.

    gg
    Hi gg,

    Let's take it to another level...
    I really like your article, it's very interesting. However, your Singleton pattern is NOT thread safe. I can break it very easily. I bring out some issues first before I go into the "not safe" part.

    Let us first see what you have done in your article.

    You are using the DCL pattern (Double-Checked locking Optimization) of D. Schmidt. You modified it with thanks to Scott Meyers and Andrei Alexandrescu a link to those articles can be very interesting.

    According to Andrew Koeing "Ruminations on C++" and Herb Sutter C++ "Coding Standards" the destructor should be made virtual. So, you should make your no_copy destructor virtual.

    You explain the creation of the Singleton and not the destruction. Either you accept the memory leak or the weak construction.

    You have locked the Instance method where the DCLP technique is used. However, the unload method is not guarded. You can now unload/delete the one and only instance while creating the SingletonObject. This means the code your wrote is incorrect and NOT thread safe!

    T *p = InterlockedReadAcquire(&m_p);
    if (!p)
    {
    CriticalSection::scoped_lock lock(m_cs);
    p = m_p;

    Please set the pointer back to NULL/0 when you delete it, so the statement above can be executed correctly without using a pointer which point to a deleted Singleton object. This means, if I put the creating and the destructing in different threads, your construction will fail and is not thread safe. So, what can we conclude? We can conclude that your example is still not thread safe.

    The Display methods can be declared as const method.

    Lets talk about the friend class you're using. It's like slapping the Object Oriented concept with both hands. I can still Unload the one and only instance. So, you shouldn't declare the entire class as friend but only the methods using.

    P.S. for the next time you post a message and criticize people on details make sure your own creation is good enough.

    HE

  7. #22
    Join Date
    Nov 2003
    Posts
    1,902

    Re: Singleton in multithreaded

    To existing subscribers of this thread, our new contributor most likely found their way here from: http://www.codeproject.com/KB/thread...edLocking.aspx
    I am "Member 4490530" and "Codeplug-gg" in the article discussion area.

    >> However, the unload method is not guarded.
    Guarding it is pointless if references to the singleton are still being used elsewhere. The semantics of the Unload() method are clearly commented - you must guarantee that no references are being held. That includes attempts to get a reference. Thread-safety between Unload() and Instance() was never claimed. Stating that they aren't thread-safe is just stating the obvious...

    >> Please set the pointer back to NULL/0 when you delete it
    Good catch. Two calls to Unload() would be bad. However, the ability to call Instance() after Unload() was never intended. Adding some assert()'s to catch this type of misuse wouldn't hurt.

    Multiple threads calling Unload() and Instance() doesn't make a lot of sense to me, but despite that, I've added support for it in the latest version of my DCL object, "Once", and "SingletonConstruct":
    Code:
    #include <stdexcept>
    #include <windows.h>
    
    /*------------------------------------------------------------------------------
    no_copy - Disallow copy semantics
    ------------------------------------------------------------------------------*/
    class no_copy
    {
    protected:
        no_copy() {}
        ~no_copy() {}
    private:
        no_copy(const no_copy&);
        const no_copy& operator=(const no_copy&);
    };//no_copy
    
    /*------------------------------------------------------------------------------
    scoped_lock - 
        Generalized template for Acquire()/Release() operations in the constructor 
        and destructor respectively
    ------------------------------------------------------------------------------*/
    template<class sync_t>
    class scoped_lock : public no_copy
    {
        sync_t &m_sync;
    public:
        explicit scoped_lock(sync_t &s) : m_sync(s) {m_sync.Acquire();}
        ~scoped_lock() {m_sync.Release();}
    };//scoped_lock
    
    /*------------------------------------------------------------------------------
    CriticalSection - C++ version of a Win32 CS
    ------------------------------------------------------------------------------*/
    class CriticalSection : public no_copy
    {
        CRITICAL_SECTION m_cs;
    
    public:
        typedef scoped_lock<CriticalSection> scoped_lock;
    
        CriticalSection(bool bUseSpinCount = true, DWORD SpinCount = 4000) 
        {
            if (bUseSpinCount && 
                !InitializeCriticalSectionAndSpinCount(&m_cs, SpinCount))
                throw std::runtime_error("InitCSAndSpinCount failure");
            else
                InitializeCriticalSection(&m_cs);
        }//constructor
    
        ~CriticalSection() {::DeleteCriticalSection(&m_cs);}
        void Acquire() {::EnterCriticalSection(&m_cs);}
        void Release() {::LeaveCriticalSection(&m_cs);}
    };//CriticalSection
    
    //------------------------------------------------------------------------------
    // Interlocked*Acquire/Release() functions only for Itanium Processor Family,
    // (IPF), which we map to non-IPF Interlocked functions for other platforms
    #if !defined(_M_IA64) && !defined(__IA64__)
    #   define InterlockedExchangeAddAcquire  InterlockedExchangeAdd
    #   define InterlockedExchangeAddRelease  InterlockedExchangeAdd
    #endif
    
    /*------------------------------------------------------------------------------
    Once - 
        C++ implementation of "one-time initialization" among multiple threads. You
        must ensure that construction of a Once object is thread-safe. For example,
        Once definitions at global scope will ensures m_once will be zero'd out
        during global object construction (which is single threaded). You can then
        call one of the DoOnce() methods, or construct a Sentry object on the stack
        to determine if one-time initialization should occur.
    
        This is basically an implementation of double-check locking (DCL), which
        can be tricky to get right - see the following:
    
            "C++ and the Perils of Double-Checked Locking"
            http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf
    
        The proper solution is to ensure proper ordering/visibility of changes to 
        m_once. This is achieved via acquire/release memory barriers, via
        InterlockedExchangeAddAcquire and InterlockedExchangeAddRelease.
    ------------------------------------------------------------------------------*/
    class Once : public no_copy
    {
        LONG m_once;
        CriticalSection m_cs;
        friend class Sentry;
    
    public:
        class Sentry : public no_copy
        {
            Once &m_o;
            bool m_bOwner;
            bool m_bAcquiredCS;
    
        public:
            explicit Sentry(Once &o) : m_o(o), m_bOwner(false), m_bAcquiredCS(false)
            {
                if (InterlockedExchangeAddAcquire(&m_o.m_once, 0))
                    return; // m_once is non-zero, "once" has occurred already
    
                // "once" hasn't happened yet
                m_o.m_cs.Acquire();
                m_bAcquiredCS = true;
                m_bOwner = (m_o.m_once == 0);
            }//constructor
    
            ~Sentry()
            {
                if (m_bOwner)
                    InterlockedExchangeAddRelease(&m_o.m_once, 1);
                if (m_bAcquiredCS)
                    m_o.m_cs.Release();
            }//destructor
    
            bool OnceOwner() const {return m_bOwner;}
        };//Sentry
    
        Once() : m_once(0) {}
    
        template <typename Obj_t>
        bool DoOnce(Obj_t *obj, void (Obj_t::*mfn)())
        {
            Sentry s(*this);
            if (s.OnceOwner())
                (obj->*mfn)();
            return s.OnceOwner();
        }//DoOnce
    
        template<class Functor_t>
        bool DoOnce(const Functor_t &f)
        {
            Sentry s(*this);
            if (s.OnceOwner())
                f();
            return s.OnceOwner();
        }//DoOnce
    
    #ifdef POINTLESS
        // Once isn't enough for some folks...
        // NOTE - Once object should be acquired before calling this method
        void Reset() {InterlockedExchange(&m_once, 0);}
    
        // Use our member CriticalSection for Acquire/Release semantics
        void Acquire() {m_cs.Acquire();}
        void Release() {m_cs.Release();}
        typedef scoped_lock<Once> scoped_lock;
    #endif
    };//Once
    
    /*------------------------------------------------------------------------------
    SingletonConstruct - 
        Template class for constructing a singleton in an efficient and thread safe 
        manner. This object must be a static member of the class that you want to be 
        a singleton. This ensures m_once will be constructed during global object
        construction (which is single threaded).
    ------------------------------------------------------------------------------*/
    template <class T>
    class SingletonConstruct : public no_copy
    {
        Once m_once;
        T *m_p;
    
    public:
        SingletonConstruct() : m_p(0) {}
        ~SingletonConstruct() {Unload();}
    
        // NOTE: 1) Caller must guarantee that no references are being held
        //       2) This is thread-safe with respect to calls to Instance() when 
        //          POINTLESS is defined.
        //       3) Guaranteeing 1) makes 2) pointless...
        void Unload()
        {
    #ifdef POINTLESS
            Once::scoped_lock l(m_once);
            // reset the Once object, this will block any calls to Instance() 
            // while we have the Once object acquired - ensuring that construction
            // and destruction are mutually exclusive and thread-safe
            m_once.Reset();
    #endif
            if (m_p) // yes, I know delete 0 doesn't hurt...
            {
                delete m_p;
                m_p = 0;
            }//if
        }//Unload
    
        T& Instance()
        {
            Once::Sentry s(m_once);
            if (s.OnceOwner())
                m_p = new T;
            return *m_p;
        }//Instance
    };//SingletonConstruct
    >> P.S. ...
    Don't let emotions have any influence on the contents of your posts. I apologize if feelings were hurt. My only intention is to educate readers on what I know - and learn whatever I can in the process.

    gg

  8. #23
    Join Date
    Oct 2009
    Posts
    2

    Red face Re: Singleton in multithreaded

    Quote Originally Posted by Codeplug View Post
    To existing subscribers of this thread, our new contributor most likely found their way here from: http://www.codeproject.com/KB/thread...edLocking.aspx
    I am "Member 4490530" and "Codeplug-gg" in the article discussion area.

    >> However, the unload method is not guarded.
    Guarding it is pointless if references to the singleton are still being used elsewhere. The semantics of the Unload() method are clearly commented - you must guarantee that no references are being held. That includes attempts to get a reference. Thread-safety between Unload() and Instance() was never claimed. Stating that they aren't thread-safe is just stating the obvious...

    >> Please set the pointer back to NULL/0 when you delete it
    Good catch. Two calls to Unload() would be bad. However, the ability to call Instance() after Unload() was never intended. Adding some assert()'s to catch this type of misuse wouldn't hurt.

    Multiple threads calling Unload() and Instance() doesn't make a lot of sense to me, but despite that, I've added support for it in the latest version of my DCL object, "Once", and "SingletonConstruct":
    Code:
    #include <stdexcept>
    #include <windows.h>
    
    /*------------------------------------------------------------------------------
    no_copy - Disallow copy semantics
    ------------------------------------------------------------------------------*/
    class no_copy
    {
    protected:
        no_copy() {}
        ~no_copy() {}
    private:
        no_copy(const no_copy&);
        const no_copy& operator=(const no_copy&);
    };//no_copy
    
    /*------------------------------------------------------------------------------
    scoped_lock - 
        Generalized template for Acquire()/Release() operations in the constructor 
        and destructor respectively
    ------------------------------------------------------------------------------*/
    template<class sync_t>
    class scoped_lock : public no_copy
    {
        sync_t &m_sync;
    public:
        explicit scoped_lock(sync_t &s) : m_sync(s) {m_sync.Acquire();}
        ~scoped_lock() {m_sync.Release();}
    };//scoped_lock
    
    /*------------------------------------------------------------------------------
    CriticalSection - C++ version of a Win32 CS
    ------------------------------------------------------------------------------*/
    class CriticalSection : public no_copy
    {
        CRITICAL_SECTION m_cs;
    
    public:
        typedef scoped_lock<CriticalSection> scoped_lock;
    
        CriticalSection(bool bUseSpinCount = true, DWORD SpinCount = 4000) 
        {
            if (bUseSpinCount && 
                !InitializeCriticalSectionAndSpinCount(&m_cs, SpinCount))
                throw std::runtime_error("InitCSAndSpinCount failure");
            else
                InitializeCriticalSection(&m_cs);
        }//constructor
    
        ~CriticalSection() {::DeleteCriticalSection(&m_cs);}
        void Acquire() {::EnterCriticalSection(&m_cs);}
        void Release() {::LeaveCriticalSection(&m_cs);}
    };//CriticalSection
    
    //------------------------------------------------------------------------------
    // Interlocked*Acquire/Release() functions only for Itanium Processor Family,
    // (IPF), which we map to non-IPF Interlocked functions for other platforms
    #if !defined(_M_IA64) && !defined(__IA64__)
    #   define InterlockedExchangeAddAcquire  InterlockedExchangeAdd
    #   define InterlockedExchangeAddRelease  InterlockedExchangeAdd
    #endif
    
    /*------------------------------------------------------------------------------
    Once - 
        C++ implementation of "one-time initialization" among multiple threads. You
        must ensure that construction of a Once object is thread-safe. For example,
        Once definitions at global scope will ensures m_once will be zero'd out
        during global object construction (which is single threaded). You can then
        call one of the DoOnce() methods, or construct a Sentry object on the stack
        to determine if one-time initialization should occur.
    
        This is basically an implementation of double-check locking (DCL), which
        can be tricky to get right - see the following:
    
            "C++ and the Perils of Double-Checked Locking"
            http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf
    
        The proper solution is to ensure proper ordering/visibility of changes to 
        m_once. This is achieved via acquire/release memory barriers, via
        InterlockedExchangeAddAcquire and InterlockedExchangeAddRelease.
    ------------------------------------------------------------------------------*/
    class Once : public no_copy
    {
        LONG m_once;
        CriticalSection m_cs;
        friend class Sentry;
    
    public:
        class Sentry : public no_copy
        {
            Once &m_o;
            bool m_bOwner;
            bool m_bAcquiredCS;
    
        public:
            explicit Sentry(Once &o) : m_o(o), m_bOwner(false), m_bAcquiredCS(false)
            {
                if (InterlockedExchangeAddAcquire(&m_o.m_once, 0))
                    return; // m_once is non-zero, "once" has occurred already
    
                // "once" hasn't happened yet
                m_o.m_cs.Acquire();
                m_bAcquiredCS = true;
                m_bOwner = (m_o.m_once == 0);
            }//constructor
    
            ~Sentry()
            {
                if (m_bOwner)
                    InterlockedExchangeAddRelease(&m_o.m_once, 1);
                if (m_bAcquiredCS)
                    m_o.m_cs.Release();
            }//destructor
    
            bool OnceOwner() const {return m_bOwner;}
        };//Sentry
    
        Once() : m_once(0) {}
    
        template <typename Obj_t>
        bool DoOnce(Obj_t *obj, void (Obj_t::*mfn)())
        {
            Sentry s(*this);
            if (s.OnceOwner())
                (obj->*mfn)();
            return s.OnceOwner();
        }//DoOnce
    
        template<class Functor_t>
        bool DoOnce(const Functor_t &f)
        {
            Sentry s(*this);
            if (s.OnceOwner())
                f();
            return s.OnceOwner();
        }//DoOnce
    
    #ifdef POINTLESS
        // Once isn't enough for some folks...
        // NOTE - Once object should be acquired before calling this method
        void Reset() {InterlockedExchange(&m_once, 0);}
    
        // Use our member CriticalSection for Acquire/Release semantics
        void Acquire() {m_cs.Acquire();}
        void Release() {m_cs.Release();}
        typedef scoped_lock<Once> scoped_lock;
    #endif
    };//Once
    
    /*------------------------------------------------------------------------------
    SingletonConstruct - 
        Template class for constructing a singleton in an efficient and thread safe 
        manner. This object must be a static member of the class that you want to be 
        a singleton. This ensures m_once will be constructed during global object
        construction (which is single threaded).
    ------------------------------------------------------------------------------*/
    template <class T>
    class SingletonConstruct : public no_copy
    {
        Once m_once;
        T *m_p;
    
    public:
        SingletonConstruct() : m_p(0) {}
        ~SingletonConstruct() {Unload();}
    
        // NOTE: 1) Caller must guarantee that no references are being held
        //       2) This is thread-safe with respect to calls to Instance() when 
        //          POINTLESS is defined.
        //       3) Guaranteeing 1) makes 2) pointless...
        void Unload()
        {
    #ifdef POINTLESS
            Once::scoped_lock l(m_once);
            // reset the Once object, this will block any calls to Instance() 
            // while we have the Once object acquired - ensuring that construction
            // and destruction are mutually exclusive and thread-safe
            m_once.Reset();
    #endif
            if (m_p) // yes, I know delete 0 doesn't hurt...
            {
                delete m_p;
                m_p = 0;
            }//if
        }//Unload
    
        T& Instance()
        {
            Once::Sentry s(m_once);
            if (s.OnceOwner())
                m_p = new T;
            return *m_p;
        }//Instance
    };//SingletonConstruct
    >> P.S. ...
    Don't let emotions have any influence on the contents of your posts. I apologize if feelings were hurt. My only intention is to educate readers on what I know - and learn whatever I can in the process.

    gg
    Hi gg,

    I'll fix, update and add extra references to any websites where people have posted their messages. We all do it for the readers (the majority at least).
    Some people gave me input for the article and I was happy to get it. Not only it will mature my article but it will catch a wide range of people looking for this specific problem. As I mentioned before, I really like your post and I think you did a great job.

    I'll explain the DCLP of Schmidt with the volatile and I'll bring out the ideas Mr. Meyer. I will add a big note that this pattern of Schmidt is not thread save eventhough volatile(s) is added. Finally I will refer to this link with your name and a big thanks for the input.

    My apologies for my last reaction. I'm tired of the DCLP discussions

    HE

Page 2 of 2 FirstFirst 12

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured