Diligent Engine  v.2.4.g
StateObjectsRegistry.hpp
Go to the documentation of this file.
1 /*
2  * Copyright 2019-2021 Diligent Graphics LLC
3  * Copyright 2015-2019 Egor Yusov
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  * In no event and under no legal theory, whether in tort (including negligence),
18  * contract, or otherwise, unless required by applicable law (such as deliberate
19  * and grossly negligent acts) or agreed to in writing, shall any Contributor be
20  * liable for any damages, including any direct, indirect, special, incidental,
21  * or consequential damages of any character arising as a result of this License or
22  * out of the use or inability to use the software (including but not limited to damages
23  * for loss of goodwill, work stoppage, computer failure or malfunction, or any and
24  * all other commercial damages or losses), even if such Contributor has been advised
25  * of the possibility of such damages.
26  */
27 
28 #pragma once
29 
32 
33 #include "DeviceObject.h"
34 #include <unordered_map>
35 #include "STDAllocator.hpp"
36 
37 namespace Diligent
38 {
40 
61 template <typename ResourceDescType>
63 {
64 public:
66  static constexpr int DeletedObjectsToPurge = 32;
67 
68  StateObjectsRegistry(IMemoryAllocator& RawAllocator, const Char* RegistryName) :
69  m_NumDeletedObjects{0},
70  m_DescToObjHashMap(STD_ALLOCATOR_RAW_MEM(HashMapElem, RawAllocator, "Allocator for unordered_map<ResourceDescType, RefCntWeakPtr<IDeviceObject> >")),
71  m_RegistryName{RegistryName}
72  {}
73 
75  {
76  // Object registry is part of the device, and every device
77  // object holds a strong reference to the device. So device
78  // is destroyed after all device objects are destroyed, and there
79  // may only be expired references in the registry. After we
80  // purge it, the registry must be empty.
81  Purge();
82  VERIFY(m_DescToObjHashMap.empty(), "DescToObjHashMap is not empty");
83  }
84 
86 
96  void Add(const ResourceDescType& ObjectDesc, IDeviceObject* pObject)
97  {
98  ThreadingTools::LockHelper Lock(m_LockFlag);
99 
100  // If the number of outstanding deleted objects reached the threshold value,
101  // purge the registry. Since we have exclusive access now, it is safe
102  // to do.
103  if (m_NumDeletedObjects >= DeletedObjectsToPurge)
104  {
105  Purge();
106  m_NumDeletedObjects = 0;
107  }
108 
109  // Try to construct the new element in place
110  auto Elems = m_DescToObjHashMap.emplace(std::make_pair(ObjectDesc, Diligent::RefCntWeakPtr<IDeviceObject>(pObject)));
111  // It is theorertically possible that the same object can be found
112  // in the registry. This might happen if two threads try to create
113  // the same object at the same time. They both will not find the
114  // object and then will create and try to add it.
115  //
116  // If the object already exists, we replace the existing reference.
117  // This is safer as there might be scenarios where existing reference
118  // might be expired. For instance, two threads try to create the same
119  // object which is not in the registry. The first thread creates
120  // the object, adds it to the registry and then releases it. After that
121  // the second thread creates the same object and tries to add it to
122  // the registry. It will find an existing expired reference to the
123  // object.
124  if (!Elems.second)
125  {
126  VERIFY(Elems.first->first == ObjectDesc, "Incorrect object description");
127  LOG_WARNING_MESSAGE("Object named '", Elems.first->first.Name,
128  "' with the same description already exists in the registry."
129  "Replacing with the new object named '",
130  ObjectDesc.Name ? ObjectDesc.Name : "", "'.");
131  Elems.first->second = pObject;
132  }
133  }
134 
136  void Find(const ResourceDescType& Desc, IDeviceObject** ppObject)
137  {
138  VERIFY(*ppObject == nullptr, "Overwriting reference to existing object may cause memory leaks");
139  *ppObject = nullptr;
140  ThreadingTools::LockHelper Lock(m_LockFlag);
141 
142  auto It = m_DescToObjHashMap.find(Desc);
143  if (It != m_DescToObjHashMap.end())
144  {
145  // Try to obtain strong reference to the object.
146  // This is an atomic operation and we either get
147  // a new strong reference or object has been destroyed
148  // and we get null.
149  auto pObject = It->second.Lock();
150  if (pObject)
151  {
152  *ppObject = pObject.Detach();
153  //LOG_INFO_MESSAGE( "Equivalent of the requested state object named \"", Desc.Name ? Desc.Name : "", "\" found in the ", m_RegistryName, " registry. Reusing existing object.");
154  }
155  else
156  {
157  // Expired object found: remove it from the map
158  m_DescToObjHashMap.erase(It);
159  Atomics::AtomicDecrement(m_NumDeletedObjects);
160  }
161  }
162  }
163 
165  void Purge()
166  {
167  Uint32 NumPurgedObjects = 0;
168  auto It = m_DescToObjHashMap.begin();
169  while (It != m_DescToObjHashMap.end())
170  {
171  auto NextIt = It;
172  ++NextIt;
173  // Note that IsValid() is not a thread-safe function in the sense that it
174  // can give false positive results. The only thread-safe way to check if the
175  // object is alive is to lock the weak pointer, but that requires thread
176  // synchronization. We will immediately unlock the pointer anyway, so we
177  // want to detect 100% expired pointers. IsValid() does provide that information
178  // because once a weak pointer becomes invalid, it will be invalid
179  // until it is destroyed. It is not a problem if we miss an expired weak
180  // pointer as it will definitiely be removed next time.
181  if (!It->second.IsValid())
182  {
183  m_DescToObjHashMap.erase(It);
184  ++NumPurgedObjects;
185  }
186 
187  It = NextIt;
188  }
189  LOG_INFO_MESSAGE("Purged ", NumPurgedObjects, " deleted objects from the ", m_RegistryName, " registry");
190  }
191 
196  {
197  Atomics::AtomicIncrement(m_NumDeletedObjects);
198  }
199 
200 private:
202  ThreadingTools::LockFlag m_LockFlag;
203 
205  Atomics::AtomicLong m_NumDeletedObjects;
206 
208  typedef std::pair<const ResourceDescType, RefCntWeakPtr<IDeviceObject>> HashMapElem;
209  std::unordered_map<ResourceDescType, RefCntWeakPtr<IDeviceObject>, std::hash<ResourceDescType>, std::equal_to<ResourceDescType>, STDAllocatorRawMem<HashMapElem>> m_DescToObjHashMap;
210 
212  const String m_RegistryName;
213 };
214 } // namespace Diligent
Diligent::StateObjectsRegistry::Find
void Find(const ResourceDescType &Desc, IDeviceObject **ppObject)
Finds the object in the registry.
Definition: StateObjectsRegistry.hpp:136
ThreadingTools::LockHelper
Definition: LockHelper.hpp:59
Diligent::StateObjectsRegistry::StateObjectsRegistry
StateObjectsRegistry(IMemoryAllocator &RawAllocator, const Char *RegistryName)
Definition: StateObjectsRegistry.hpp:68
Diligent::Char
char Char
Definition: BasicTypes.h:64
ThreadingTools::LockFlag
Definition: LockHelper.hpp:36
STDAllocator.hpp
Diligent::RefCntWeakPtr
Implementation of weak pointers.
Definition: RefCntAutoPtr.hpp:40
Diligent::StateObjectsRegistry::ReportDeletedObject
void ReportDeletedObject()
Increments the number of outstanding deleted objects. When this number reaches DeletedObjectsToPurge,...
Definition: StateObjectsRegistry.hpp:195
Diligent::StateObjectsRegistry::~StateObjectsRegistry
~StateObjectsRegistry()
Definition: StateObjectsRegistry.hpp:74
BasicAtomics::AtomicIncrement
static Type AtomicIncrement(std::atomic< Type > &Val)
Definition: BasicAtomics.hpp:41
Diligent::IDeviceObject
Base interface for all objects created by the render device Diligent::IRenderDevice.
Definition: DeviceObject.h:52
Diligent::STDAllocator
Definition: STDAllocator.hpp:53
LOG_INFO_MESSAGE
#define LOG_INFO_MESSAGE(...)
Definition: Errors.hpp:124
Diligent::StateObjectsRegistry::DeletedObjectsToPurge
static constexpr int DeletedObjectsToPurge
Number of outstanding deleted objects to purge the registry.
Definition: StateObjectsRegistry.hpp:66
Diligent::Uint32
uint32_t Uint32
32-bit unsigned integer
Definition: BasicTypes.h:51
Diligent::StateObjectsRegistry
Template class implementing state object registry.
Definition: StateObjectsRegistry.hpp:62
BasicAtomics::AtomicLong
std::atomic< Long > AtomicLong
Definition: BasicAtomics.hpp:35
DeviceObject.h
BasicAtomics::AtomicDecrement
static Type AtomicDecrement(std::atomic< Type > &Val)
Definition: BasicAtomics.hpp:48
Diligent::IMemoryAllocator
Base interface for a raw memory allocator.
Definition: MemoryAllocator.h:41
LOG_WARNING_MESSAGE
#define LOG_WARNING_MESSAGE(...)
Definition: Errors.hpp:123
Diligent::StateObjectsRegistry::Add
void Add(const ResourceDescType &ObjectDesc, IDeviceObject *pObject)
Adds a new object to the registry.
Definition: StateObjectsRegistry.hpp:96
STD_ALLOCATOR_RAW_MEM
#define STD_ALLOCATOR_RAW_MEM(Type, Allocator, Description)
Definition: STDAllocator.hpp:179
Diligent::String
std::basic_string< Char > String
String variable.
Definition: BasicTypes.h:66
VERIFY
#define VERIFY(...)
Definition: DebugUtilities.hpp:76
Diligent::StateObjectsRegistry::Purge
void Purge()
Purges outstanding deleted objects from the registry.
Definition: StateObjectsRegistry.hpp:165
Diligent
The library uses Direct3D-style math:
Definition: AdvancedMath.hpp:37