/* -*- 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 .
*/
/* * This file is part of LibreOffice published API.
*/
/** 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; }
T * getInstance() { static T * pInstance = 0; if (!pInstance) { ::osl::MutexGuard aGuard(::osl::Mutex::getGlobalMutex()); if (!pInstance) pInstance = new T; } return pInstance; }
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)
{ #ifdefined _MSC_VER static Inst * m_pInstance = NULL; #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)
{ #ifdefined _MSC_VER static Inst * m_pInstance = NULL; #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)
{ #ifdefined _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;
}
/** 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)
*/ #ifdefined LIBO_INTERNAL_ONLY template<typename T, typename Unique> classStatic { 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> classStatic { public: /** Gets the static. Mutual exclusion is performed using the osl global mutex.
/** 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)
*/ #ifdefined 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.
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
*/ #ifdefined 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).
*/ #ifdefined 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
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.