/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * 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 . */ #ifndef INCLUDED_RTL_INSTANCE_HXX #define INCLUDED_RTL_INSTANCE_HXX #include "sal/config.h" #include #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 #include 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 #include 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 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 #include namespace { struct InitInstance { T * operator()() { static T aInstance; return &aInstance; } } struct InitData { Data const & operator()() { return ...; } } } T * getInstance() { return rtl_Instance< T, InitInstance, ::osl::MutexGuard, ::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 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 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; } static Inst * create(InstCtor aInstCtor, GuardCtor aGuardCtor, const Data &rData) { #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(rData); 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 = NULL; #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();
    ...
    
@tparam T variable's type @tparam Unique Implementation trick to make the inner static holder unique, using the outer class (the one that derives from this base class) */ #if defined LIBO_INTERNAL_ONLY template class Static { public: /** Gets the static. Mutual exclusion is implied by a functional -fthreadsafe-statics @return static variable */ static T & get() { static T instance; return instance; } }; #else 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; } }; }; #endif /** 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();
    ...
    
@tparam T variable's type @tparam Unique Implementation trick to make the inner static holder unique, using the outer class (the one that derives from this base class) */ #if defined LIBO_INTERNAL_ONLY template class StaticWithArg { public: /** Gets the static. Mutual exclusion is implied by a functional -fthreadsafe-statics @return static variable */ static T & get(const Data& rData) { static T instance(rData); return instance; } /** Gets the static. Mutual exclusion is implied by a functional -fthreadsafe-statics @return static variable */ static T & get(Data& rData) { static T instance(rData); return instance; } }; #else template class StaticWithArg { public: /** Gets the static. Mutual exclusion is performed using the osl global mutex. @return static variable */ static T & get(const Data& rData) { return *rtl_Instance< T, StaticInstanceWithArg, ::osl::MutexGuard, ::osl::GetGlobalMutex, Data >::create( StaticInstanceWithArg(), ::osl::GetGlobalMutex(), rData ); } /** Gets the static. Mutual exclusion is performed using the osl global mutex. @return static variable */ static T & get(Data& rData) { return *rtl_Instance< T, StaticInstanceWithArg, ::osl::MutexGuard, ::osl::GetGlobalMutex, Data >::create( StaticInstanceWithArg(), ::osl::GetGlobalMutex(), rData ); } private: struct StaticInstanceWithArg { T * operator () (const Data& rData) { static T instance(rData); return &instance; } T * operator () (Data& rData) { static T instance(rData); return &instance; } }; }; #endif /** Helper class for a late-initialized static aggregate, e.g. an array, implementing the double-checked locking pattern correctly. @tparam T aggregate's element type @tparam InitAggregate initializer functor class */ #if defined LIBO_INTERNAL_ONLY template class StaticAggregate { public: /** Gets the static aggregate, late-initializing. Mutual exclusion is implied by a functional -fthreadsafe-statics @return aggregate */ static T * get() { static T *instance = InitAggregate()(); return instance; } }; #else 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() ); } }; #endif /** 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();
    ...
    
@tparam T variable's type @tparam InitData initializer functor class @tparam 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). @tparam Data Initializer functor's return type. Default is T (common practice). */ #if defined LIBO_INTERNAL_ONLY template class StaticWithInit { public: /** Gets the static. Mutual exclusion is implied by a functional -fthreadsafe-statics @return static variable */ static T & get() { static T instance = InitData()(); return instance; } }; #else 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; } }; }; #endif } // namespace rtl #endif // INCLUDED_RTL_INSTANCE_HXX /* vim:set shiftwidth=4 softtabstop=4 expandtab: */