Some improvements to Lazy<T> class. Ensures that initializer is *not* called more than once.
This commit is contained in:
parent
ec94dd9d6d
commit
280815ff49
@ -31,7 +31,9 @@
|
|||||||
#include <MUtils/Exception.h>
|
#include <MUtils/Exception.h>
|
||||||
|
|
||||||
//Qt
|
//Qt
|
||||||
|
#include <QThread>
|
||||||
#include <QAtomicPointer>
|
#include <QAtomicPointer>
|
||||||
|
#include <QAtomicInt>
|
||||||
|
|
||||||
//CRT
|
//CRT
|
||||||
#include <functional>
|
#include <functional>
|
||||||
@ -43,7 +45,7 @@ namespace MUtils
|
|||||||
*
|
*
|
||||||
* The lazy-initialized value of type T can be obtained from a `Lazy<T>` instance by using the `operator*()`. Initialization of the value happens when the `operator*()` is called for the very first time, by invoking the `initializer` lambda-function that was passed to the constructor. The return value of the `initializer` lambda-function is then stored internally, so that any subsequent call to the `operator*()` *immediately* returns the previously created value.
|
* The lazy-initialized value of type T can be obtained from a `Lazy<T>` instance by using the `operator*()`. Initialization of the value happens when the `operator*()` is called for the very first time, by invoking the `initializer` lambda-function that was passed to the constructor. The return value of the `initializer` lambda-function is then stored internally, so that any subsequent call to the `operator*()` *immediately* returns the previously created value.
|
||||||
*
|
*
|
||||||
* **Note on thread-saftey:** This class is thread-safe in the sense that all calls to `operator*()` on the same `Lazy<T>` instance, regardless from which thread, are guaranteed to return the exactly same value/object. Still, if the value has *not* been initialized yet **and** if multiple threads happen to call `operator*()` at the same time, then the `initializer` lambda-function *may* be invoked more than once (concurrently and by different threads). In that case, all but one return value of the `initializer` lambda-function are discarded, and all threads eventually obtain the same value/object.
|
* **Note on thread-saftey:** This class is thread-safe in the sense that all calls to `operator*()` on the same `Lazy<T>` instance, regardless from which thread, are guaranteed to return the exactly same value/object. The *first* thread trying to access the value will invoke the `initializer` lambda-function; concurrent threads may need to busy-wait until the initialization is completed. The `initializer` lambda-function is invoked at most once.
|
||||||
*/
|
*/
|
||||||
template<typename T> class Lazy
|
template<typename T> class Lazy
|
||||||
{
|
{
|
||||||
@ -52,33 +54,46 @@ namespace MUtils
|
|||||||
|
|
||||||
T& operator*(void)
|
T& operator*(void)
|
||||||
{
|
{
|
||||||
T *value;
|
return (*getValue());
|
||||||
while (!(value = m_value))
|
}
|
||||||
{
|
|
||||||
if (!(value = m_initializer()))
|
T* operator->(void)
|
||||||
{
|
{
|
||||||
MUTILS_THROW("Initializer returned NULL pointer!");
|
return getValue();
|
||||||
}
|
|
||||||
if (m_value.testAndSetOrdered(NULL, value))
|
|
||||||
{
|
|
||||||
break; /*success*/
|
|
||||||
}
|
|
||||||
delete value;
|
|
||||||
value = NULL;
|
|
||||||
}
|
|
||||||
return *value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
~Lazy(void)
|
~Lazy(void)
|
||||||
{
|
{
|
||||||
if(T *const value = m_value.fetchAndStoreOrdered(NULL))
|
if(T *const value = m_value)
|
||||||
{
|
{
|
||||||
delete value;
|
delete value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
__forceinline T* getValue()
|
||||||
|
{
|
||||||
|
T *value;
|
||||||
|
while (!(value = m_value))
|
||||||
|
{
|
||||||
|
if (m_status.testAndSetOrdered(0, 1))
|
||||||
|
{
|
||||||
|
if (value = m_initializer())
|
||||||
|
{
|
||||||
|
m_value.fetchAndStoreOrdered(value);
|
||||||
|
break; /*success*/
|
||||||
|
}
|
||||||
|
m_status.fetchAndStoreOrdered(0);
|
||||||
|
MUTILS_THROW("Initializer returned NULL pointer!");
|
||||||
|
}
|
||||||
|
QThread::yieldCurrentThread();
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QAtomicPointer<T> m_value;
|
QAtomicPointer<T> m_value;
|
||||||
|
QAtomicInt m_status;
|
||||||
const std::function<T*(void)> m_initializer;
|
const std::function<T*(void)> m_initializer;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user