diff options
Diffstat (limited to 'sal/inc/rtl/instance.hxx')
-rw-r--r-- | sal/inc/rtl/instance.hxx | 472 |
1 files changed, 472 insertions, 0 deletions
diff --git a/sal/inc/rtl/instance.hxx b/sal/inc/rtl/instance.hxx new file mode 100644 index 000000000000..0bf4806bafd4 --- /dev/null +++ b/sal/inc/rtl/instance.hxx @@ -0,0 +1,472 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 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 + <http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html> + 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 occurance 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 occurances 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 occurences 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. + <pre> + struct MyStatic : public rtl::Static<MyType, MyStatic> {}; + ... + MyType & rStatic = MyStatic::get(); + ... + </pre> + + @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<typename T, typename Unique> +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<typename T, typename InitAggregate> +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. + <pre> + struct MyStatic : public rtl::StaticWithInit<MyType, MyStatic> { + MyType operator () () { + ... + return MyType( ... ); + } + }; + ... + MyType & rStatic = MyStatic::get(); + ... + </pre> + + @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<typename T, typename InitData, + typename Unique = InitData, typename Data = T> +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 |