summaryrefslogtreecommitdiffstats
path: root/include/rtl/instance.hxx
diff options
context:
space:
mode:
Diffstat (limited to 'include/rtl/instance.hxx')
-rw-r--r--include/rtl/instance.hxx639
1 files changed, 639 insertions, 0 deletions
diff --git a/include/rtl/instance.hxx b/include/rtl/instance.hxx
new file mode 100644
index 000000000..0f673e636
--- /dev/null
+++ b/include/rtl/instance.hxx
@@ -0,0 +1,639 @@
+/* -*- 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 <cstddef>
+
+#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::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.
+ <pre>
+ struct MyStatic : public rtl::Static<MyType, MyStatic> {};
+ ...
+ MyType & rStatic = MyStatic::get();
+ ...
+ </pre>
+
+ @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<typename T, typename Unique>
+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<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;
+ }
+ };
+};
+#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.
+ <pre>
+ struct MyStatic : public rtl::Static<MyType, MyStatic> {};
+ ...
+ MyType & rStatic = MyStatic::get();
+ ...
+ </pre>
+
+ @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<typename T, typename Data, typename Unique>
+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<typename T, typename Data, typename Unique>
+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<typename T, typename InitAggregate>
+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<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() );
+ }
+};
+#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.
+ <pre>
+ struct MyStatic : public rtl::StaticWithInit<MyType, MyStatic> {
+ MyType operator () () {
+ ...
+ return MyType( ... );
+ }
+ };
+ ...
+ MyType & rStatic = MyStatic::get();
+ ...
+ </pre>
+
+ @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<typename T, typename InitData,
+ typename Unique = InitData, typename Data = T>
+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<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;
+ }
+ };
+};
+#endif
+} // namespace rtl
+
+#endif // INCLUDED_RTL_INSTANCE_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */