UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
RefCounting.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
5#include "CoreTypes.h"
6#include "AutoRTFM.h"
11#include "Misc/Build.h"
14#include "Templates/Requires.h"
15#include "Templates/TypeHash.h"
17#include <atomic>
18#include <type_traits>
19
29{
31 : RefCount(InRefCount)
32 {
33 }
38
39 UE_DEPRECATED(5.6, "Inspecting an object's refcount is deprecated.")
40 operator uint32() const
41 {
42 return RefCount;
43 }
44
45 void CheckAtLeast(uint32 N) const
46 {
47 // It's harmless to check if your refcount is at least a certain amount. Be aware
48 // that inside an AutoRTFM transaction, Release() is deferred until commit, so an
49 // object's refcount may be higher than you expected. In other words, when inside
50 // of a transaction, this check may not trigger even when the number of active
51 // reference holders is lower than the passed-in value.
52 checkSlow(RefCount >= N);
53 }
54
55private:
56 uint32 RefCount = 0;
57};
58
63namespace UE::Private
64{
65
66CORE_API void CheckRefCountIsNonZero();
67
68template <typename AtomicType>
69class TTransactionalAtomicRefCount
70{
71public:
72 template <auto DeleteFn>
73 AtomicType AddRef() const
74 {
75 AtomicType Refs;
76
78 {
79 Refs = RefCount++;
80 };
81
82 // If we are inside a transaction, a rollback must undo our refcount bump.
83 // In general, this is best handled by Release(). However, there is one case
84 // that needs to be handled with special care. A brand-new object has a
85 // refcount of zero, and a rollback must return it to this zero-refcount state.
86 // However, calling AddRef() followed by Release() would not accomplish this;
87 // instead, it would free the object entirely! We need to guard against this,
88 // since it could lead to a double-free, so we detect the zero-reference case
89 // and special-case it.
90 if (Refs == 0)
91 {
93 {
94 --RefCount;
95 // The refcount is likely zero now, but leaving the object alive isn't a leak.
96 // We are restoring the object back to its initial "just-created" state.
97 };
98 }
99 else
100 {
102 {
104 };
105 }
106
107 return Refs + 1;
108 }
109
110 template <auto DeleteFn>
111 AtomicType Release() const
112 {
113#if DO_GUARD_SLOW
114 if (RefCount == 0)
115 {
116 CheckRefCountIsNonZero();
117 }
118#endif
119
121
122 if (AutoRTFM::IsClosed())
123 {
124 // We return the active number of references minus one to maintain the existing
125 // Release() behavior as closely as possible while inside a transaction, even
126 // though we are deferring reference count changes until commit time.
127 // Be advised that GetRefCount() would reveal our trickery, since it
128 // always returns the true refcount.
130 {
131 RefsToReturn = AtomicType(RefCount.load(std::memory_order_relaxed)) - 1;
132 };
133
134 // Refcount changes and frees are deferred until the transaction is concluded.
136 {
138 };
139 }
140 else
141 {
143 }
144
145 return RefsToReturn;
146 }
147
148 AtomicType GetRefCount() const
149 {
150 // This is equivalent to https://en.cppreference.com/w/cpp/memory/shared_ptr/use_count
151 //
152 // Inside of an AutoRTFM transaction, the returned refcount value may be higher than you'd
153 // expect, because all Release calls are deferred until the transaction commit time.
154 AtomicType Refs;
156 {
157 // A 'live' reference count is unstable by nature and so there's no benefit
158 // to try and enforce memory ordering around the reading of it.
159 Refs = RefCount.load(std::memory_order_relaxed);
160 };
161 return Refs;
162 }
163
164private:
165 template <auto DeleteFn>
166 AtomicType ImmediatelyRelease() const
167 {
168 // fetch_sub returns the refcount _before_ it was decremented. std::memory_order_acq_rel is
169 // used so that, if we do end up executing the destructor, it's not possible for side effects
170 // from executing the destructor to end up being visible before we've determined that the
171 // reference count is actually zero.
172 AtomicType RefsBeforeRelease = RefCount.fetch_sub(1, std::memory_order_acq_rel);
173
174#if DO_GUARD_SLOW
175 // A check-failure is issued if an object is over-released.
176 if (RefsBeforeRelease == 0)
177 {
178 CheckRefCountIsNonZero();
179 }
180#endif
181 // We immediately free the object if its refcount has become zero.
182 if (RefsBeforeRelease == 1)
183 {
184 DeleteFn(this);
185 }
186 return RefsBeforeRelease;
187 }
188
189 mutable std::atomic<AtomicType> RefCount = 0;
190};
191
192}
193
196{
197public:
198 virtual ~IRefCountedObject() { }
199 virtual FReturnedRefCountValue AddRef() const = 0;
200
201 // TODO (SOL-7350): return FReturnedRefCountValue from Release(); clean up call sites
202 // which rely on its return value.
203 virtual uint32 Release() const = 0;
204
205 // TODO (SOL-7350): mark this function as deprecated; clean up existing callers.
206 virtual uint32 GetRefCount() const = 0;
207};
208
212class FRefCountBase : UE::Private::TTransactionalAtomicRefCount<uint32>
213{
214public:
215 FRefCountBase() = default;
216 virtual ~FRefCountBase() = default;
217
218 FRefCountBase(const FRefCountBase& Rhs) = delete;
219 FRefCountBase& operator=(const FRefCountBase& Rhs) = delete;
220
222 {
223 return FReturnedRefCountValue{Super::AddRef<DeleteThis>()};
224 }
225
227 {
228 return Super::Release<DeleteThis>();
229 }
230
232 {
233 return Super::GetRefCount();
234 }
235
236private:
237 using Super = UE::Private::TTransactionalAtomicRefCount<uint32>;
238
239 static void DeleteThis(const Super* This)
240 {
241 delete static_cast<const FRefCountBase*>(This);
242 }
243};
244
252{
253public:
254 FRefCountedObject(): NumRefs(0) {}
255 virtual ~FRefCountedObject() { check(!NumRefs); }
259 {
260 return FReturnedRefCountValue{uint32(++NumRefs)};
261 }
263 {
264 uint32 Refs = uint32(--NumRefs);
265 if(Refs == 0)
266 {
267 delete this;
268 }
269 return Refs;
270 }
272 {
273 return uint32(NumRefs);
274 }
275private:
276 mutable int32 NumRefs;
277};
278
282class FThreadSafeRefCountedObject : UE::Private::TTransactionalAtomicRefCount<uint32>
283{
284public:
286
289
291 {
292 check(Super::GetRefCount() == 0);
293 }
294
296 {
297 return FReturnedRefCountValue{Super::AddRef<DeleteThis>()};
298 }
299
301 {
302 return Super::Release<DeleteThis>();
303 }
304
306 {
307 return Super::GetRefCount();
308 }
309
310private:
311 using Super = UE::Private::TTransactionalAtomicRefCount<uint32>;
312
313 static void DeleteThis(const Super* This)
314 {
315 delete static_cast<const FThreadSafeRefCountedObject*>(This);
316 }
317};
318
324{
326 NotThreadSafe = 0,
327
329 ThreadSafe = 1
330};
331
354template <typename T, ERefCountingMode Mode = ERefCountingMode::ThreadSafe>
356
360template <typename T>
361class TRefCountingMixin<T, ERefCountingMode::ThreadSafe> : UE::Private::TTransactionalAtomicRefCount<uint32>
362{
363public:
364 TRefCountingMixin() = default;
365
368
370 {
371 return FReturnedRefCountValue{Super::template AddRef<StaticDestroyMixin>()};
372 }
373
375 {
376 return Super::template Release<StaticDestroyMixin>();
377 }
378
380 {
381 return Super::GetRefCount();
382 }
383
384 static void StaticDestroyObject(const T* Obj)
385 {
386 delete Obj;
387 }
388
389private:
390 using Super = UE::Private::TTransactionalAtomicRefCount<uint32>;
391
392 static void StaticDestroyMixin(const Super* This)
393 {
394 // This static_cast is traversing two levels of the class hierarchy.
395 // We are casting from our parent class (TTransactionalAtomicRefCount*) to our subclass (T*).
396 T::StaticDestroyObject(static_cast<const T*>(This));
397 }
398};
399
403template <typename T>
405{
406public:
407 TRefCountingMixin() = default;
408
411
413 {
414 return FReturnedRefCountValue{++RefCount};
415 }
416
418 {
419 checkSlow(RefCount > 0);
420
421 if (--RefCount == 0)
422 {
423 StaticDestroyMixin(this);
424 }
425
426 // Note: TRefCountPtr doesn't use the return value
427 return 0;
428 }
429
431 {
432 return RefCount;
433 }
434
435 static void StaticDestroyObject(const T* Obj)
436 {
437 delete Obj;
438 }
439
440private:
441 static void StaticDestroyMixin(const TRefCountingMixin* This)
442 {
443 T::StaticDestroyObject(static_cast<const T*>(This));
444 }
445
446 mutable uint32 RefCount;
447};
448
452template<typename ReferencedType>
454{
455 using ReferenceType = ReferencedType*;
456
457public:
459
461 {
462 Reference = InReference;
463 if (Reference && bAddRef)
464 {
465 Reference->AddRef();
466 }
467 }
468
470 {
471 Reference = Copy.Reference;
472 if (Reference)
473 {
474 Reference->AddRef();
475 }
476 }
477
478 template<typename CopyReferencedType>
480 {
481 Reference = static_cast<ReferencedType*>(Copy.GetReference());
482 if (Reference)
483 {
484 Reference->AddRef();
485 }
486 }
487
489 {
490 Reference = Move.Reference;
491 Move.Reference = nullptr;
492 }
493
494 template<typename MoveReferencedType>
496 {
497 Reference = static_cast<ReferencedType*>(Move.GetReference());
498 Move.Reference = nullptr;
499 }
500
502 {
503 if (Reference)
504 {
505 Reference->Release();
506 }
507 }
508
510 {
511 if (Reference != InReference)
512 {
513 // Call AddRef before Release, in case the new reference is the same as the old reference.
514 ReferencedType* OldReference = Reference;
515 Reference = InReference;
516 if (Reference)
517 {
518 Reference->AddRef();
519 }
520 if (OldReference)
521 {
522 OldReference->Release();
523 }
524 }
525 return *this;
526 }
527
529 {
530 return *this = InPtr.Reference;
531 }
532
533 template<typename CopyReferencedType>
538
540 {
541 if (this != &InPtr)
542 {
543 ReferencedType* OldReference = Reference;
544 Reference = InPtr.Reference;
545 InPtr.Reference = nullptr;
546 if (OldReference)
547 {
548 OldReference->Release();
549 }
550 }
551 return *this;
552 }
553
554 template<typename MoveReferencedType>
556 {
557 // InPtr is a different type (or we would have called the other operator), so we need not test &InPtr != this
558 ReferencedType* OldReference = Reference;
559 Reference = InPtr.Reference;
560 InPtr.Reference = nullptr;
561 if (OldReference)
562 {
563 OldReference->Release();
564 }
565 return *this;
566 }
567
569 {
570 return Reference;
571 }
572
573 UE_FORCEINLINE_HINT operator ReferenceType() const
574 {
575 return Reference;
576 }
577
579 {
580 *this = nullptr;
581 return &Reference;
582 }
583
585 {
586 return Reference;
587 }
588
590 {
591 return InReference.Reference != nullptr;
592 }
593
595 {
596 return Reference != nullptr;
597 }
598
600 {
601 *this = nullptr;
602 }
603
605 {
606 uint32 Result = 0;
607 if (Reference)
608 {
609 Result = Reference->GetRefCount();
610 check(Result > 0); // you should never have a zero ref count if there is a live ref counted pointer (*this is live)
611 }
612 return Result;
613 }
614
615 inline void Swap(TRefCountPtr& InPtr) // this does not change the reference count, and so is faster
616 {
617 ReferencedType* OldReference = Reference;
618 Reference = InPtr.Reference;
619 InPtr.Reference = OldReference;
620 }
621
623 {
624 ReferenceType PtrReference = Reference;
625 Ar << PtrReference;
626 if(Ar.IsLoading())
627 {
628 *this = PtrReference;
629 }
630 }
631
632private:
633 ReferencedType* Reference = nullptr;
634
635 template <typename OtherType>
636 friend class TRefCountPtr;
637
638public:
640 {
641 return GetReference() == B.GetReference();
642 }
643
645 {
646 return GetReference() == B;
647 }
648};
649
651
652#if !PLATFORM_COMPILER_HAS_GENERATED_COMPARISON_OPERATORS
653template<typename ReferencedType>
655{
656 return A == B.GetReference();
657}
658#endif
659
660template<typename ReferencedType>
665
666
667template<typename ReferencedType>
669{
670 Ptr.Serialize(Ar);
671 return Ar;
672}
673
674template <
675 typename T,
676 typename... TArgs
677 UE_REQUIRES(!std::is_array_v<T>)
678>
680{
681 T* NewUnwrappedObject = new T(Forward<TArgs>(Args)...);
682
683 // Set up `NewObject` in the open to avoid unnecessary (but harmless) OnAbort tasks.
686 {
688 };
689 return NewObject;
690}
#define checkSlow(expr)
Definition AssertionMacros.h:332
#define check(expr)
Definition AssertionMacros.h:314
#define UE_DEPRECATED(Version, Message)
Definition CoreMiscDefines.h:302
FPlatformTypes::int32 int32
A 32-bit signed integer.
Definition Platform.h:1125
#define UE_FORCEINLINE_HINT
Definition Platform.h:723
UE_FORCEINLINE_HINT TSharedRef< CastToType, Mode > StaticCastSharedRef(TSharedRef< CastFromType, Mode > const &InSharedRef)
Definition SharedPointer.h:127
#define ALIAS_TEMPLATE_TYPE_LAYOUT(TemplatePrefix, T, Alias)
Definition MemoryLayout.h:762
UE_FORCEINLINE_HINT uint32 GetTypeHash(const TRefCountPtr< ReferencedType > &InPtr)
Definition RefCounting.h:661
UE_FORCEINLINE_HINT bool operator==(ReferencedType *A, const TRefCountPtr< ReferencedType > &B)
Definition RefCounting.h:654
FArchive & operator<<(FArchive &Ar, TRefCountPtr< ReferencedType > &Ptr)
Definition RefCounting.h:668
TRefCountPtr< T > MakeRefCount(TArgs &&... Args)
Definition RefCounting.h:679
ERefCountingMode
Definition RefCounting.h:324
#define UE_REQUIRES(...)
Definition Requires.h:86
void Move(T &A, typename TMoveSupportTraits< T >::Copy B)
Definition UnrealTemplate.h:24
uint8_t uint8
Definition binka_ue_file_header.h:8
uint32_t uint32
Definition binka_ue_file_header.h:6
Definition Archive.h:1208
UE_FORCEINLINE_HINT bool IsLoading() const
Definition Archive.h:236
Definition RefCounting.h:213
FRefCountBase()=default
uint32 GetRefCount() const
Definition RefCounting.h:231
FReturnedRefCountValue AddRef() const
Definition RefCounting.h:221
FRefCountBase(const FRefCountBase &Rhs)=delete
virtual ~FRefCountBase()=default
uint32 Release() const
Definition RefCounting.h:226
FRefCountBase & operator=(const FRefCountBase &Rhs)=delete
Definition RefCounting.h:252
virtual ~FRefCountedObject()
Definition RefCounting.h:255
FRefCountedObject()
Definition RefCounting.h:254
FReturnedRefCountValue AddRef() const
Definition RefCounting.h:258
FRefCountedObject & operator=(const FRefCountedObject &Rhs)=delete
uint32 Release() const
Definition RefCounting.h:262
FRefCountedObject(const FRefCountedObject &Rhs)=delete
uint32 GetRefCount() const
Definition RefCounting.h:271
Definition RefCounting.h:283
virtual ~FThreadSafeRefCountedObject()
Definition RefCounting.h:290
FReturnedRefCountValue AddRef() const
Definition RefCounting.h:295
uint32 GetRefCount() const
Definition RefCounting.h:305
FThreadSafeRefCountedObject(const FThreadSafeRefCountedObject &Rhs)=delete
FThreadSafeRefCountedObject & operator=(const FThreadSafeRefCountedObject &Rhs)=delete
uint32 Release() const
Definition RefCounting.h:300
Definition RefCounting.h:196
virtual uint32 GetRefCount() const =0
virtual ~IRefCountedObject()
Definition RefCounting.h:198
virtual uint32 Release() const =0
virtual FReturnedRefCountValue AddRef() const =0
Definition RefCounting.h:454
TRefCountPtr(TRefCountPtr< MoveReferencedType > &&Move)
Definition RefCounting.h:495
void Swap(TRefCountPtr &InPtr)
Definition RefCounting.h:615
UE_FORCEINLINE_HINT bool operator==(const TRefCountPtr &B) const
Definition RefCounting.h:639
UE_FORCEINLINE_HINT TRefCountPtr & operator=(const TRefCountPtr< CopyReferencedType > &InPtr)
Definition RefCounting.h:534
uint32 GetRefCount()
Definition RefCounting.h:604
TRefCountPtr(TRefCountPtr &&Move)
Definition RefCounting.h:488
void Serialize(FArchive &Ar)
Definition RefCounting.h:622
TRefCountPtr(const TRefCountPtr &Copy)
Definition RefCounting.h:469
ReferencedType ** GetInitReference()
Definition RefCounting.h:578
TRefCountPtr & operator=(ReferencedType *InReference)
Definition RefCounting.h:509
UE_FORCEINLINE_HINT TRefCountPtr & operator=(const TRefCountPtr &InPtr)
Definition RefCounting.h:528
TRefCountPtr(const TRefCountPtr< CopyReferencedType > &Copy)
Definition RefCounting.h:479
TRefCountPtr & operator=(TRefCountPtr< MoveReferencedType > &&InPtr)
Definition RefCounting.h:555
TRefCountPtr & operator=(TRefCountPtr &&InPtr)
Definition RefCounting.h:539
UE_FORCEINLINE_HINT ReferencedType * GetReference() const
Definition RefCounting.h:584
UE_FORCEINLINE_HINT ReferencedType * operator->() const
Definition RefCounting.h:568
UE_FORCEINLINE_HINT bool operator==(ReferencedType *B) const
Definition RefCounting.h:644
UE_FORCEINLINE_HINT friend bool IsValidRef(const TRefCountPtr &InReference)
Definition RefCounting.h:589
UE_FORCEINLINE_HINT bool IsValid() const
Definition RefCounting.h:594
TRefCountPtr(ReferencedType *InReference, bool bAddRef=true)
Definition RefCounting.h:460
UE_FORCEINLINE_HINT void SafeRelease()
Definition RefCounting.h:599
~TRefCountPtr()
Definition RefCounting.h:501
UE_FORCEINLINE_HINT TRefCountPtr()=default
TRefCountingMixin & operator=(const TRefCountingMixin &)=delete
TRefCountingMixin(const TRefCountingMixin &)=delete
FReturnedRefCountValue AddRef() const
Definition RefCounting.h:412
static void StaticDestroyObject(const T *Obj)
Definition RefCounting.h:435
uint32 Release() const
Definition RefCounting.h:417
uint32 GetRefCount() const
Definition RefCounting.h:430
uint32 GetRefCount() const
Definition RefCounting.h:379
FReturnedRefCountValue AddRef() const
Definition RefCounting.h:369
uint32 Release() const
Definition RefCounting.h:374
TRefCountingMixin(const TRefCountingMixin &)=delete
static void StaticDestroyObject(const T *Obj)
Definition RefCounting.h:384
TRefCountingMixin & operator=(const TRefCountingMixin &)=delete
Definition RefCounting.h:355
@ Release
Definition PhysicsPublic.h:123
Definition PackageReader.cpp:44
Definition RefCounting.h:29
FReturnedRefCountValue & operator=(FReturnedRefCountValue &&Other)=default
FReturnedRefCountValue(FReturnedRefCountValue &&Other)=default
FReturnedRefCountValue(const FReturnedRefCountValue &Other)=default
FReturnedRefCountValue & operator=(const FReturnedRefCountValue &Other)=default
void CheckAtLeast(uint32 N) const
Definition RefCounting.h:45
FReturnedRefCountValue(uint32 InRefCount)
Definition RefCounting.h:30