/************************************************************** * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *************************************************************/ #if !defined INCLUDED_RTL_INSTANCE_HXX #define INCLUDED_RTL_INSTANCE_HXX #include "osl/doublecheckedlocking.h" #include "osl/getglobalmutex.hxx" namespace { /** A non-broken version of the double-checked locking pattern. See for a description of double-checked locking, why it is broken, and how it can be fixed. Always use this template instead of spelling out the double-checked locking pattern explicitly, and only in those rare cases where that is not possible and you have to spell it out explicitly, at least call OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER() at the right places. That way, all platform-dependent code to make double-checked locking work can be kept in one place. Usage scenarios: 1 Static instance (most common case) Pattern: T * getInstance() { static T * pInstance = 0; if (!pInstance) { ::osl::MutexGuard aGuard(::osl::Mutex::getGlobalMutex()); if (!pInstance) { static T aInstance; pInstance = &aInstance; } } return pInstance; } Code: #include "rtl/instance.hxx" #include "osl/getglobalmutex.hxx" namespace { struct Init { T * operator()() { static T aInstance; return &aInstance; } }; } T * getInstance() { return rtl_Instance< T, Init, ::osl::MutexGuard, ::osl::GetGlobalMutex >::create( Init(), ::osl::GetGlobalMutex()); } 2 Dynamic instance Pattern: T * getInstance() { static T * pInstance = 0; if (!pInstance) { ::osl::MutexGuard aGuard(::osl::Mutex::getGlobalMutex()); if (!pInstance) pInstance = new T; } return pInstance; } Code: #include "rtl/instance.hxx" #include "osl/getglobalmutex.hxx" namespace { struct Init { T * operator()() { return new T; } }; } T * getInstance() { return rtl_Instance< T, Init, ::osl::MutexGuard, ::osl::GetGlobalMutex >::create( Init(), ::osl::GetGlobalMutex()); } 3 Other guard/mutex Pattern: T * getInstance() { static T * pInstance = 0; if (!pInstance) { SomeGuard aGuard(pSomeMutex); if (!pInstance) { static T aInstance; pInstance = &aInstance; } } return pInstance; } Code: #include "rtl/instance.hxx" namespace { struct InitInstance { T * operator()() { static T aInstance; return &aInstance; } }; struct InitGuard { SomeMutex * operator()() { return pSomeMutex; } }; } T * getInstance() { return rtl_Instance< T, InitInstance, SomeGuard, InitGuard >::create( InitInstance(), InitMutex()); } 4 Calculate extra data Pattern: T * getInstance() { static T * pInstance = 0; if (!pInstance) { Data aData(...); ::osl::MutexGuard aGuard(::osl::Mutex::getGlobalMutex()); if (!pInstance) { static T aInstance(aData); pInstance = &aInstance; } } return pInstance; } Code: #include "rtl/instance.hxx" #include "osl/getglobalmutex.hxx" namespace { struct InitInstance { T * operator()() { static T aInstance; return &aInstance; } } struct InitData { Data const & operator()() { return ...; } } } T * getInstance() { return rtl_Instance< T, InitInstance, ::osl::Mutex, ::osl::GetGlobalMutex, Data, InitData >::create( InitInstance(), ::osl::GetGlobalMutex(), InitData()); } Some comments: For any instantiation of rtl_Instance, at most one call to a create method may occur in the program code: Each occurrence of a create method within the program code is supposed to return a fresh object instance on the first call, and that same object instance on subsequent calls; but independent occurrences of create methods are supposed to return independent object instances. Since there is a one-to-one correspondence between object instances and instantiations of rtl_Instance, the requirement should be clear. One measure to enforce the requirement is that rtl_Instance lives in an unnamed namespace, so that instantiations of rtl_Instance in different translation units will definitely be different instantiations. A drawback of that measure is that the name of the class needs a funny "hand coded" prefix "rtl_" instead of a proper namespace prefix like "::rtl::". A known problem with this template is when two occurrences of calls to create methods with identical template arguments appear in one translation unit. Those two places will share a single object instance. This can be avoided by using different Init structs (see the above code samples) in the two places. There is no need to make m_pInstance volatile, in order to avoid usage of stale copies of m_pInstance: At the first check, a thread will see that m_pInstance contains either 0 or a valid pointer. If it contains a valid pointer, it cannot be stale, and that pointer is used. If it contains 0, acquiring the mutex will ensure that the second check sees a non-stale value in all cases. On some compilers, the create methods would not be inlined if they contained any static variables, so m_pInstance is made a class member instead (and the create methods are inlined). But on MSC, the definition of the class member m_pInstance would cause compilation to fail with an internal compiler error. Since MSC is able to inline methods containing static variables, m_pInstance is moved into the methods there. Note that this only works well because for any instantiation of rtl_Instance at most one call to a create method should be present, anyway. */ template< typename Inst, typename InstCtor, typename Guard, typename GuardCtor, typename Data = int, typename DataCtor = int > class rtl_Instance { public: static inline Inst * create(InstCtor aInstCtor, GuardCtor aGuardCtor) { #if defined _MSC_VER static Inst * m_pInstance = 0; #endif // _MSC_VER Inst * p = m_pInstance; if (!p) { Guard aGuard(aGuardCtor()); p = m_pInstance; if (!p) { p = aInstCtor(); OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); m_pInstance = p; } } else { OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); } return p; } static inline Inst * create(InstCtor aInstCtor, GuardCtor aGuardCtor, DataCtor aDataCtor) { #if defined _MSC_VER static Inst * m_pInstance = 0; #endif // _MSC_VER Inst * p = m_pInstance; if (!p) { Data aData(aDataCtor()); Guard aGuard(aGuardCtor()); p = m_pInstance; if (!p) { p = aInstCtor(aData); OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); m_pInstance = p; } } else { OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); } return p; } private: #if !defined _MSC_VER static Inst * m_pInstance; #endif // _MSC_VER }; #if !defined _MSC_VER template< typename Inst, typename InstCtor, typename Guard, typename GuardCtor, typename Data, typename DataCtor > Inst * rtl_Instance< Inst, InstCtor, Guard, GuardCtor, Data, DataCtor >::m_pInstance = 0; #endif // _MSC_VER } namespace rtl { /** Helper base class for a late-initialized (default-constructed) static variable, implementing the double-checked locking pattern correctly. @derive Derive from this class (common practice), e.g.
    struct MyStatic : public rtl::Static {};
    ...
    MyType & rStatic = MyStatic::get();
    ...
    
