summaryrefslogtreecommitdiffstats
path: root/Common/include
diff options
context:
space:
mode:
authorEgor Yusov <egor.yusov@gmail.com>2016-08-20 06:35:47 +0000
committerEgor Yusov <egor.yusov@gmail.com>2016-08-20 06:35:47 +0000
commit2b64f003622ee7871fd7aec041622427dd2fc88f (patch)
tree893dab48c6d0e7121e047d8d3bb6d5bf172ce2d5 /Common/include
parentRelease v1.0.0 (diff)
downloadDiligentCore-2b64f003622ee7871fd7aec041622427dd2fc88f.tar.gz
DiligentCore-2b64f003622ee7871fd7aec041622427dd2fc88f.zip
Updated to Diligent Engine 2.0
Diffstat (limited to 'Common/include')
-rw-r--r--Common/include/AdaptiveFixedBlockAllocator.h87
-rw-r--r--Common/include/BasicFileStream.h6
-rw-r--r--Common/include/BasicTypes.h24
-rw-r--r--Common/include/DataBlobImpl.h6
-rw-r--r--Common/include/DebugUtilities.h24
-rw-r--r--Common/include/DefaultRawMemoryAllocator.h54
-rw-r--r--Common/include/Errors.h58
-rw-r--r--Common/include/FileWrapper.h2
-rw-r--r--Common/include/FixedBlockMemoryAllocator.h224
-rw-r--r--Common/include/FormatMessage.h2
-rw-r--r--Common/include/HashUtils.h6
-rw-r--r--Common/include/InterfaceID.h2
-rw-r--r--Common/include/LockHelper.h14
-rw-r--r--Common/include/ObjectBase.h10
-rw-r--r--Common/include/RefCntAutoPtr.h37
-rw-r--r--Common/include/RefCountedObjectImpl.h426
-rw-r--r--Common/include/STDAllocator.h156
-rw-r--r--Common/include/StringTools.h27
-rw-r--r--Common/include/Timer.h2
-rw-r--r--Common/include/UniqueIdentifier.h2
-rw-r--r--Common/include/ValidatedCast.h9
-rw-r--r--Common/include/pch.h2
22 files changed, 968 insertions, 212 deletions
diff --git a/Common/include/AdaptiveFixedBlockAllocator.h b/Common/include/AdaptiveFixedBlockAllocator.h
new file mode 100644
index 00000000..51396f14
--- /dev/null
+++ b/Common/include/AdaptiveFixedBlockAllocator.h
@@ -0,0 +1,87 @@
+/* Copyright 2015 Egor Yusov
+ *
+ * Licensed 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
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF ANY PROPRIETARY RIGHTS.
+ *
+ * In no event and under no legal theory, whether in tort (including negligence),
+ * contract, or otherwise, unless required by applicable law (such as deliberate
+ * and grossly negligent acts) or agreed to in writing, shall any Contributor be
+ * liable for any damages, including any direct, indirect, special, incidental,
+ * or consequential damages of any character arising as a result of this License or
+ * out of the use or inability to use the software (including but not limited to damages
+ * for loss of goodwill, work stoppage, computer failure or malfunction, or any and
+ * all other commercial damages or losses), even if such Contributor has been advised
+ * of the possibility of such damages.
+ */
+
+#pragma once
+
+#include "MemoryAllocator.h"
+#include "FixedBlockMemoryAllocator.h"
+#include "DebugUtilities.h"
+
+namespace Diligent
+{
+ // Adaptive allocator that can function as a raw memory allocator or as
+ // fixed block memory allocator. In the fixed block memory allocator mode,
+ // the block size is determined by the size of the first allocation
+ class AdaptiveFixedBlockAllocator : public IMemoryAllocator
+ {
+ public:
+ AdaptiveFixedBlockAllocator(IMemoryAllocator &RawMemAllocator, Uint32 NumBlocksPerAllocation) :
+ m_RawMemAllocator(RawMemAllocator),
+ m_pFixedBlockAllocator(nullptr, STDDeleterRawMem<FixedBlockMemoryAllocator>(RawMemAllocator)),
+ m_NumBlocksPerAllocation(NumBlocksPerAllocation)
+ {
+ // Initialize allocator when we get the fist allocation request and know allocation size
+ }
+
+ // Allocates block of memory
+ virtual void* Allocate(size_t Size, const Char* dbgDescription, const char* dbgFileName, const Int32 dbgLineNumber)override final
+ {
+ if (m_NumBlocksPerAllocation > 1)
+ {
+ if( !m_pFixedBlockAllocator )
+ {
+ // Create fixed block allocator
+ auto *pRawMem = m_RawMemAllocator.Allocate(sizeof(FixedBlockMemoryAllocator), "Memory for FixedBlockMemoryAllocator", __FILE__, __LINE__);
+ m_pFixedBlockAllocator.reset( new(pRawMem) FixedBlockMemoryAllocator(m_RawMemAllocator, Size, m_NumBlocksPerAllocation) );
+ }
+
+ return m_pFixedBlockAllocator->Allocate(Size, dbgDescription, dbgFileName, dbgLineNumber);
+ }
+ else
+ {
+ // Use default raw allocator
+ return m_RawMemAllocator.Allocate(Size, dbgDescription, dbgFileName, dbgLineNumber);
+ }
+ }
+
+ // Releases memory
+ virtual void Free(void *Ptr)override final
+ {
+ if (m_NumBlocksPerAllocation > 1)
+ {
+ VERIFY_EXPR(m_pFixedBlockAllocator);
+ m_pFixedBlockAllocator->Free(Ptr);
+ }
+ else
+ {
+ VERIFY_EXPR(!m_pFixedBlockAllocator);
+ m_RawMemAllocator.Free(Ptr);
+ }
+ }
+
+ private:
+ IMemoryAllocator &m_RawMemAllocator;
+ Uint32 m_NumBlocksPerAllocation = 0;
+ std::unique_ptr<FixedBlockMemoryAllocator, STDDeleterRawMem<FixedBlockMemoryAllocator> > m_pFixedBlockAllocator;
+ };
+}
diff --git a/Common/include/BasicFileStream.h b/Common/include/BasicFileStream.h
index 85f685a5..af71f9f8 100644
--- a/Common/include/BasicFileStream.h
+++ b/Common/include/BasicFileStream.h
@@ -1,4 +1,4 @@
-/* Copyright 2015 Egor Yusov
+/* Copyright 2015-2016 Egor Yusov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -36,10 +36,10 @@ namespace Diligent
{
/// Basic file stream implementation
-class BasicFileStream : public ObjectBase<IFileStream>
+class BasicFileStream : public ObjectBase<IFileStream, IMemoryAllocator>
{
public:
- typedef ObjectBase<IFileStream> TBase;
+ typedef ObjectBase<IFileStream, IMemoryAllocator> TBase;
BasicFileStream(const Diligent::Char *Path,
EFileAccessMode Access = EFileAccessMode::Read);
diff --git a/Common/include/BasicTypes.h b/Common/include/BasicTypes.h
index 7d3bea3d..45840213 100644
--- a/Common/include/BasicTypes.h
+++ b/Common/include/BasicTypes.h
@@ -1,4 +1,4 @@
-/* Copyright 2015 Egor Yusov
+/* Copyright 2015-2016 Egor Yusov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,25 +28,25 @@
namespace Diligent
{
- typedef float Float32;
+ typedef float Float32; ///< 32-bit float
- typedef int64_t Int64;
- typedef int32_t Int32;
- typedef int16_t Int16;
- typedef int8_t Int8;
+ typedef int64_t Int64; ///< 64-bit signed integer
+ typedef int32_t Int32; ///< 32-bit signed integer
+ typedef int16_t Int16; ///< 16-bit signed integer
+ typedef int8_t Int8; ///< 8-bit signed integer
- typedef uint64_t Uint64;
- typedef uint32_t Uint32;
- typedef uint16_t Uint16;
- typedef uint8_t Uint8;
+ typedef uint64_t Uint64; ///< 64-bit unsigned integer
+ typedef uint32_t Uint32; ///< 32-bit unsigned integer
+ typedef uint16_t Uint16; ///< 16-bit unsigned integer
+ typedef uint8_t Uint8; ///< 8-bit unsigned integer
typedef size_t SizeType;
typedef void* PVoid;
- typedef bool Bool;
+ typedef bool Bool; ///< Boolean
static const Bool False = false;
static const Bool True = true;
typedef char Char;
- typedef std::basic_string<Char> String;
+ typedef std::basic_string<Char> String; ///< String variable
}
diff --git a/Common/include/DataBlobImpl.h b/Common/include/DataBlobImpl.h
index d9441588..7b243f2c 100644
--- a/Common/include/DataBlobImpl.h
+++ b/Common/include/DataBlobImpl.h
@@ -1,4 +1,4 @@
-/* Copyright 2015 Egor Yusov
+/* Copyright 2015-2016 Egor Yusov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -35,10 +35,10 @@ namespace Diligent
{
/// Base interface for a file stream
-class DataBlobImpl : public Diligent::ObjectBase<IDataBlob>
+class DataBlobImpl : public Diligent::ObjectBase<IDataBlob, IMemoryAllocator>
{
public:
- typedef Diligent::ObjectBase<IDataBlob> TBase;
+ typedef Diligent::ObjectBase<IDataBlob, IMemoryAllocator> TBase;
virtual void QueryInterface( const Diligent::INTERFACE_ID &IID, IObject **ppInterface )override;
diff --git a/Common/include/DebugUtilities.h b/Common/include/DebugUtilities.h
index d57fd06b..8027b0ed 100644
--- a/Common/include/DebugUtilities.h
+++ b/Common/include/DebugUtilities.h
@@ -1,4 +1,4 @@
-/* Copyright 2015 Egor Yusov
+/* Copyright 2015-2016 Egor Yusov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -31,18 +31,26 @@
// This function is only requried to ensure that Message argument passed to the macro
// is actually string and not something else
inline void EnsureStr( const char* ){}
+
+#define ASSERTION_FAILED(Message, ...)\
+{ \
+ EnsureStr(Message); \
+ Diligent::MsgStream ms; \
+ Diligent::FormatMsg( ms, Message, ##__VA_ARGS__);\
+ PlatformDebug::AssertionFailed( ms.str().c_str(), __FUNCTION__, __FILE__, __LINE__); \
+}
+
# define VERIFY(Expr, Message, ...)\
{ \
+ EnsureStr(Message); \
if( !(Expr) ) \
{ \
- Diligent::MsgStream ms; \
- Diligent::FormatMsg( ms, Message, ##__VA_ARGS__);\
- PlatformDebug::AssertionFailed( ms.str().c_str(), __FUNCTION__, __FILE__, __LINE__); \
- } \
- EnsureStr(Message); \
+ ASSERTION_FAILED(Message, ##__VA_ARGS__)\
+ } \
}
-# define UNEXPECTED(Message, ...) { VERIFY(false, Message, ##__VA_ARGS__); }
-# define UNSUPPORTED(Message, ...) { VERIFY(false, Message, ##__VA_ARGS__); }
+
+# define UNEXPECTED ASSERTION_FAILED
+# define UNSUPPORTED ASSERTION_FAILED
# define VERIFY_EXPR(Expr) VERIFY(Expr, "Debug exression failed:\n", #Expr)
diff --git a/Common/include/DefaultRawMemoryAllocator.h b/Common/include/DefaultRawMemoryAllocator.h
new file mode 100644
index 00000000..142fe6b9
--- /dev/null
+++ b/Common/include/DefaultRawMemoryAllocator.h
@@ -0,0 +1,54 @@
+/* Copyright 2015-2016 Egor Yusov
+ *
+ * Licensed 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
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF ANY PROPRIETARY RIGHTS.
+ *
+ * In no event and under no legal theory, whether in tort (including negligence),
+ * contract, or otherwise, unless required by applicable law (such as deliberate
+ * and grossly negligent acts) or agreed to in writing, shall any Contributor be
+ * liable for any damages, including any direct, indirect, special, incidental,
+ * or consequential damages of any character arising as a result of this License or
+ * out of the use or inability to use the software (including but not limited to damages
+ * for loss of goodwill, work stoppage, computer failure or malfunction, or any and
+ * all other commercial damages or losses), even if such Contributor has been advised
+ * of the possibility of such damages.
+ */
+
+#pragma once
+
+/// \file
+/// Defines Diligent::DefaultRawMemoryAllocator class
+
+#include "MemoryAllocator.h"
+
+namespace Diligent
+{
+
+class DefaultRawMemoryAllocator : public IMemoryAllocator
+{
+public:
+ DefaultRawMemoryAllocator();
+
+ /// Allocates block of memory
+ virtual void* Allocate( size_t Size, const Char* dbgDescription, const char* dbgFileName, const Int32 dbgLineNumber)override;
+
+ /// Releases memory
+ virtual void Free(void *Ptr)override;
+
+ static DefaultRawMemoryAllocator& GetAllocator();
+
+private:
+ DefaultRawMemoryAllocator(const DefaultRawMemoryAllocator&) = delete;
+ DefaultRawMemoryAllocator(DefaultRawMemoryAllocator&&) = delete;
+ DefaultRawMemoryAllocator& operator = (const DefaultRawMemoryAllocator&) = delete;
+ DefaultRawMemoryAllocator& operator = (DefaultRawMemoryAllocator&&) = delete;
+};
+
+}
diff --git a/Common/include/Errors.h b/Common/include/Errors.h
index 658adb0e..c7028708 100644
--- a/Common/include/Errors.h
+++ b/Common/include/Errors.h
@@ -1,4 +1,4 @@
-/* Copyright 2015 Egor Yusov
+/* Copyright 2015-2016 Egor Yusov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,6 +29,17 @@
#include "FormatMessage.h"
#include "FileSystem.h"
+template<bool>
+void ThrowIf(std::string &&)
+{
+}
+
+template<>
+inline void ThrowIf<true>(std::string &&msg)
+{
+ throw std::runtime_error( std::move(msg) );
+}
+
template<bool bThrowException, typename FirstArgType, typename... RestArgsType>
void LogError( const char *strFunctionName, const char *strFullFilePath, int Line, const FirstArgType& first, const RestArgsType&... RestArgs )
{
@@ -39,10 +50,7 @@ void LogError( const char *strFunctionName, const char *strFullFilePath, int Lin
Diligent::FormatMsg( ss, first, RestArgs... );
auto strFullMessage = ss.str();
PlatformDebug::OutputDebugMessage( bThrowException ? PlatformDebug::DebugMessageSeverity::FatalError : PlatformDebug::DebugMessageSeverity::Error, strFullMessage.c_str() );
- if( bThrowException )
- {
- throw std::runtime_error( strFullMessage );
- }
+ ThrowIf<bThrowException>(std::move(strFullMessage));
}
#define LOG_ERROR(...)\
@@ -50,6 +58,16 @@ void LogError( const char *strFunctionName, const char *strFullFilePath, int Lin
LogError<false>(__FUNCTION__, __FILE__, __LINE__, ##__VA_ARGS__); \
}
+#define LOG_ERROR_ONCE(...)\
+{ \
+ static bool IsFirstTime = true; \
+ if(IsFirstTime) \
+ { \
+ LogError<false>(__FUNCTION__, __FILE__, __LINE__, ##__VA_ARGS__); \
+ IsFirstTime = false; \
+ } \
+}
+
#define LOG_ERROR_AND_THROW(...) \
{ \
LogError<true>(__FUNCTION__, __FILE__, __LINE__, ##__VA_ARGS__); \
@@ -65,3 +83,33 @@ void LogError( const char *strFunctionName, const char *strFullFilePath, int Lin
#define LOG_ERROR_MESSAGE(...) LOG_DEBUG_MESSAGE(PlatformDebug::DebugMessageSeverity::Error, ##__VA_ARGS__)
#define LOG_WARNING_MESSAGE(...) LOG_DEBUG_MESSAGE(PlatformDebug::DebugMessageSeverity::Warning, ##__VA_ARGS__)
#define LOG_INFO_MESSAGE(...) LOG_DEBUG_MESSAGE(PlatformDebug::DebugMessageSeverity::Info, ##__VA_ARGS__)
+
+#define LOG_ERROR_MESSAGE_ONCE(...)\
+{ \
+ static bool IsFirstTime = true; \
+ if(IsFirstTime) \
+ { \
+ LOG_ERROR_MESSAGE(__VA_ARGS__) \
+ IsFirstTime = false; \
+ } \
+}
+
+#define LOG_WARNING_MESSAGE_ONCE(...)\
+{ \
+ static bool IsFirstTime = true; \
+ if(IsFirstTime) \
+ { \
+ LOG_WARNING_MESSAGE(__VA_ARGS__)\
+ IsFirstTime = false; \
+ } \
+}
+
+#define LOG_INFO_MESSAGE_ONCE(...)\
+{ \
+ static bool IsFirstTime = true; \
+ if(IsFirstTime) \
+ { \
+ LOG_INFO_MESSAGE(__VA_ARGS__) \
+ IsFirstTime = false; \
+ } \
+}
diff --git a/Common/include/FileWrapper.h b/Common/include/FileWrapper.h
index 85851d89..41e96084 100644
--- a/Common/include/FileWrapper.h
+++ b/Common/include/FileWrapper.h
@@ -1,4 +1,4 @@
-/* Copyright 2015 Egor Yusov
+/* Copyright 2015-2016 Egor Yusov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/Common/include/FixedBlockMemoryAllocator.h b/Common/include/FixedBlockMemoryAllocator.h
new file mode 100644
index 00000000..e91b5157
--- /dev/null
+++ b/Common/include/FixedBlockMemoryAllocator.h
@@ -0,0 +1,224 @@
+/* Copyright 2015-2016 Egor Yusov
+ *
+ * Licensed 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
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF ANY PROPRIETARY RIGHTS.
+ *
+ * In no event and under no legal theory, whether in tort (including negligence),
+ * contract, or otherwise, unless required by applicable law (such as deliberate
+ * and grossly negligent acts) or agreed to in writing, shall any Contributor be
+ * liable for any damages, including any direct, indirect, special, incidental,
+ * or consequential damages of any character arising as a result of this License or
+ * out of the use or inability to use the software (including but not limited to damages
+ * for loss of goodwill, work stoppage, computer failure or malfunction, or any and
+ * all other commercial damages or losses), even if such Contributor has been advised
+ * of the possibility of such damages.
+ */
+
+#pragma once
+
+/// \file
+/// Declaration of Diligent::FixedBlockMemoryAllocator class
+
+#include <unordered_map>
+#include <mutex>
+#include <unordered_set>
+#include <vector>
+#include "MemoryAllocator.h"
+#include "STDAllocator.h"
+namespace Diligent
+{
+
+#ifdef _DEBUG
+ inline void FillWithDebugPattern(void *ptr, Uint8 Pattern, size_t NumBytes)
+ {
+ memset(ptr, Pattern, NumBytes);
+ }
+#else
+ #define FillWithDebugPattern(...)
+#endif
+
+/// Memory allocator that allocates memory in a fixed-size chunks
+class FixedBlockMemoryAllocator : public IMemoryAllocator
+{
+public:
+ FixedBlockMemoryAllocator(IMemoryAllocator &RawMemoryAllocator, size_t BlockSize, Uint32 NumBlocksInPage);
+ ~FixedBlockMemoryAllocator();
+
+ /// Allocates block of memory
+ virtual void* Allocate( size_t Size, const Char* dbgDescription, const char* dbgFileName, const Int32 dbgLineNumber)override final;
+
+ /// Releases memory
+ virtual void Free(void *Ptr)override final;
+
+private:
+ FixedBlockMemoryAllocator(const FixedBlockMemoryAllocator&) = delete;
+ FixedBlockMemoryAllocator(FixedBlockMemoryAllocator&&) = delete;
+ FixedBlockMemoryAllocator& operator = (const FixedBlockMemoryAllocator&) = delete;
+ FixedBlockMemoryAllocator& operator = (FixedBlockMemoryAllocator&&) = delete;
+
+ void CreateNewPage();
+
+ // Memory page class is based on the fixed-size memory pool described in "Fast Efficient Fixed-Size Memory Pool"
+ // by Ben Kenwright
+ class MemoryPage
+ {
+ public:
+ static const Uint8 NewPageMemPattern = 0xAA;
+ static const Uint8 AllocatedBlockMemPattern = 0xAB;
+ static const Uint8 DeallocatedBlockMemPattern = 0xDE;
+ static const Uint8 InitializedBlockMemPattern = 0xCF;
+
+ MemoryPage(FixedBlockMemoryAllocator &OwnerAllocator):
+ m_pOwnerAllocator(&OwnerAllocator),
+ m_NumFreeBlocks(OwnerAllocator.m_NumBlocksInPage),
+ m_NumInitializedBlocks(0)
+ {
+ auto PageSize = OwnerAllocator.m_BlockSize * OwnerAllocator.m_NumBlocksInPage;
+ m_pPageStart = reinterpret_cast<Uint8*>(
+ OwnerAllocator.m_RawMemoryAllocator.Allocate(PageSize, "FixedBlockMemoryAllocator page", __FILE__, __LINE__)
+ );
+ m_pNextFreeBlock = m_pPageStart;
+ FillWithDebugPattern(m_pPageStart, NewPageMemPattern, PageSize);
+ }
+
+ MemoryPage(MemoryPage&& Page) :
+ m_NumFreeBlocks(Page.m_NumFreeBlocks),
+ m_NumInitializedBlocks(Page.m_NumInitializedBlocks),
+ m_pPageStart(Page.m_pPageStart),
+ m_pNextFreeBlock(Page.m_pNextFreeBlock),
+ m_pOwnerAllocator(Page.m_pOwnerAllocator)
+ {
+ Page.m_NumFreeBlocks = 0;
+ Page.m_NumInitializedBlocks = 0;
+ Page.m_pPageStart = nullptr;
+ Page.m_pNextFreeBlock = nullptr;
+ Page.m_pOwnerAllocator = nullptr;
+ }
+
+ ~MemoryPage()
+ {
+ if(m_pOwnerAllocator)
+ m_pOwnerAllocator->m_RawMemoryAllocator.Free(m_pPageStart);
+ }
+
+ void* GetBlockStartAddress(Uint32 BlockIndex) const
+ {
+ VERIFY_EXPR(m_pOwnerAllocator != nullptr);
+ VERIFY(BlockIndex >= 0 && BlockIndex < m_pOwnerAllocator->m_NumBlocksInPage, "Invalid block index" )
+ return reinterpret_cast<Uint8*>(m_pPageStart) + BlockIndex * m_pOwnerAllocator->m_BlockSize;
+ }
+
+#ifdef _DEBUG
+ void dbgVerifyAddress(const void* pBlockAddr)const
+ {
+ size_t Delta = reinterpret_cast<const Uint8*>(pBlockAddr) - reinterpret_cast<Uint8*>(m_pPageStart);
+ VERIFY(Delta % m_pOwnerAllocator->m_BlockSize == 0, "Invalid address");
+ Uint32 BlockIndex = static_cast<Uint32>(Delta / m_pOwnerAllocator->m_BlockSize);
+ VERIFY(BlockIndex >= 0 && BlockIndex < m_pOwnerAllocator->m_NumBlocksInPage, "Invalid block index" );
+ }
+#else
+ #define dbgVerifyAddress(...)
+#endif
+
+ void* Allocate()
+ {
+ VERIFY_EXPR(m_pOwnerAllocator != nullptr);
+
+ if (m_NumFreeBlocks == 0)
+ {
+ VERIFY_EXPR(m_NumInitializedBlocks == m_pOwnerAllocator->m_NumBlocksInPage);
+ return nullptr;
+ }
+
+ // Initialize the next block
+ if (m_NumInitializedBlocks < m_pOwnerAllocator->m_NumBlocksInPage)
+ {
+ // Link next uninitialized block to the end of the list:
+
+ //
+ // ___________ ___________
+ // | | | |
+ // | 0xcdcdcd | -->| 0xcdcdcd | m_NumInitializedBlocks
+ // |-----------| | |-----------|
+ // | | | | |
+ // m_NumInitializedBlocks | 0xcdcdcd | ==> ---| |
+ // |-----------| |-----------|
+ //
+ // ~ ~ ~ ~
+ // | | | |
+ // 0 | | | |
+ // ----------- -----------
+ //
+ auto *pUninitializedBlock = GetBlockStartAddress(m_NumInitializedBlocks);
+ FillWithDebugPattern(pUninitializedBlock, InitializedBlockMemPattern, m_pOwnerAllocator->m_BlockSize);
+ void** ppNextBlock = reinterpret_cast<void**>( pUninitializedBlock );
+ ++m_NumInitializedBlocks;
+ if( m_NumInitializedBlocks < m_pOwnerAllocator->m_NumBlocksInPage )
+ *ppNextBlock = GetBlockStartAddress(m_NumInitializedBlocks);
+ else
+ *ppNextBlock = nullptr;
+ }
+
+ void* res = m_pNextFreeBlock;
+ dbgVerifyAddress(res);
+ // Move pointer to the next free block
+ m_pNextFreeBlock = *reinterpret_cast<void**>(m_pNextFreeBlock);
+ --m_NumFreeBlocks;
+ if(m_NumFreeBlocks != 0)
+ dbgVerifyAddress(m_pNextFreeBlock);
+ else
+ VERIFY_EXPR(m_pNextFreeBlock == nullptr);
+
+ FillWithDebugPattern(res, AllocatedBlockMemPattern, m_pOwnerAllocator->m_BlockSize);
+ return res;
+ }
+
+ void DeAllocate(void* p)
+ {
+ VERIFY_EXPR(m_pOwnerAllocator != nullptr);
+
+ dbgVerifyAddress(p);
+ FillWithDebugPattern(p, DeallocatedBlockMemPattern, m_pOwnerAllocator->m_BlockSize);
+ // Add block to the beginning of the linked list
+ *reinterpret_cast<void**>(p) = m_pNextFreeBlock;
+ m_pNextFreeBlock = p;
+ ++m_NumFreeBlocks;
+ }
+
+ bool HasSpace()const{return m_NumFreeBlocks>0;}
+ bool HasAllocations()const{return m_NumFreeBlocks<m_NumInitializedBlocks;}
+ private:
+
+ MemoryPage(const MemoryPage&)=delete;
+ MemoryPage& operator = (const MemoryPage)=delete;
+ MemoryPage& operator = (MemoryPage&&)=delete;
+
+ Uint32 m_NumFreeBlocks = 0; // Num of remaining blocks
+ Uint32 m_NumInitializedBlocks = 0; // Num of initialized blocks
+ void* m_pPageStart = nullptr; // Beginning of memory pool
+ void* m_pNextFreeBlock = nullptr; // Num of next free block
+ FixedBlockMemoryAllocator *m_pOwnerAllocator = nullptr;
+ };
+
+ std::vector<MemoryPage, STDAllocatorRawMem<MemoryPage> > m_PagePool;
+ std::unordered_set<size_t, std::hash<size_t>, std::equal_to<size_t>, STDAllocatorRawMem<size_t> > m_AvailablePages;
+ typedef std::pair<void*, size_t> AddrToPageIdMapElem;
+ std::unordered_map<void*, size_t, std::hash<void*>, std::equal_to<void*>, STDAllocatorRawMem<AddrToPageIdMapElem> > m_AddrToPageId;
+
+ std::mutex m_Mutex;
+
+ IMemoryAllocator &m_RawMemoryAllocator;
+ size_t m_BlockSize;
+ Uint32 m_NumBlocksInPage;
+
+ //Uint8 *tmpLargeBuffer, *tmpCurrPtr;
+};
+
+}
diff --git a/Common/include/FormatMessage.h b/Common/include/FormatMessage.h
index a20435ae..abbacce2 100644
--- a/Common/include/FormatMessage.h
+++ b/Common/include/FormatMessage.h
@@ -1,4 +1,4 @@
-/* Copyright 2015 Egor Yusov
+/* Copyright 2015-2016 Egor Yusov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/Common/include/HashUtils.h b/Common/include/HashUtils.h
index 03cf54b6..825d7171 100644
--- a/Common/include/HashUtils.h
+++ b/Common/include/HashUtils.h
@@ -1,4 +1,4 @@
-/* Copyright 2015 Egor Yusov
+/* Copyright 2015-2016 Egor Yusov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -68,7 +68,7 @@ namespace Diligent
template<typename CharType>
struct CStringCompare
{
- bool operator()( const Char *str1, const Char *str2 )const
+ bool operator()( const CharType *str1, const CharType *str2 )const
{
UNSUPPORTED( "Template specialization is not implemented" )
return false;
@@ -127,7 +127,7 @@ namespace Diligent
// Disable copy constuctor and assignments. The struct is designed
// to be initialized at creation time only
- HashMapStringKey( const HashMapStringKey& ) = delete;
+ HashMapStringKey( const HashMapStringKey& ) = delete;
HashMapStringKey& operator = ( const HashMapStringKey& ) = delete;
HashMapStringKey& operator = ( HashMapStringKey&& ) = delete;
diff --git a/Common/include/InterfaceID.h b/Common/include/InterfaceID.h
index ce426b5e..dee585e3 100644
--- a/Common/include/InterfaceID.h
+++ b/Common/include/InterfaceID.h
@@ -1,4 +1,4 @@
-/* Copyright 2015 Egor Yusov
+/* Copyright 2015-2016 Egor Yusov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/Common/include/LockHelper.h b/Common/include/LockHelper.h
index 5f27aad3..34b90f35 100644
--- a/Common/include/LockHelper.h
+++ b/Common/include/LockHelper.h
@@ -1,4 +1,4 @@
-/* Copyright 2015 Egor Yusov
+/* Copyright 2015-2016 Egor Yusov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,6 +23,7 @@
#pragma once
+#include <thread>
#include "Atomics.h"
namespace ThreadingTools
@@ -34,7 +35,8 @@ public:
enum {LOCK_FLAG_UNLOCKED = 0, LOCK_FLAG_LOCKED = 1};
LockFlag(Atomics::Long InitFlag = LOCK_FLAG_UNLOCKED)
{
- m_Flag.store(InitFlag);
+ //m_Flag.store(InitFlag);
+ m_Flag = InitFlag;
}
operator Atomics::Long()const{return m_Flag;}
@@ -97,9 +99,7 @@ public:
static void UnsafeLock(LockFlag &LockFlag)
{
while( !UnsafeTryLock( LockFlag ) )
- /*Sleep(5)*/
- // TODO
- ;
+ std::this_thread::yield();
}
void Lock(LockFlag &LockFlag)
@@ -107,9 +107,7 @@ public:
VERIFY( m_pLockFlag == NULL, "Object already locked" );
// Wait for the flag to become unlocked and lock it
while( !TryLock( LockFlag ) )
- /*Sleep(5)*/
- // TODO
- ;
+ std::this_thread::yield();
}
static void UnsafeUnlock(LockFlag &LockFlag)
diff --git a/Common/include/ObjectBase.h b/Common/include/ObjectBase.h
index c7de28c8..f92cac1e 100644
--- a/Common/include/ObjectBase.h
+++ b/Common/include/ObjectBase.h
@@ -1,4 +1,4 @@
-/* Copyright 2015 Egor Yusov
+/* Copyright 2015-2016 Egor Yusov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -58,12 +58,12 @@ namespace Diligent
/// Template class implementing base functionality for an object
-template<typename BaseInterface>
-class ObjectBase : public RefCountedObject<BaseInterface>
+template<typename BaseInterface, typename TObjectAllocator = IMemoryAllocator>
+class ObjectBase : public RefCountedObject<BaseInterface, TObjectAllocator>
{
public:
- ObjectBase(IObject *pOwner = nullptr) :
- RefCountedObject<BaseInterface>( pOwner )
+ ObjectBase(IObject *pOwner = nullptr, TObjectAllocator *pObjAllocator = nullptr) :
+ RefCountedObject<BaseInterface, TObjectAllocator>( pOwner, pObjAllocator )
{}
virtual void QueryInterface( const Diligent::INTERFACE_ID &IID, IObject **ppInterface )
diff --git a/Common/include/RefCntAutoPtr.h b/Common/include/RefCntAutoPtr.h
index 7df6f246..bc886187 100644
--- a/Common/include/RefCntAutoPtr.h
+++ b/Common/include/RefCntAutoPtr.h
@@ -1,4 +1,4 @@
-/* Copyright 2015 Egor Yusov
+/* Copyright 2015-2016 Egor Yusov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -66,18 +66,8 @@ class RefCntWeakPtr;
// RefCntWeakPtr<ObjectBase> pWeakPtr(pRawPtr);
//
-template<typename T>
-class DefaultBlockAddRefRelease : public T
-{
-private:
- // Note that the null pointer constant nullptr or any other value of type std::nullptr_t
- // cannot be converted to a pointer with reinterpret_cast: implicit conversion or
- // static_cast should be used for this purpose.
- virtual decltype( static_cast<T*>(nullptr)->AddRef() ) AddRef()override = 0;
- virtual decltype( static_cast<T*>(nullptr)->Release() ) Release()override = 0;
-};
-
-template <typename T, template<typename> class BlockAddRefRelease = DefaultBlockAddRefRelease>
+/// Template class that implements reference counting
+template <typename T>
class RefCntAutoPtr
{
public:
@@ -88,6 +78,13 @@ public:
m_pObject->AddRef();
}
+ RefCntAutoPtr(IObject *pObj, const INTERFACE_ID &IID) :
+ m_pObject(nullptr)
+ {
+ if(pObj)
+ pObj->QueryInterface( IID, reinterpret_cast<IObject**>(&m_pObject) );
+ }
+
RefCntAutoPtr(const RefCntAutoPtr &AutoPtr) :
m_pObject(AutoPtr.m_pObject)
{
@@ -186,8 +183,18 @@ public:
operator const T* ()const { return RawPtr(); }
- BlockAddRefRelease<T>* operator -> () { return static_cast<BlockAddRefRelease<T>*> (m_pObject); }
- const BlockAddRefRelease<T>* operator -> ()const{ return static_cast<BlockAddRefRelease<T>*> (m_pObject); }
+ class BlockAddRefRelease : public T
+ {
+ private:
+ // Note that the null pointer constant nullptr or any other value of type std::nullptr_t
+ // cannot be converted to a pointer with reinterpret_cast: implicit conversion or
+ // static_cast should be used for this purpose.
+ virtual decltype( static_cast<T*>(nullptr)->AddRef() ) AddRef()override = 0;
+ virtual decltype( static_cast<T*>(nullptr)->Release() ) Release()override = 0;
+ };
+
+ BlockAddRefRelease* operator -> () { return static_cast<BlockAddRefRelease*> (m_pObject); }
+ const BlockAddRefRelease* operator -> ()const{ return static_cast<BlockAddRefRelease*> (m_pObject); }
private:
// Note that the DoublePtrHelper is a private class, and can be created only by RefCntWeakPtr
diff --git a/Common/include/RefCountedObjectImpl.h b/Common/include/RefCountedObjectImpl.h
index dc5f7233..f42e56f1 100644
--- a/Common/include/RefCountedObjectImpl.h
+++ b/Common/include/RefCountedObjectImpl.h
@@ -1,4 +1,4 @@
-/* Copyright 2015 Egor Yusov
+/* Copyright 2015-2016 Egor Yusov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -31,17 +31,20 @@
#include "DebugUtilities.h"
#include "LockHelper.h"
#include "ValidatedCast.h"
+#include "MemoryAllocator.h"
namespace Diligent
{
+class IMemoryAllocator;
/// Base class for reference counting objects
-template<typename Base>
+template<typename Base, typename TObjectAllocator = IMemoryAllocator>
class RefCountedObject : public Base
{
public:
- RefCountedObject(IObject *pOwner = nullptr) :
- m_pRefCounters(nullptr)
+ RefCountedObject(IObject *pOwner = nullptr, TObjectAllocator *pObjAllocator = nullptr) :
+ m_pRefCounters(nullptr),
+ m_pAllocator(pObjAllocator)
{
if( pOwner )
{
@@ -57,15 +60,19 @@ public:
virtual ~RefCountedObject()
{
- // WARNING! m_pRefCounters pointer might be expired in scenarios like this:
+ // m_pRefCounters is set to null before executing delete this.
+ //
+ // WARNING! If m_pRefCounters was not set to null, it still may be expired in scenarios like this:
//
// A ==sp==> B ---wp---> A
//
// RefCounters_A.ReleaseStrongRef(){ // NumStrongRef == 0, NumWeakRef == 1
+ // RefCounters_A.m_pObject = nullptr;
+ // bDestroyThis = (m_lNumWeakReferences == 0) == false;
// delete A{
// A.~dtor(){
// B.~dtor(){
- // wpA.ReleaseWeakRef(){ // NumStrongRef == 0, NumWeakRef == 0
+ // wpA.ReleaseWeakRef(){ // NumStrongRef == 0, NumWeakRef == 0, m_pObject==nullptr
// delete RefCounters_A;
// ...
// VERIFY( m_pRefCounters->GetNumStrongRefs() == 0 // Access violation!
@@ -77,7 +84,7 @@ public:
// "There remain strong references to the object being destroyed" );
};
- virtual IReferenceCounters* GetReferenceCounters()const override
+ virtual IReferenceCounters* GetReferenceCounters()const override final
{
return m_pRefCounters;
}
@@ -92,6 +99,25 @@ public:
return m_pRefCounters->ReleaseStrongRef();
}
+ void* operator new(size_t Size)
+ {
+ return new Uint8[Size];
+ }
+
+ void operator delete(void *ptr)
+ {
+ delete[] reinterpret_cast<Uint8*>(ptr);
+ }
+
+ void* operator new(size_t Size, TObjectAllocator &Allocator, const Char* dbgDescription, const char* dbgFileName, const Int32 dbgLineNumber)
+ {
+ return Allocator.Allocate(Size, dbgDescription, dbgFileName, dbgLineNumber);
+ }
+
+ void operator delete(void *ptr, TObjectAllocator &Allocator, const Char* dbgDescription, const char* dbgFileName, const Int32 dbgLineNumber)
+ {
+ return Allocator.Free(ptr);
+ }
private:
@@ -103,157 +129,244 @@ private:
return new RefCountersImpl( pOwner );
}
- virtual Atomics::Long AddStrongRef()override
+ virtual Atomics::Long AddStrongRef()override final
{
VERIFY( m_pObject, "Attempting to increment strong reference counter for a destroyed object!" );
return Atomics::AtomicIncrement(m_lNumStrongReferences);
}
- virtual Atomics::Long ReleaseStrongRef()override
+ virtual Atomics::Long ReleaseStrongRef()override final
{
- // If the object is about to be destroyed, we must assure that no other
- // thread is accessing the ENTIRE REFERENCE COUNTERS OBJECT at the same time.
- // (Protecting only the pointer is not sufficient!)
- // The problem may arise if a weak pointer in some other thread is trying to
- // obtain access to the object.
- // The safest way is to always protect the entire function:
- ThreadingTools::LockHelper Lock(m_LockFlag);
-
- // It is unsafe to not always lock.
- // For instance, locking if there is only one strong reference left
- // if( m_lNumStrongReferences == 1 )
- // Lock.Lock( m_LockFlag );
- // may fail. Suppose the following scenario:
- //
- // This thread | Another thread
- // | 1. Start releasing another strong
- // | reference to this object
- // 1. Read m_lNumStrongReferences==2 | 2. Read m_lNumStrongReferences==2
- // No lock acquired | 3. Decrement the counter,
- // | m_lNumStrongReferences==1
- // 2. Decrement the counter, |
- // m_lNumStrongReferences==0, |
- // and the object will be |
- // destroyed without locking |
-
-
- // Likewise locking if there is at least one weak reference
- // if( m_lNumWeakReferences > 0 )
- // Lock.Lock( m_LockFlag );
- // may also fail. Suppose the following scenario:
- //
- // This thread | Another thread
- // |
- // 1. Read m_lNumWeakReferences==0 | 1. Start creating weak reference
- // No lock acquired | from another strong reference object
- // | 2. Call AddWeakRef(),
- // | m_lNumWeakReferences==1
- // | 3. Call Release() on the original strong
- // | referenceo bject, m_lNumStrongReferences==1
- // | 4. Start creating another strong reference
- // | from the weak pointer, acquired lock and
- // | read m_pObject
- // 2. Destroy the object |
- // | 5. Attempt to create strong reference from
- // | invalidated pointer
-
- // Both situations are unlikely to happen. However, they show that
- // conditional locking is not safe. There might be other more probable
- // situations
+ // Decrement strong reference counter without acquiring the lock.
auto RefCount = Atomics::AtomicDecrement(m_lNumStrongReferences);
VERIFY( RefCount >= 0, "Inconsistent call to ReleaseStrongRef()" );
if( RefCount == 0 )
{
- // Locking the object here is also not safe as antoher thread
- // may be running GetObject(). If it obtains the lock first, it will
- // get the pointer to the object which will then be destroyed by this
- // thread
-
- VERIFY(m_pObject, "Object pointer is null, which means it has already been destroyed");
- // There are no more STRONG references to the object and it is about to be
- // destroyed. There could be weak references, so reference counters
- // can remain alive after the object itself is destroyed.
-
- // Note that since reference counters are locked, no weak pointers can access
- // m_pObject while the object is being deleted.
-
- // We cannot destroy the object while reference counters are locked as this will
- // cause a deadlock in cases like this:
- //
- // A ==sp==> B ---wp---> A
- //
- // RefCounters_A.Lock();
- // delete A{
- // A.~dtor(){
- // B.~dtor(){
- // wpA.ReleaseWeakRef(){
- // RefCounters_A.Lock(); // Deadlock
+ // Since RefCount==0, there are no more strong references and the only place
+ // where strong ref counter can be incremented is from GetObject().
+
+ // There is a serious risk: if several threads get to this point,
+ // then <this> may already be destroyed and m_LockFlag expired.
+ // Consider the following scenario:
+ // |
+ // This thread | Another thread
+ // |
+ // m_lNumStrongReferences == 1
+ // m_lNumWeakReferences == 1
+ // |
+ // 1. Decrement m_lNumStrongReferences |
+ // Read RefCount==0, no lock acquired|
+ // | 1. Run GetObject()
+ // | - acquire the lock
+ // | - increment m_lNumStrongReferences
+ // | - release the lock
+ // |
+ // | 2. Run ReleaseWeakRef()
+ // | - decrement m_lNumWeakReferences
+ // |
+ // | 3. Run ReleaseStrongRef()
+ // | - decrement m_lNumStrongReferences
+ // | - read RefCount==0
//
+ // Both threads will get to this point. The first one will destroy <this>
+ // The second one will read expired m_LockFlag
- // So we store the pointer to the object and destory it after unlocking the
- // reference counters
- auto *pObj = m_pObject;
-
- // In a multithreaded environment, reference counters object may
- // be destroyed at any time while m_pObject->~dtor() is running.
- // NOTE: m_pObject may not be the only object referencing m_pRefCounters.
- // All objects that are owned by m_pObject will point to the same
- // reference counters object.
- m_pObject->m_pRefCounters = nullptr;
-
- // Note that this is the only place where m_pObject member can be modified
- // after the reference counters object has been created
- m_pObject = nullptr;
- // The object is now detached from the reference counters and it is if
- // it was destroyed since no one can obtain access to it.
-
- // It is essentially important to check the number of weak references
- // while the object is locked. Otherwise reference counters object
- // may be destroyed twice if ReleaseWeakRef() is executed by other thread:
- //
- // This thread | Another thread - ReleaseWeakRef()
- // |
- // 1. Decrement m_lNumStrongReferences,| 1. Decrement m_lNumWeakReferences,
- // m_lNumStrongReferences==0 | m_lNumWeakReferences == 0
- // |
- // 2. Destroy the object | 2. Destroy the object
- //
- bool bDestroyThis = m_lNumWeakReferences == 0;
+ // IT IS CRUCIALLY IMPORTANT TO ASSURE THAT ONLY ONE THREAD WILL EVER
+ // EXECUTE THIS CODE
- // We must explicitly unlock the object now to avoid deadlocks. Also,
- // if this is deleted, this->m_LockFlag will expire, which will cause
- // Lock.~LockHelper() to crash
- Lock.Unlock();
+ // The sloution is to atomically increment strong ref counter in GetObject().
+ // There are two possible scenarios depending on who first increments the counter:
- // Destroy referenced object
- delete pObj;
- // Note that this may be destroyed here already,
- // see comments in ~RefCountedObject()
- if( bDestroyThis )
- delete this;
+ // Scenario I
+ //
+ // This thread | Another thread - GetObject() | One more thread - GetObject()
+ // | |
+ // m_lNumStrongReferences == 1 |
+ // | |
+ // | 1. Acquire the lock |
+ // 1. Decrement m_lNumStrongReferences | | 1. Wait for the lock
+ // 2. Read RefCount==0 | 2. Increment m_lNumStrongReferences |
+ // 3. Start destroying the object | 3. Read StrongRefCnt == 1 |
+ // 4. Wait for the lock | 4. DO NOT return the reference |
+ // | to the object |
+ // | 5. Decrement m_lNumStrongReferences |
+ // _ _ _ _ _ _ _ _ _ _ _ _ _| 6. Release the lock _ _ _ _ _ _ _ |_ _ _ _ _ _ _ _ _ _ _ _ _ _
+ // | | 2. Acquire the lock
+ // | | 3. Increment m_lNumStrongReferences
+ // | | 4. Read StrongRefCnt == 1
+ // | | 5. DO NOT return the reference
+ // | | to the object
+ // | | 6. Decrement m_lNumStrongReferences
+ // _ _ _ _ _ _ _ _ _ _ _ _ | _ _ _ _ _ _ _ _ _ _ _ _ _ _ | _ 7. Release the lock _ _ _ _ _ _
+ // 5. Acquire the lock | |
+ // - m_lNumStrongReferences==0 | |
+ // 6. DESTROY the object | |
+ // | |
+
+ // GetObject() MUST BE SERIALIZED for this to work properly!
+
+
+ // Scenario II
+ //
+ // This thread | Another thread - GetObject()
+ // |
+ // m_lNumStrongReferences == 1
+ // |
+ // | 1. Acquire the lock
+ // | 2. Increment m_lNumStrongReferences
+ // 1. Decrement m_lNumStrongReferences |
+ // 2. Read RefCount>0 |
+ // 3. DO NOT destroy the object | 3. Read StrongRefCnt > 1 (while m_lNumStrongReferences == 1)
+ // | 4. Return the reference to the object
+ // | - Increment m_lNumStrongReferences
+ // | 5. Decrement m_lNumStrongReferences
+
+#ifdef _DEBUG
+ Atomics::Long NumStrongRefs = m_lNumStrongReferences;
+ VERIFY( NumStrongRefs == 0 || NumStrongRefs == 1, "Num strong references (", NumStrongRefs, ") is expected to be 0 or 1" );
+#endif
+
+ // Acquire the lock.
+ ThreadingTools::LockHelper Lock(m_LockFlag);
+
+ // GetObject() first acquires the lock, and only then increments and
+ // decrements the ref counter. If it reads 1 after incremeting the counter,
+ // it does not return the reference to the object and decrements the counter.
+ // If we acquired the lock, GetObject() will not start until we are done
+ VERIFY_EXPR( m_lNumStrongReferences == 0 && m_pObject != nullptr )
+
+ // Extra caution
+ if(m_lNumStrongReferences == 0 && m_pObject != nullptr)
+ {
+ // We cannot destroy the object while reference counters are locked as this will
+ // cause a deadlock in cases like this:
+ //
+ // A ==sp==> B ---wp---> A
+ //
+ // RefCounters_A.Lock();
+ // delete A{
+ // A.~dtor(){
+ // B.~dtor(){
+ // wpA.ReleaseWeakRef(){
+ // RefCounters_A.Lock(); // Deadlock
+ //
+
+ // So we store the pointer to the object and destory it after unlocking the
+ // reference counters
+ auto *pObj = m_pObject;
+
+ // In a multithreaded environment, reference counters object may
+ // be destroyed at any time while m_pObject->~dtor() is running.
+ // NOTE: m_pObject may not be the only object referencing m_pRefCounters.
+ // All objects that are owned by m_pObject will point to the same
+ // reference counters object.
+ m_pObject->m_pRefCounters = nullptr;
+
+ // Note that this is the only place where m_pObject member is modified
+ // after the ref counters object has been created
+ m_pObject = nullptr;
+ // The object is now detached from the reference counters and it is if
+ // it was destroyed since no one can obtain access to it.
+
+
+ // It is essentially important to check the number of weak references
+ // while the object is locked. Otherwise reference counters object
+ // may be destroyed twice if ReleaseWeakRef() is executed by other thread:
+ //
+ // This thread | Another thread - ReleaseWeakRef()
+ // |
+ // 1. Decrement m_lNumStrongReferences,|
+ // m_lNumStrongReferences==0, |
+ // acquire the lock, destroy |
+ // the obj, release the lock |
+ // m_lNumWeakReferences == 1 |
+ // | 1. Aacquire the lock,
+ // | decrement m_lNumWeakReferences,
+ // | m_lNumWeakReferences == 0, m_pObject == nullptr
+ // |
+ // 2. Read m_lNumWeakReferences == 0 |
+ // 3. Destroy the ref counters obj | 2. Destroy the ref counters obj
+ //
+ bool bDestroyThis = m_lNumWeakReferences == 0;
+ // ReleaseWeakRef() decrements m_lNumWeakReferences, and checks it for
+ // null only after acquiring the lock. So if m_lNumWeakReferences==0, no
+ // weak reference-related code may be running
+
+
+ // We must explicitly unlock the object now to avoid deadlocks. Also,
+ // if this is deleted, this->m_LockFlag will expire, which will cause
+ // Lock.~LockHelper() to crash
+ Lock.Unlock();
+
+ // Destroy referenced object
+ //m_Delete(pObj);
+ if (pObj->m_pAllocator)
+ {
+ auto *pAllocator = pObj->m_pAllocator;
+ pObj->~RefCountedObject();
+ pAllocator->Free(pObj);
+ }
+ else
+ {
+ delete pObj;
+ }
+
+ // Note that <this> may be destroyed here already,
+ // see comments in ~RefCountedObject()
+ if( bDestroyThis )
+ delete this;
+ }
}
return RefCount;
}
- virtual Atomics::Long AddWeakRef()override
+ virtual Atomics::Long AddWeakRef()override final
{
return Atomics::AtomicIncrement(m_lNumWeakReferences);
}
- virtual Atomics::Long ReleaseWeakRef()override
+ virtual Atomics::Long ReleaseWeakRef()override final
{
+ // All access to m_pObject must be atomic!
ThreadingTools::LockHelper Lock(m_LockFlag);
- // It is essentially important to check the number of references
+ // It is essentially important to check the number of weak references
// while the object is locked. Otherwise reference counters object
// may be destroyed twice if ReleaseStrongRef() is executed by other
// thread.
auto NumWeakReferences = Atomics::AtomicDecrement(m_lNumWeakReferences);
VERIFY( NumWeakReferences >= 0, "Inconsistent call to ReleaseWeakRef()" );
- if( NumWeakReferences == 0 && m_lNumStrongReferences == 0 )
+
+ // There is one special case when we must not destroy the ref counters object even
+ // when NumWeakReferences == 0 && m_lNumStrongReferences == 0 :
+ //
+ // This thread | Another thread - ReleaseStrongRef()
+ // |
+ // 1. Lock the object |
+ // |
+ // 2. Decrement m_lNumWeakReferences, | 1. Decrement m_lNumStrongReferences,
+ // m_lNumWeakReferences==0 | RefCount == 0
+ // |
+ // | 2. Start waiting for the lock to destroy
+ // | the object, m_pObject != nullptr
+ // 3. Do not destroy reference |
+ // counters, unlock |
+ // | 3. Acquire the lock,
+ // | destroy the object,
+ // | read m_lNumWeakReferences==0
+ // | destroy the reference counters
+ //
+ if( NumWeakReferences == 0 && /*m_lNumStrongReferences == 0 &&*/ m_pObject == nullptr )
{
- // There are no more references to the ref counters object.
+ // m_pObject is set to null atomically. If it is not null, ReleaseStrongRef()
+ // will take care of it.
+ // Access to m_pObject and decrementing m_lNumWeakReferences is atomic. Since we acquired the lock,
+ // no other thread can change either of them.
+ // Access to m_lNumStrongReferences is NOT PROTECTED by lock.
+
+ // There are no more references to the ref counters object and the object itself
+ // is already destroyed.
// We can safely unlock it and destroy.
// If we do not unlock it, this->m_LockFlag will expire,
// which will cause Lock.~LockHelper() to crash.
@@ -263,31 +376,61 @@ private:
return NumWeakReferences;
}
- virtual void GetObject( class IObject **ppObject )override
+ virtual void GetObject( class IObject **ppObject )override final
{
- // We need to lock the object before accessing it to prevent
- // deletion of the referenced object in another thread.
- // The thread which is about to delete the object locks it and
- // decrements the reference counter only after it gets exclusive access.
- // So if we obtain mutex first, we will increase the reference counter
- // before the other thread decrements it. Thus the object will not
- // be deleted.
+ if( m_pObject == nullptr)
+ return; // Early exit
+
+ // It is essential to INCREMENT REF COUNTER while object IS LOCKED to make sure that
+ // StrongRefCnt > 1 guarantees that the object is alive.
+
+ // If other thread started deleting the object in ReleaseStrongRef(), then m_lNumStrongReferences==0
+ // We must make sure only one thread is allowed to increment the counter to guarantee that if StrongRefCnt > 1,
+ // there is at least one real strong reference left. Otherwise the following scenario may occur:
+ //
+ // m_lNumStrongReferences == 1
+ //
+ // Thread 1 - ReleaseStrongRef() | Thread 2 - GetObject() | Thread 3 - GetObject()
+ // | |
+ // - Decrement m_lNumStrongReferences | -Increment m_lNumStrongReferences | -Increment m_lNumStrongReferences
+ // - Read RefCount == 0 | -Read StrongRefCnt==1 | -Read StrongRefCnt==2
+ // Destroy the object | | -Return reference to the soon
+ // | | to expire object
+ //
ThreadingTools::LockHelper Lock(m_LockFlag);
- if( m_pObject )
+
+ auto StrongRefCnt = Atomics::AtomicIncrement(m_lNumStrongReferences);
+
+ // Checking if m_pObject != nullptr is not reliable:
+ //
+ // This thread | Another thread -
+ // |
+ // 1. Acquire the lock |
+ // | 1. Decrement m_lNumStrongReferences
+ // 2. Increment m_lNumStrongReferences | 2. Test RefCount==0
+ // 3. Read StrongRefCnt == 1 | 3. Start destroying the object
+ // m_pObject != nullptr |
+ // 4. DO NOT return the reference to | 4. Wait for the lock, m_pObject != nullptr
+ // the object |
+ // 5. Decrement m_lNumStrongReferences |
+ // | 5. Destroy the object
+
+ if( m_pObject && StrongRefCnt > 1 )
{
// QueryInterface() must not lock the object, or a deadlock happens.
// The only other two methods that lock the object are ReleaseStrongRef()
// and ReleaseWeakRef(), which are never called by QueryInterface()
m_pObject->QueryInterface(Diligent::IID_Unknown, ppObject);
}
+ Atomics::AtomicDecrement(m_lNumStrongReferences);
}
- virtual Atomics::Long GetNumStrongRefs()const override
+ virtual Atomics::Long GetNumStrongRefs()const override final
{
return m_lNumStrongReferences;
}
- virtual Atomics::Long GetNumWeakRefs()const override
+ virtual Atomics::Long GetNumWeakRefs()const override final
{
return m_lNumWeakReferences;
}
@@ -308,10 +451,10 @@ private:
}
// No copies/moves
- RefCountersImpl(const RefCountersImpl&);
- RefCountersImpl(RefCountersImpl&&);
- RefCountersImpl& operator = (const RefCountersImpl&);
- RefCountersImpl& operator = (RefCountersImpl&&);
+ RefCountersImpl(const RefCountersImpl&) = delete;
+ RefCountersImpl(RefCountersImpl&&) = delete;
+ RefCountersImpl& operator = (const RefCountersImpl&) = delete;
+ RefCountersImpl& operator = (RefCountersImpl&&) = delete;
// It is crucially important that the type of the pointer
// is RefCountedObject and not IObject, since the latter
@@ -327,6 +470,7 @@ private:
// the type of pOwner->GetReferenceCounters() may not be convertible
// to RefCountedObject<Base>::RefCountersImpl*.
IReferenceCounters *m_pRefCounters;
+ TObjectAllocator *m_pAllocator;
};
}
diff --git a/Common/include/STDAllocator.h b/Common/include/STDAllocator.h
new file mode 100644
index 00000000..9fc0c5cd
--- /dev/null
+++ b/Common/include/STDAllocator.h
@@ -0,0 +1,156 @@
+/* Copyright 2015-2016 Egor Yusov
+ *
+ * Licensed 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
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF ANY PROPRIETARY RIGHTS.
+ *
+ * In no event and under no legal theory, whether in tort (including negligence),
+ * contract, or otherwise, unless required by applicable law (such as deliberate
+ * and grossly negligent acts) or agreed to in writing, shall any Contributor be
+ * liable for any damages, including any direct, indirect, special, incidental,
+ * or consequential damages of any character arising as a result of this License or
+ * out of the use or inability to use the software (including but not limited to damages
+ * for loss of goodwill, work stoppage, computer failure or malfunction, or any and
+ * all other commercial damages or losses), even if such Contributor has been advised
+ * of the possibility of such damages.
+ */
+
+#pragma once
+
+/// \file
+/// Defines Diligent::DefaultRawMemoryAllocator class
+
+#include "BasicTypes.h"
+#include "MemoryAllocator.h"
+#include "DebugUtilities.h"
+
+namespace Diligent
+{
+
+template <typename T, typename AllocatorType>
+struct STDAllocator
+{
+ typedef T value_type;
+ typedef value_type* pointer;
+ typedef const value_type* const_pointer;
+ typedef value_type& reference;
+ typedef const value_type& const_reference;
+ typedef std::size_t size_type;
+ typedef std::ptrdiff_t difference_type;
+
+ STDAllocator(AllocatorType& Allocator, const Char* dbgDescription, const Char* dbgFileName, const Int32 dbgLineNumber)noexcept :
+ m_Allocator(Allocator),
+ m_dbgDescription(dbgDescription),
+ m_dbgFileName(dbgFileName),
+ m_dbgLineNumber(dbgLineNumber)
+ {
+ }
+
+ template <class U>
+ STDAllocator(const STDAllocator<U, AllocatorType>& other)noexcept :
+ m_Allocator(other.m_Allocator),
+ m_dbgDescription(other.m_dbgDescription),
+ m_dbgFileName(other.m_dbgFileName),
+ m_dbgLineNumber(other.m_dbgLineNumber)
+ {
+ }
+
+ template <class U>
+ STDAllocator(STDAllocator<U, AllocatorType>&& other)noexcept :
+ m_Allocator(other.m_Allocator),
+ m_dbgDescription(other.m_dbgDescription),
+ m_dbgFileName(other.m_dbgFileName),
+ m_dbgLineNumber(other.m_dbgLineNumber)
+ {
+ }
+
+ template <class U>
+ STDAllocator& operator = (STDAllocator<U, AllocatorType>&& other)noexcept
+ {
+ // Android build requires this operator to be defined - I have no idea why
+ VERIFY_EXPR(&m_Allocator == &other.m_Allocator);
+ m_dbgDescription = other.m_dbgDescription;
+ m_dbgFileName = other.m_dbgFileName;
+ m_dbgLineNumber = other.m_dbgLineNumber;
+ return *this;
+ }
+
+ template< class U > struct rebind
+ {
+ typedef STDAllocator<U, AllocatorType> other;
+ };
+
+ T* allocate(std::size_t count)
+ {
+ return reinterpret_cast<T*>( m_Allocator.Allocate(count * sizeof(T), m_dbgDescription, m_dbgFileName, m_dbgLineNumber ) );
+ }
+
+ void deallocate(T* p, std::size_t count)
+ {
+ m_Allocator.Free(p);
+ }
+
+ inline size_type max_size() const
+ {
+ return std::numeric_limits<size_type>::max() / sizeof(T);
+ }
+
+ // construction/destruction
+ template< class U, class... Args >
+ void construct( U* p, Args&&... args )
+ {
+ ::new(p) U(std::forward<Args>(args)...);
+ }
+
+ inline void destroy(pointer p)
+ {
+ p->~T();
+ }
+
+ AllocatorType &m_Allocator;
+ const Char* m_dbgDescription;
+ const Char* m_dbgFileName;
+ Int32 m_dbgLineNumber;
+};
+
+#define STD_ALLOCATOR(Type, AllocatorType, Allocator, Description) STDAllocator<Type, AllocatorType>(Allocator, Description, __FILE__, __LINE__)
+
+template <class T, class U, class A>
+bool operator==(const STDAllocator<T, A>&left, const STDAllocator<U, A>&right)
+{
+ return &left.m_Allocator == &right.m_Allocator;
+}
+
+template <class T, class U, class A>
+bool operator!=(const STDAllocator<T, A> &left, const STDAllocator<U, A> &right)
+{
+ return !(left == right);
+}
+
+template<class T> using STDAllocatorRawMem = STDAllocator<T, IMemoryAllocator>;
+#define STD_ALLOCATOR_RAW_MEM(Type, Allocator, Description) STDAllocatorRawMem<Type>(Allocator, Description, __FILE__, __LINE__)
+
+template< class T, typename AllocatorType >
+struct STDDeleter
+{
+ STDDeleter(AllocatorType &Allocator) :
+ m_Allocator(Allocator)
+ {}
+
+ void operator()(T *ptr)
+ {
+ ptr->~T();
+ m_Allocator.Free(ptr);
+ }
+
+ AllocatorType &m_Allocator;
+};
+template<class T> using STDDeleterRawMem = STDDeleter<T, IMemoryAllocator>;
+
+}
diff --git a/Common/include/StringTools.h b/Common/include/StringTools.h
index 687eeef8..1fe07d48 100644
--- a/Common/include/StringTools.h
+++ b/Common/include/StringTools.h
@@ -1,4 +1,4 @@
-/* Copyright 2015 Egor Yusov
+/* Copyright 2015-2016 Egor Yusov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,6 +26,7 @@
#include <string>
#include <sstream>
#include <locale>
+#include "DebugUtilities.h"
namespace Diligent
{
@@ -72,4 +73,28 @@ inline int StrCmpNoCase(const char* Str1, const char* Str2)
return _stricmp( Str1, Str2 );
}
+// Returns true if RefStr == Str + Suff
+inline bool StrCmpSuff(const char *RefStr, const char *Str, const char *Suff)
+{
+ VERIFY_EXPR(RefStr != nullptr && Str!= nullptr && Suff != nullptr);
+ if(RefStr==nullptr)
+ return false;
+
+ const auto *r = RefStr;
+ const auto *s = Str;
+ for(; *r!=0 && *s!=0; ++r, ++s)
+ {
+ if (*r != *s)
+ return false;
+ }
+
+ if( *s != 0 )
+ {
+ VERIFY_EXPR(*r == 0);
+ return false;
+ }
+
+ return strcmp(r, Suff) == 0;
+}
+
}
diff --git a/Common/include/Timer.h b/Common/include/Timer.h
index 5e3dfe54..d0023a87 100644
--- a/Common/include/Timer.h
+++ b/Common/include/Timer.h
@@ -1,4 +1,4 @@
-/* Copyright 2015 Egor Yusov
+/* Copyright 2015-2016 Egor Yusov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/Common/include/UniqueIdentifier.h b/Common/include/UniqueIdentifier.h
index e4fd7159..ce539608 100644
--- a/Common/include/UniqueIdentifier.h
+++ b/Common/include/UniqueIdentifier.h
@@ -1,4 +1,4 @@
-/* Copyright 2015 Egor Yusov
+/* Copyright 2015-2016 Egor Yusov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/Common/include/ValidatedCast.h b/Common/include/ValidatedCast.h
index 916148c4..2f68f912 100644
--- a/Common/include/ValidatedCast.h
+++ b/Common/include/ValidatedCast.h
@@ -1,4 +1,4 @@
-/* Copyright 2015 Egor Yusov
+/* Copyright 2015-2016 Egor Yusov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,6 +28,11 @@
template<typename DstType, typename SrcType>
DstType* ValidatedCast( SrcType *Ptr )
{
- CHECK_DYNAMIC_TYPE( DstType, Ptr );
+#ifdef _DEBUG
+ if(Ptr != nullptr)
+ {
+ CHECK_DYNAMIC_TYPE( DstType, Ptr );
+ }
+#endif
return static_cast<DstType*>( Ptr );
}
diff --git a/Common/include/pch.h b/Common/include/pch.h
index 8b5e042b..6aba18ab 100644
--- a/Common/include/pch.h
+++ b/Common/include/pch.h
@@ -1,4 +1,4 @@
-/* Copyright 2015 Egor Yusov
+/* Copyright 2015-2016 Egor Yusov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.