Diligent Engine  v.2.4.g
RefCountedObjectImpl.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 "../../Primitives/interface/Object.h"
34 #include "../../Primitives/interface/MemoryAllocator.h"
35 #include "../../Platforms/interface/Atomics.hpp"
36 #include "../../Platforms/Basic/interface/DebugUtilities.hpp"
37 #include "LockHelper.hpp"
38 #include "ValidatedCast.hpp"
39 
40 namespace Diligent
41 {
42 
43 // This class controls the lifetime of a refcounted object
44 class RefCountersImpl final : public IReferenceCounters
45 {
46 public:
47  inline virtual ReferenceCounterValueType AddStrongRef() override final
48  {
49  VERIFY(m_ObjectState == ObjectState::Alive, "Attempting to increment strong reference counter for a destroyed or not itialized object!");
50  VERIFY(m_ObjectWrapperBuffer[0] != 0 && m_ObjectWrapperBuffer[1] != 0, "Object wrapper is not initialized");
51  return Atomics::AtomicIncrement(m_lNumStrongReferences);
52  }
53 
54  template <class TPreObjectDestroy>
55  inline ReferenceCounterValueType ReleaseStrongRef(TPreObjectDestroy PreObjectDestroy)
56  {
57  VERIFY(m_ObjectState == ObjectState::Alive, "Attempting to decrement strong reference counter for an object that is not alive");
58  VERIFY(m_ObjectWrapperBuffer[0] != 0 && m_ObjectWrapperBuffer[1] != 0, "Object wrapper is not initialized");
59 
60  // Decrement strong reference counter without acquiring the lock.
61  auto RefCount = Atomics::AtomicDecrement(m_lNumStrongReferences);
62  VERIFY(RefCount >= 0, "Inconsistent call to ReleaseStrongRef()");
63  if (RefCount == 0)
64  {
65  PreObjectDestroy();
66  TryDestroyObject();
67  }
68 
69  return RefCount;
70  }
71 
72  inline virtual ReferenceCounterValueType ReleaseStrongRef() override final
73  {
74  return ReleaseStrongRef([]() {});
75  }
76 
77  inline virtual ReferenceCounterValueType AddWeakRef() override final
78  {
79  return Atomics::AtomicIncrement(m_lNumWeakReferences);
80  }
81 
82  inline virtual ReferenceCounterValueType ReleaseWeakRef() override final
83  {
84  // The method must be serialized!
85  ThreadingTools::LockHelper Lock(m_LockFlag);
86  // It is essentially important to check the number of weak references
87  // while holding the lock. Otherwise reference counters object
88  // may be destroyed twice if ReleaseStrongRef() is executed by other
89  // thread.
90  auto NumWeakReferences = Atomics::AtomicDecrement(m_lNumWeakReferences);
91  VERIFY(NumWeakReferences >= 0, "Inconsistent call to ReleaseWeakRef()");
92 
93  // There are two special case when we must not destroy the ref counters object even
94  // when NumWeakReferences == 0 && m_lNumStrongReferences == 0 :
95  //
96  // This thread | Another thread - ReleaseStrongRef()
97  // |
98  // 1. Lock the object |
99  // |
100  // 2. Decrement m_lNumWeakReferences, | 1. Decrement m_lNumStrongReferences,
101  // m_lNumWeakReferences==0 | RefCount == 0
102  // |
103  // | 2. Start waiting for the lock to destroy
104  // | the object, m_ObjectState != ObjectState::Destroyed
105  // 3. Do not destroy reference |
106  // counters, unlock |
107  // | 3. Acquire the lock,
108  // | destroy the object,
109  // | read m_lNumWeakReferences==0
110  // | destroy the reference counters
111  //
112 
113  // If an exception is thrown during the object construction and there is a weak pointer to the object itself,
114  // we may get to this point, but should not destroy the reference counters, because it will be destroyed by MakeNewRCObj
115  // Consider this example:
116  //
117  // A ==sp==> B ---wp---> A
118  //
119  // MakeNewRCObj::operator()
120  // try
121  // {
122  // A.ctor()
123  // B.ctor()
124  // wp.ctor m_lNumWeakReferences==1
125  // throw
126  // wp.dtor m_lNumWeakReferences==0, destroy this
127  // }
128  // catch(...)
129  // {
130  // Destory ref counters second time
131  // }
132  //
133  if (NumWeakReferences == 0 && /*m_lNumStrongReferences == 0 &&*/ m_ObjectState == ObjectState::Destroyed)
134  {
135  VERIFY_EXPR(m_lNumStrongReferences == 0);
136  VERIFY(m_ObjectWrapperBuffer[0] == 0 && m_ObjectWrapperBuffer[1] == 0, "Object wrapper must be null");
137  // m_ObjectState is set to ObjectState::Destroyed under the lock. If the state is not Destroyed,
138  // ReleaseStrongRef() will take care of it.
139  // Access to Object wrapper and decrementing m_lNumWeakReferences is atomic. Since we acquired the lock,
140  // no other thread can access either of them.
141  // Access to m_lNumStrongReferences is NOT PROTECTED by lock.
142 
143  // There are no more references to the ref counters object and the object itself
144  // is already destroyed.
145  // We can safely unlock it and destroy.
146  // If we do not unlock it, this->m_LockFlag will expire,
147  // which will cause Lock.~LockHelper() to crash.
148  Lock.Unlock();
149  SelfDestroy();
150  }
151  return NumWeakReferences;
152  }
153 
154  inline virtual void GetObject(struct IObject** ppObject) override final
155  {
156  if (m_ObjectState != ObjectState::Alive)
157  return; // Early exit
158 
159  // It is essential to INCREMENT REF COUNTER while HOLDING THE LOCK to make sure that
160  // StrongRefCnt > 1 guarantees that the object is alive.
161 
162  // If other thread started deleting the object in ReleaseStrongRef(), then m_lNumStrongReferences==0
163  // We must make sure only one thread is allowed to increment the counter to guarantee that if StrongRefCnt > 1,
164  // there is at least one real strong reference left. Otherwise the following scenario may occur:
165  //
166  // m_lNumStrongReferences == 1
167  //
168  // Thread 1 - ReleaseStrongRef() | Thread 2 - GetObject() | Thread 3 - GetObject()
169  // | |
170  // - Decrement m_lNumStrongReferences | -Increment m_lNumStrongReferences | -Increment m_lNumStrongReferences
171  // - Read RefCount == 0 | -Read StrongRefCnt==1 | -Read StrongRefCnt==2
172  // Destroy the object | | -Return reference to the soon
173  // | | to expire object
174  //
175  ThreadingTools::LockHelper Lock(m_LockFlag);
176 
177  auto StrongRefCnt = Atomics::AtomicIncrement(m_lNumStrongReferences);
178 
179  // Checking if m_ObjectState == ObjectState::Alive only is not reliable:
180  //
181  // This thread | Another thread
182  // |
183  // 1. Acquire the lock |
184  // | 1. Decrement m_lNumStrongReferences
185  // 2. Increment m_lNumStrongReferences | 2. Test RefCount==0
186  // 3. Read StrongRefCnt == 1 | 3. Start destroying the object
187  // m_ObjectState == ObjectState::Alive |
188  // 4. DO NOT return the reference to | 4. Wait for the lock, m_ObjectState == ObjectState::Alive
189  // the object |
190  // 5. Decrement m_lNumStrongReferences |
191  // | 5. Destroy the object
192 
193  if (m_ObjectState == ObjectState::Alive && StrongRefCnt > 1)
194  {
195  VERIFY(m_ObjectWrapperBuffer[0] != 0 && m_ObjectWrapperBuffer[1] != 0, "Object wrapper is not initialized");
196  // QueryInterface() must not lock the object, or a deadlock happens.
197  // The only other two methods that lock the object are ReleaseStrongRef()
198  // and ReleaseWeakRef(), which are never called by QueryInterface()
199  auto* pWrapper = reinterpret_cast<ObjectWrapperBase*>(m_ObjectWrapperBuffer);
200  pWrapper->QueryInterface(IID_Unknown, ppObject);
201  }
202  Atomics::AtomicDecrement(m_lNumStrongReferences);
203  }
204 
205  inline virtual ReferenceCounterValueType GetNumStrongRefs() const override final
206  {
207  return m_lNumStrongReferences;
208  }
209 
210  inline virtual ReferenceCounterValueType GetNumWeakRefs() const override final
211  {
212  return m_lNumWeakReferences;
213  }
214 
215 private:
216  template <typename AllocatorType, typename ObjectType>
217  friend class MakeNewRCObj;
218 
219  RefCountersImpl() noexcept
220  {
221  m_lNumStrongReferences = 0;
222  m_lNumWeakReferences = 0;
223 #ifdef DILIGENT_DEBUG
224  memset(m_ObjectWrapperBuffer, 0, sizeof(m_ObjectWrapperBuffer));
225 #endif
226  }
227 
228  class ObjectWrapperBase
229  {
230  public:
231  virtual void DestroyObject() = 0;
232  virtual void QueryInterface(const INTERFACE_ID& iid, IObject** ppInterface) = 0;
233  };
234 
235  template <typename ObjectType, typename AllocatorType>
236  class ObjectWrapper : public ObjectWrapperBase
237  {
238  public:
239  ObjectWrapper(ObjectType* pObject, AllocatorType* pAllocator) noexcept :
240  m_pObject{pObject},
241  m_pAllocator{pAllocator}
242  {}
243  virtual void DestroyObject() override final
244  {
245  if (m_pAllocator)
246  {
247  m_pObject->~ObjectType();
248  m_pAllocator->Free(m_pObject);
249  }
250  else
251  {
252  delete m_pObject;
253  }
254  }
255  virtual void QueryInterface(const INTERFACE_ID& iid, IObject** ppInterface) override final
256  {
257  return m_pObject->QueryInterface(iid, ppInterface);
258  }
259 
260  private:
261  // It is crucially important that the type of the pointer
262  // is ObjectType and not IObject, since the latter
263  // does not have virtual dtor.
264  ObjectType* const m_pObject;
265  AllocatorType* const m_pAllocator;
266  };
267 
268  template <typename ObjectType, typename AllocatorType>
269  void Attach(ObjectType* pObject, AllocatorType* pAllocator)
270  {
271  VERIFY(m_ObjectState == ObjectState::NotInitialized, "Object has already been attached");
272  static_assert(sizeof(ObjectWrapper<ObjectType, AllocatorType>) == sizeof(m_ObjectWrapperBuffer), "Unexpected object wrapper size");
273  new (m_ObjectWrapperBuffer) ObjectWrapper<ObjectType, AllocatorType>(pObject, pAllocator);
274  m_ObjectState = ObjectState::Alive;
275  }
276 
277  void TryDestroyObject()
278  {
279  // Since RefCount==0, there are no more strong references and the only place
280  // where strong ref counter can be incremented is from GetObject().
281 
282  // If several threads were allowed to get to this point, there would
283  // be serious risk that <this> had already been destroyed and m_LockFlag expired.
284  // Consider the following scenario:
285  // |
286  // This thread | Another thread
287  // |
288  // m_lNumStrongReferences == 1
289  // m_lNumWeakReferences == 1
290  // |
291  // 1. Decrement m_lNumStrongReferences |
292  // Read RefCount==0, no lock acquired|
293  // | 1. Run GetObject()
294  // | - acquire the lock
295  // | - increment m_lNumStrongReferences
296  // | - release the lock
297  // |
298  // | 2. Run ReleaseWeakRef()
299  // | - decrement m_lNumWeakReferences
300  // |
301  // | 3. Run ReleaseStrongRef()
302  // | - decrement m_lNumStrongReferences
303  // | - read RefCount==0
304  //
305  // Both threads will get to this point. The first one will destroy <this>
306  // The second one will read expired m_LockFlag
307 
308  // IT IS CRUCIALLY IMPORTANT TO ASSURE THAT ONLY ONE THREAD WILL EVER
309  // EXECUTE THIS CODE
310 
311  // The sloution is to atomically increment strong ref counter in GetObject().
312  // There are two possible scenarios depending on who first increments the counter:
313 
314 
315  // Scenario I
316  //
317  // This thread | Another thread - GetObject() | One more thread - GetObject()
318  // | |
319  // m_lNumStrongReferences == 1 |
320  // | |
321  // | 1. Acquire the lock |
322  // 1. Decrement m_lNumStrongReferences | | 1. Wait for the lock
323  // 2. Read RefCount==0 | 2. Increment m_lNumStrongReferences |
324  // 3. Start destroying the object | 3. Read StrongRefCnt == 1 |
325  // 4. Wait for the lock | 4. DO NOT return the reference |
326  // | to the object |
327  // | 5. Decrement m_lNumStrongReferences |
328  // _ _ _ _ _ _ _ _ _ _ _ _ _| 6. Release the lock _ _ _ _ _ _ _ |_ _ _ _ _ _ _ _ _ _ _ _ _ _
329  // | | 2. Acquire the lock
330  // | | 3. Increment m_lNumStrongReferences
331  // | | 4. Read StrongRefCnt == 1
332  // | | 5. DO NOT return the reference
333  // | | to the object
334  // | | 6. Decrement m_lNumStrongReferences
335  // _ _ _ _ _ _ _ _ _ _ _ _ | _ _ _ _ _ _ _ _ _ _ _ _ _ _ | _ 7. Release the lock _ _ _ _ _ _
336  // 5. Acquire the lock | |
337  // - m_lNumStrongReferences==0 | |
338  // 6. DESTROY the object | |
339  // | |
340 
341  // GetObject() MUST BE SERIALIZED for this to work properly!
342 
343 
344  // Scenario II
345  //
346  // This thread | Another thread - GetObject()
347  // |
348  // m_lNumStrongReferences == 1
349  // |
350  // | 1. Acquire the lock
351  // | 2. Increment m_lNumStrongReferences
352  // 1. Decrement m_lNumStrongReferences |
353  // 2. Read RefCount>0 |
354  // 3. DO NOT destroy the object | 3. Read StrongRefCnt > 1 (while m_lNumStrongReferences == 1)
355  // | 4. Return the reference to the object
356  // | - Increment m_lNumStrongReferences
357  // | 5. Decrement m_lNumStrongReferences
358 
359 #ifdef DILIGENT_DEBUG
360  {
361  Atomics::Long NumStrongRefs = m_lNumStrongReferences;
362  VERIFY(NumStrongRefs == 0 || NumStrongRefs == 1, "Num strong references (", NumStrongRefs, ") is expected to be 0 or 1");
363  }
364 #endif
365 
366  // Acquire the lock.
367  ThreadingTools::LockHelper Lock(m_LockFlag);
368 
369  // GetObject() first acquires the lock, and only then increments and
370  // decrements the ref counter. If it reads 1 after incremeting the counter,
371  // it does not return the reference to the object and decrements the counter.
372  // If we acquired the lock, GetObject() will not start until we are done
373  VERIFY_EXPR(m_lNumStrongReferences == 0 && m_ObjectState == ObjectState::Alive);
374 
375  // Extra caution
376  if (m_lNumStrongReferences == 0 && m_ObjectState == ObjectState::Alive)
377  {
378  VERIFY(m_ObjectWrapperBuffer[0] != 0 && m_ObjectWrapperBuffer[1] != 0, "Object wrapper is not initialized");
379  // We cannot destroy the object while reference counters are locked as this will
380  // cause a deadlock in cases like this:
381  //
382  // A ==sp==> B ---wp---> A
383  //
384  // RefCounters_A.Lock();
385  // delete A{
386  // A.~dtor(){
387  // B.~dtor(){
388  // wpA.ReleaseWeakRef(){
389  // RefCounters_A.Lock(); // Deadlock
390  //
391 
392  // So we copy the object wrapper and destroy the object after unlocking the
393  // reference counters
394  size_t ObjectWrapperBufferCopy[ObjectWrapperBufferSize];
395  for (size_t i = 0; i < ObjectWrapperBufferSize; ++i)
396  ObjectWrapperBufferCopy[i] = m_ObjectWrapperBuffer[i];
397 #ifdef DILIGENT_DEBUG
398  memset(m_ObjectWrapperBuffer, 0, sizeof(m_ObjectWrapperBuffer));
399 #endif
400  auto* pWrapper = reinterpret_cast<ObjectWrapperBase*>(ObjectWrapperBufferCopy);
401 
402  // In a multithreaded environment, reference counters object may
403  // be destroyed at any time while m_pObject->~dtor() is running.
404  // NOTE: m_pObject may not be the only object referencing m_pRefCounters.
405  // All objects that are owned by m_pObject will point to the same
406  // reference counters object.
407 
408  // Note that this is the only place where m_ObjectState is
409  // modified after the ref counters object has been created
410  m_ObjectState = ObjectState::Destroyed;
411  // The object is now detached from the reference counters and it is if
412  // it was destroyed since no one can obtain access to it.
413 
414 
415  // It is essentially important to check the number of weak references
416  // while the object is locked. Otherwise reference counters object
417  // may be destroyed twice if ReleaseWeakRef() is executed by other thread:
418  //
419  // This thread | Another thread - ReleaseWeakRef()
420  // |
421  // 1. Decrement m_lNumStrongReferences,|
422  // m_lNumStrongReferences==0, |
423  // acquire the lock, destroy |
424  // the obj, release the lock |
425  // m_lNumWeakReferences == 1 |
426  // | 1. Aacquire the lock,
427  // | decrement m_lNumWeakReferences,
428  // | m_lNumWeakReferences == 0,
429  // | m_ObjectState == ObjectState::Destroyed
430  // |
431  // 2. Read m_lNumWeakReferences == 0 |
432  // 3. Destroy the ref counters obj | 2. Destroy the ref counters obj
433  //
434  bool bDestroyThis = m_lNumWeakReferences == 0;
435  // ReleaseWeakRef() decrements m_lNumWeakReferences, and checks it for
436  // zero only after acquiring the lock. So if m_lNumWeakReferences==0, no
437  // weak reference-related code may be running
438 
439 
440  // We must explicitly unlock the object now to avoid deadlocks. Also,
441  // if this is deleted, this->m_LockFlag will expire, which will cause
442  // Lock.~LockHelper() to crash
443  Lock.Unlock();
444 
445  // Destroy referenced object
446  pWrapper->DestroyObject();
447 
448  // Note that <this> may be destroyed here already,
449  // see comments in ~ControlledObjectType()
450  if (bDestroyThis)
451  SelfDestroy();
452  }
453  }
454 
455  void SelfDestroy()
456  {
457  delete this;
458  }
459 
460  ~RefCountersImpl()
461  {
462  VERIFY(m_lNumStrongReferences == 0 && m_lNumWeakReferences == 0,
463  "There exist outstanding references to the object being destroyed");
464  }
465 
466  // No copies/moves
467  // clang-format off
468  RefCountersImpl (const RefCountersImpl&) = delete;
469  RefCountersImpl (RefCountersImpl&&) = delete;
470  RefCountersImpl& operator = (const RefCountersImpl&) = delete;
471  RefCountersImpl& operator = (RefCountersImpl&&) = delete;
472  // clang-format on
473 
474  struct IObjectStub : public IObject
475  {
476  virtual ~IObjectStub() = 0;
477  };
478  // MSVC starting with 19.25.28610.4 fails to compile sizeof(ObjectWrapper<IObject, IMemoryAllocator>) because
479  // IObject does not have virtual destructor. The compiler is technically right, so we use IObjectStub,
480  // which does have virtual destructor.
481  static constexpr size_t ObjectWrapperBufferSize = sizeof(ObjectWrapper<IObjectStub, IMemoryAllocator>) / sizeof(size_t);
482 
483  size_t m_ObjectWrapperBuffer[ObjectWrapperBufferSize];
484  Atomics::AtomicLong m_lNumStrongReferences;
485  Atomics::AtomicLong m_lNumWeakReferences;
486  ThreadingTools::LockFlag m_LockFlag;
487  enum class ObjectState : Int32
488  {
489  NotInitialized,
490  Alive,
491  Destroyed
492  };
493  volatile ObjectState m_ObjectState = ObjectState::NotInitialized;
494 };
495 
496 
498 template <typename Base>
499 class RefCountedObject : public Base
500 {
501 public:
502  template <typename... BaseCtorArgTypes>
503  RefCountedObject(IReferenceCounters* pRefCounters, BaseCtorArgTypes&&... BaseCtorArgs) noexcept :
504  // clang-format off
505  Base {std::forward<BaseCtorArgTypes>(BaseCtorArgs)...},
506  m_pRefCounters{ValidatedCast<RefCountersImpl>(pRefCounters) }
507  // clang-format on
508  {
509  // If object is allocated on stack, ref counters will be null
510  //VERIFY(pRefCounters != nullptr, "Reference counters must not be null")
511  }
512 
513  // Virtual destructor makes sure all derived classes can be destroyed
514  // through the pointer to the base class
516  {
517  // WARNING! m_pRefCounters may be expired in scenarios like this:
518  //
519  // A ==sp==> B ---wp---> A
520  //
521  // RefCounters_A.ReleaseStrongRef(){ // NumStrongRef == 0, NumWeakRef == 1
522  // bDestroyThis = (m_lNumWeakReferences == 0) == false;
523  // delete A{
524  // A.~dtor(){
525  // B.~dtor(){
526  // wpA.ReleaseWeakRef(){ // NumStrongRef == 0, NumWeakRef == 0, m_pObject==nullptr
527  // delete RefCounters_A;
528  // ...
529  // VERIFY( m_pRefCounters->GetNumStrongRefs() == 0 // Access violation!
530 
531  // This also may happen if one thread is executing ReleaseStrongRef(), while
532  // another one is simultaneously running ReleaseWeakRef().
533 
534  //VERIFY( m_pRefCounters->GetNumStrongRefs() == 0,
535  // "There remain strong references to the object being destroyed" );
536  }
537 
538  inline virtual IReferenceCounters* DILIGENT_CALL_TYPE GetReferenceCounters() const override final
539  {
540  VERIFY_EXPR(m_pRefCounters != nullptr);
541  return m_pRefCounters;
542  }
543 
544  inline virtual ReferenceCounterValueType DILIGENT_CALL_TYPE AddRef() override final
545  {
546  VERIFY_EXPR(m_pRefCounters != nullptr);
547  // Since type of m_pRefCounters is RefCountersImpl,
548  // this call will not be virtual and should be inlined
549  return m_pRefCounters->AddStrongRef();
550  }
551 
553  {
554  VERIFY_EXPR(m_pRefCounters != nullptr);
555  // Since type of m_pRefCounters is RefCountersImpl,
556  // this call will not be virtual and should be inlined
557  return m_pRefCounters->ReleaseStrongRef();
558  }
559 
560  template <class TPreObjectDestroy>
561  inline ReferenceCounterValueType Release(TPreObjectDestroy PreObjectDestroy)
562  {
563  VERIFY_EXPR(m_pRefCounters != nullptr);
564  return m_pRefCounters->ReleaseStrongRef(PreObjectDestroy);
565  }
566 
567 protected:
568  template <typename AllocatorType, typename ObjectType>
569  friend class MakeNewRCObj;
570 
571  friend class RefCountersImpl;
572 
573 
574  // Operator delete can only be called from MakeNewRCObj if an exception is thrown,
575  // or from RefCountersImpl when object is destroyed
576  // It needs to be protected (not private!) to allow generation of destructors in derived classes
577 
578  void operator delete(void* ptr)
579  {
580  delete[] reinterpret_cast<Uint8*>(ptr);
581  }
582 
583  template <typename ObjectAllocatorType>
584  void operator delete(void* ptr, ObjectAllocatorType& Allocator, const Char* dbgDescription, const char* dbgFileName, const Int32 dbgLineNumber)
585  {
586  return Allocator.Free(ptr);
587  }
588 
589 private:
590  // Operator new is private, and can only be called by MakeNewRCObj
591 
592  void* operator new(size_t Size)
593  {
594  return new Uint8[Size];
595  }
596 
597  template <typename ObjectAllocatorType>
598  void* operator new(size_t Size, ObjectAllocatorType& Allocator, const Char* dbgDescription, const char* dbgFileName, const Int32 dbgLineNumber)
599  {
600  return Allocator.Allocate(Size, dbgDescription, dbgFileName, dbgLineNumber);
601  }
602 
603 
604  // Note that the type of the reference counters is RefCountersImpl,
605  // not IReferenceCounters. This avoids virtual calls from
606  // AddRef() and Release() methods
607  RefCountersImpl* const m_pRefCounters;
608 };
609 
610 
611 template <typename ObjectType, typename AllocatorType = IMemoryAllocator>
613 {
614 public:
615  MakeNewRCObj(AllocatorType& Allocator, const Char* Description, const char* FileName, const Int32 LineNumber, IObject* pOwner = nullptr) noexcept :
616  // clang-format off
617  m_pAllocator{&Allocator},
618  m_pOwner{pOwner}
619 #ifdef DILIGENT_DEVELOPMENT
620  , m_dvpDescription{Description}
621  , m_dvpFileName {FileName }
622  , m_dvpLineNumber {LineNumber }
623  // clang-format on
624 #endif
625  {
626  }
627 
628  MakeNewRCObj(IObject* pOwner = nullptr) noexcept :
629  // clang-format off
630  m_pAllocator {nullptr},
631  m_pOwner {pOwner }
632 #ifdef DILIGENT_DEVELOPMENT
633  , m_dvpDescription{nullptr}
634  , m_dvpFileName {nullptr}
635  , m_dvpLineNumber {0 }
636 #endif
637  // clang-format on
638  {}
639 
640  // clang-format off
641  MakeNewRCObj (const MakeNewRCObj&) = delete;
642  MakeNewRCObj (MakeNewRCObj&&) = delete;
643  MakeNewRCObj& operator=(const MakeNewRCObj&) = delete;
644  MakeNewRCObj& operator=(MakeNewRCObj&&) = delete;
645  // clang-format on
646 
647  template <typename... CtorArgTypes>
648  ObjectType* operator()(CtorArgTypes&&... CtorArgs)
649  {
650  RefCountersImpl* pNewRefCounters = nullptr;
651  IReferenceCounters* pRefCounters = nullptr;
652  if (m_pOwner != nullptr)
653  pRefCounters = m_pOwner->GetReferenceCounters();
654  else
655  {
656  // Constructor of RefCountersImpl class is private and only accessible
657  // by methods of MakeNewRCObj
658  pNewRefCounters = new RefCountersImpl();
659  pRefCounters = pNewRefCounters;
660  }
661  ObjectType* pObj = nullptr;
662  try
663  {
664 #ifndef DILIGENT_DEVELOPMENT
665  static constexpr const char* m_dvpDescription = "<Unavailable in release build>";
666  static constexpr const char* m_dvpFileName = "<Unavailable in release build>";
667  static constexpr Int32 m_dvpLineNumber = -1;
668 #endif
669  // Operators new and delete of RefCountedObject are private and only accessible
670  // by methods of MakeNewRCObj
671  if (m_pAllocator)
672  pObj = new (*m_pAllocator, m_dvpDescription, m_dvpFileName, m_dvpLineNumber) ObjectType(pRefCounters, std::forward<CtorArgTypes>(CtorArgs)...);
673  else
674  pObj = new ObjectType(pRefCounters, std::forward<CtorArgTypes>(CtorArgs)...);
675  if (pNewRefCounters != nullptr)
676  pNewRefCounters->Attach<ObjectType, AllocatorType>(pObj, m_pAllocator);
677  }
678  catch (...)
679  {
680  if (pNewRefCounters != nullptr)
681  pNewRefCounters->SelfDestroy();
682  throw;
683  }
684  return pObj;
685  }
686 
687 private:
688  AllocatorType* const m_pAllocator;
689  IObject* const m_pOwner;
690 
691 #ifdef DILIGENT_DEVELOPMENT
692  const Char* const m_dvpDescription;
693  const char* const m_dvpFileName;
694  Int32 const m_dvpLineNumber;
695 #endif
696 };
697 
698 #define NEW_RC_OBJ(Allocator, Desc, Type, ...) MakeNewRCObj<Type, typename std::remove_reference<decltype(Allocator)>::type>(Allocator, Desc, __FILE__, __LINE__, ##__VA_ARGS__)
699 
700 } // namespace Diligent
Diligent::IReferenceCounters
Base interface for a reference counter object that stores the number of strong and weak references an...
Definition: ReferenceCounters.h:44
Diligent::INTERFACE_ID
struct INTERFACE_ID INTERFACE_ID
Definition: InterfaceID.h:54
BasicAtomics::Long
long Long
Definition: BasicAtomics.hpp:34
ThreadingTools::LockHelper
Definition: LockHelper.hpp:59
Diligent::Char
char Char
Definition: BasicTypes.h:64
Diligent::RefCountedObject::RefCountedObject
RefCountedObject(IReferenceCounters *pRefCounters, BaseCtorArgTypes &&... BaseCtorArgs) noexcept
Definition: RefCountedObjectImpl.hpp:503
ThreadingTools::LockFlag
Definition: LockHelper.hpp:36
Diligent::RefCountersImpl::AddWeakRef
virtual ReferenceCounterValueType AddWeakRef() override final
Definition: RefCountedObjectImpl.hpp:77
Diligent::IObject
Base interface for all dynamic objects in the engine.
Definition: Object.h:41
Diligent::RefCountedObject::RefCountersImpl
friend class RefCountersImpl
Definition: RefCountedObjectImpl.hpp:571
Diligent::RefCountersImpl::ReleaseWeakRef
virtual ReferenceCounterValueType ReleaseWeakRef() override final
Definition: RefCountedObjectImpl.hpp:82
Diligent::RefCountersImpl::GetNumWeakRefs
virtual ReferenceCounterValueType GetNumWeakRefs() const override final
Definition: RefCountedObjectImpl.hpp:210
Diligent::RefCountersImpl::ReleaseStrongRef
virtual ReferenceCounterValueType ReleaseStrongRef() override final
Definition: RefCountedObjectImpl.hpp:72
BasicAtomics::AtomicIncrement
static Type AtomicIncrement(std::atomic< Type > &Val)
Definition: BasicAtomics.hpp:41
ValidatedCast.hpp
Diligent::INTERFACE_ID
Unique interface identifier.
Definition: InterfaceID.h:37
Diligent::RefCountedObject::AddRef
virtual ReferenceCounterValueType AddRef() override final
Definition: RefCountedObjectImpl.hpp:544
Diligent::RefCountedObject::Release
ReferenceCounterValueType Release(TPreObjectDestroy PreObjectDestroy)
Definition: RefCountedObjectImpl.hpp:561
Diligent::Int32
int32_t Int32
32-bit signed integer
Definition: BasicTypes.h:46
Diligent::IObject::GetReferenceCounters
virtual IReferenceCounters * GetReferenceCounters() const =0
Returns the pointer to IReferenceCounters interface of the associated reference counters object....
Diligent::MakeNewRCObj::MakeNewRCObj
MakeNewRCObj(AllocatorType &Allocator, const Char *Description, const char *FileName, const Int32 LineNumber, IObject *pOwner=nullptr) noexcept
Definition: RefCountedObjectImpl.hpp:615
DILIGENT_CALL_TYPE
#define DILIGENT_CALL_TYPE
Definition: CommonDefinitions.h:45
Diligent::RefCountersImpl::GetObject
virtual void GetObject(struct IObject **ppObject) override final
Definition: RefCountedObjectImpl.hpp:154
Diligent::RefCountersImpl::GetNumStrongRefs
virtual ReferenceCounterValueType GetNumStrongRefs() const override final
Definition: RefCountedObjectImpl.hpp:205
Diligent::MakeNewRCObj::operator()
ObjectType * operator()(CtorArgTypes &&... CtorArgs)
Definition: RefCountedObjectImpl.hpp:648
Diligent::MakeNewRCObj::operator=
MakeNewRCObj & operator=(const MakeNewRCObj &)=delete
Diligent::RefCountedObject::Release
virtual ReferenceCounterValueType Release() override
Definition: RefCountedObjectImpl.hpp:552
Diligent::MakeNewRCObj
Definition: RefCountedObjectImpl.hpp:612
BasicAtomics::AtomicLong
std::atomic< Long > AtomicLong
Definition: BasicAtomics.hpp:35
BasicAtomics::AtomicDecrement
static Type AtomicDecrement(std::atomic< Type > &Val)
Definition: BasicAtomics.hpp:48
Diligent::RefCountedObject::~RefCountedObject
virtual ~RefCountedObject()
Definition: RefCountedObjectImpl.hpp:515
Diligent::RefCountersImpl
Definition: RefCountedObjectImpl.hpp:44
Diligent::RefCountersImpl::AddStrongRef
virtual ReferenceCounterValueType AddStrongRef() override final
Definition: RefCountedObjectImpl.hpp:47
Diligent::RefCountedObject
Base class for all reference counting objects.
Definition: RefCountedObjectImpl.hpp:499
Diligent::Uint8
uint8_t Uint8
8-bit unsigned integer
Definition: BasicTypes.h:53
VERIFY_EXPR
#define VERIFY_EXPR(...)
Definition: DebugUtilities.hpp:79
VERIFY
#define VERIFY(...)
Definition: DebugUtilities.hpp:76
Diligent::MakeNewRCObj::MakeNewRCObj
MakeNewRCObj(IObject *pOwner=nullptr) noexcept
Definition: RefCountedObjectImpl.hpp:628
Diligent::RefCountersImpl::ReleaseStrongRef
ReferenceCounterValueType ReleaseStrongRef(TPreObjectDestroy PreObjectDestroy)
Definition: RefCountedObjectImpl.hpp:55
Diligent::ReferenceCounterValueType
long ReferenceCounterValueType
Definition: ReferenceCounters.h:37
LockHelper.hpp
Diligent::RefCountedObject::GetReferenceCounters
virtual IReferenceCounters * GetReferenceCounters() const override final
Definition: RefCountedObjectImpl.hpp:538
ThreadingTools::LockHelper::Unlock
void Unlock() noexcept
Definition: LockHelper.hpp:142
Diligent
The library uses Direct3D-style math:
Definition: AdvancedMath.hpp:37