@tplparam T variable's type @tplparam Unique Implementation trick to make the inner static holder unique, using the outer class (the one that derives from this base class) */ template class Static { public: /** Gets the static. Mutual exclusion is performed using the osl global mutex. @return static variable */ static T & get() { return *rtl_Instance< T, StaticInstance, ::osl::MutexGuard, ::osl::GetGlobalMutex >::create( StaticInstance(), ::osl::GetGlobalMutex() ); } private: struct StaticInstance { T * operator () () { static T instance; return &instance; } }; }; /** Helper class for a late-initialized static aggregate, e.g. an array, implementing the double-checked locking pattern correctly. @tplparam T aggregate's element type @tplparam InitAggregate initializer functor class */ template class StaticAggregate { public: /** Gets the static aggregate, late-initializing. Mutual exclusion is performed using the osl global mutex. @return aggregate */ static T * get() { return rtl_Instance< T, InitAggregate, ::osl::MutexGuard, ::osl::GetGlobalMutex >::create( InitAggregate(), ::osl::GetGlobalMutex() ); } }; /** Helper base class for a late-initialized static variable, implementing the double-checked locking pattern correctly. @derive Derive from this class (common practice), providing an initializer functor class, e.g.
    struct MyStatic : public rtl::StaticWithInit {
        MyType operator () () {
            ...
            return MyType( ... );
        }
    };
    ...
    MyType & rStatic = MyStatic::get();
    ...
    
@tplparam T variable's type @tplparam InitData initializer functor class @tplparam Unique Implementation trick to make the inner static holder unique, using the outer class (the one that derives from this base class). Default is InitData (common practice). @tplparam Data Initializer functor's return type. Default is T (common practice). */ template class StaticWithInit { public: /** Gets the static. Mutual exclusion is performed using the osl global mutex. @return static variable */ static T & get() { return *rtl_Instance< T, StaticInstanceWithInit, ::osl::MutexGuard, ::osl::GetGlobalMutex, Data, InitData >::create( StaticInstanceWithInit(), ::osl::GetGlobalMutex(), InitData() ); } private: struct StaticInstanceWithInit { T * operator () ( Data d ) { static T instance(d); return &instance; } }; }; } // namespace rtl #endif // INCLUDED_RTL_INSTANCE_HXX