diff options
| author | Egor Yusov <egor.yusov@gmail.com> | 2016-08-20 06:35:47 +0000 |
|---|---|---|
| committer | Egor Yusov <egor.yusov@gmail.com> | 2016-08-20 06:35:47 +0000 |
| commit | 2b64f003622ee7871fd7aec041622427dd2fc88f (patch) | |
| tree | 893dab48c6d0e7121e047d8d3bb6d5bf172ce2d5 /Common/include | |
| parent | Release v1.0.0 (diff) | |
| download | DiligentCore-2b64f003622ee7871fd7aec041622427dd2fc88f.tar.gz DiligentCore-2b64f003622ee7871fd7aec041622427dd2fc88f.zip | |
Updated to Diligent Engine 2.0
Diffstat (limited to 'Common/include')
| -rw-r--r-- | Common/include/AdaptiveFixedBlockAllocator.h | 87 | ||||
| -rw-r--r-- | Common/include/BasicFileStream.h | 6 | ||||
| -rw-r--r-- | Common/include/BasicTypes.h | 24 | ||||
| -rw-r--r-- | Common/include/DataBlobImpl.h | 6 | ||||
| -rw-r--r-- | Common/include/DebugUtilities.h | 24 | ||||
| -rw-r--r-- | Common/include/DefaultRawMemoryAllocator.h | 54 | ||||
| -rw-r--r-- | Common/include/Errors.h | 58 | ||||
| -rw-r--r-- | Common/include/FileWrapper.h | 2 | ||||
| -rw-r--r-- | Common/include/FixedBlockMemoryAllocator.h | 224 | ||||
| -rw-r--r-- | Common/include/FormatMessage.h | 2 | ||||
| -rw-r--r-- | Common/include/HashUtils.h | 6 | ||||
| -rw-r--r-- | Common/include/InterfaceID.h | 2 | ||||
| -rw-r--r-- | Common/include/LockHelper.h | 14 | ||||
| -rw-r--r-- | Common/include/ObjectBase.h | 10 | ||||
| -rw-r--r-- | Common/include/RefCntAutoPtr.h | 37 | ||||
| -rw-r--r-- | Common/include/RefCountedObjectImpl.h | 426 | ||||
| -rw-r--r-- | Common/include/STDAllocator.h | 156 | ||||
| -rw-r--r-- | Common/include/StringTools.h | 27 | ||||
| -rw-r--r-- | Common/include/Timer.h | 2 | ||||
| -rw-r--r-- | Common/include/UniqueIdentifier.h | 2 | ||||
| -rw-r--r-- | Common/include/ValidatedCast.h | 9 | ||||
| -rw-r--r-- | Common/include/pch.h | 2 |
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. |
