Halide
IntrusivePtr.h
Go to the documentation of this file.
1 #ifndef HALIDE_INTRUSIVE_PTR_H
2 #define HALIDE_INTRUSIVE_PTR_H
3 
4 /** \file
5  *
6  * Support classes for reference-counting via intrusive shared
7  * pointers.
8  */
9 
10 #include <atomic>
11 #include <cstdlib>
12 
13 #include "runtime/HalideRuntime.h" // for HALIDE_ALWAYS_INLINE
14 
15 namespace Halide {
16 namespace Internal {
17 
18 /** A class representing a reference count to be used with IntrusivePtr */
19 class RefCount {
20  std::atomic<int> count;
21 
22 public:
23  RefCount() noexcept
24  : count(0) {
25  }
26  int increment() {
27  return ++count;
28  } // Increment and return new value
29  int decrement() {
30  return --count;
31  } // Decrement and return new value
32  bool is_const_zero() const {
33  return count == 0;
34  }
35 };
36 
37 /**
38  * Because in this header we don't yet know how client classes store
39  * their RefCount (and we don't want to depend on the declarations of
40  * the client classes), any class that you want to hold onto via one
41  * of these must provide implementations of ref_count and destroy,
42  * which we forward-declare here.
43  *
44  * E.g. if you want to use IntrusivePtr<MyClass>, then you should
45  * define something like this in MyClass.cpp (assuming MyClass has
46  * a field: mutable RefCount ref_count):
47  *
48  * template<> RefCount &ref_count<MyClass>(const MyClass *c) noexcept {return c->ref_count;}
49  * template<> void destroy<MyClass>(const MyClass *c) {delete c;}
50  */
51 // @{
52 template<typename T>
53 RefCount &ref_count(const T *t) noexcept;
54 template<typename T>
55 void destroy(const T *t);
56 // @}
57 
58 /** Intrusive shared pointers have a reference count (a
59  * RefCount object) stored in the class itself. This is perhaps more
60  * efficient than storing it externally, but more importantly, it
61  * means it's possible to recover a reference-counted handle from the
62  * raw pointer, and it's impossible to have two different reference
63  * counts attached to the same raw object. Seeing as we pass around
64  * raw pointers to concrete IRNodes and Expr's interchangeably, this
65  * is a useful property.
66  */
67 template<typename T>
68 struct IntrusivePtr {
69 private:
70  void incref(T *p) {
71  if (p) {
72  ref_count(p).increment();
73  }
74  }
75 
76  void decref(T *p) {
77  if (p) {
78  // Note that if the refcount is already zero, then we're
79  // in a recursive destructor due to a self-reference (a
80  // cycle), where the ref_count has been adjusted to remove
81  // the counts due to the cycle. The next line then makes
82  // the ref_count negative, which prevents actually
83  // entering the destructor recursively.
84  if (ref_count(p).decrement() == 0) {
85  destroy(p);
86  }
87  }
88  }
89 
90 protected:
91  T *ptr = nullptr;
92 
93 public:
94  /** Access the raw pointer in a variety of ways.
95  * Note that a "const IntrusivePtr<T>" is not the same thing as an
96  * IntrusivePtr<const T>. So the methods that return the ptr are
97  * const, despite not adding an extra const to T. */
98  // @{
99  T *get() const {
100  return ptr;
101  }
102 
103  T &operator*() const {
104  return *ptr;
105  }
106 
107  T *operator->() const {
108  return ptr;
109  }
110  // @}
111 
113  decref(ptr);
114  }
115 
117  IntrusivePtr() = default;
118 
121  : ptr(p) {
122  incref(ptr);
123  }
124 
126  IntrusivePtr(const IntrusivePtr<T> &other) noexcept
127  : ptr(other.ptr) {
128  incref(ptr);
129  }
130 
132  IntrusivePtr(IntrusivePtr<T> &&other) noexcept
133  : ptr(other.ptr) {
134  other.ptr = nullptr;
135  }
136 
137  // NOLINTNEXTLINE(bugprone-unhandled-self-assignment)
139  // Same-ptr but different-this happens frequently enough
140  // to check for (see https://github.com/halide/Halide/pull/5412)
141  if (other.ptr == ptr) {
142  return *this;
143  }
144  // Other can be inside of something owned by this, so we
145  // should be careful to incref other before we decref
146  // ourselves.
147  T *temp = other.ptr;
148  incref(temp);
149  decref(ptr);
150  ptr = temp;
151  return *this;
152  }
153 
155  std::swap(ptr, other.ptr);
156  return *this;
157  }
158 
159  /* Handles can be null. This checks that. */
161  bool defined() const {
162  return ptr != nullptr;
163  }
164 
165  /* Check if two handles point to the same ptr. This is
166  * equality of reference, not equality of value. */
168  bool same_as(const IntrusivePtr &other) const {
169  return ptr == other.ptr;
170  }
171 
173  bool operator<(const IntrusivePtr<T> &other) const {
174  return ptr < other.ptr;
175  }
176 };
177 
178 } // namespace Internal
179 } // namespace Halide
180 
181 #endif
Halide::Internal::IntrusivePtr::IntrusivePtr
HALIDE_ALWAYS_INLINE IntrusivePtr()=default
Halide::Internal::RefCount::is_const_zero
bool is_const_zero() const
Definition: IntrusivePtr.h:32
Halide::Internal::IntrusivePtr::operator=
IntrusivePtr< T > & operator=(IntrusivePtr< T > &&other) noexcept
Definition: IntrusivePtr.h:154
Halide::Internal::IntrusivePtr::IntrusivePtr
HALIDE_ALWAYS_INLINE IntrusivePtr(T *p)
Definition: IntrusivePtr.h:120
Halide::Internal::RefCount::increment
int increment()
Definition: IntrusivePtr.h:26
Halide::Internal::destroy
void destroy(const T *t)
Halide::Internal::IntrusivePtr::~IntrusivePtr
~IntrusivePtr()
Definition: IntrusivePtr.h:112
Halide::Internal::IntrusivePtr::IntrusivePtr
HALIDE_ALWAYS_INLINE IntrusivePtr(IntrusivePtr< T > &&other) noexcept
Definition: IntrusivePtr.h:132
Halide::Internal::IntrusivePtr
Intrusive shared pointers have a reference count (a RefCount object) stored in the class itself.
Definition: IntrusivePtr.h:68
Halide::Internal::IntrusivePtr::operator*
T & operator*() const
Definition: IntrusivePtr.h:103
Halide
This file defines the class FunctionDAG, which is our representation of a Halide pipeline,...
Definition: AbstractGenerator.h:19
Halide::LinkageType::Internal
@ Internal
Not visible externally, similar to 'static' linkage in C.
Halide::Internal::IntrusivePtr::ptr
T * ptr
Definition: IntrusivePtr.h:91
Halide::Internal::ref_count
RefCount & ref_count(const T *t) noexcept
Because in this header we don't yet know how client classes store their RefCount (and we don't want t...
Halide::Internal::RefCount::decrement
int decrement()
Definition: IntrusivePtr.h:29
HALIDE_ALWAYS_INLINE
#define HALIDE_ALWAYS_INLINE
Definition: HalideRuntime.h:40
Halide::Internal::IntrusivePtr::IntrusivePtr
HALIDE_ALWAYS_INLINE IntrusivePtr(const IntrusivePtr< T > &other) noexcept
Definition: IntrusivePtr.h:126
Halide::Internal::IntrusivePtr::operator=
IntrusivePtr< T > & operator=(const IntrusivePtr< T > &other)
Definition: IntrusivePtr.h:138
Halide::Internal::IntrusivePtr::operator->
T * operator->() const
Definition: IntrusivePtr.h:107
Halide::Internal::IntrusivePtr::operator<
HALIDE_ALWAYS_INLINE bool operator<(const IntrusivePtr< T > &other) const
Definition: IntrusivePtr.h:173
Halide::Internal::IntrusivePtr::get
T * get() const
Access the raw pointer in a variety of ways.
Definition: IntrusivePtr.h:99
HalideRuntime.h
Halide::Internal::RefCount::RefCount
RefCount() noexcept
Definition: IntrusivePtr.h:23
Halide::Internal::IntrusivePtr::defined
HALIDE_ALWAYS_INLINE bool defined() const
Definition: IntrusivePtr.h:161
Halide::Internal::RefCount
A class representing a reference count to be used with IntrusivePtr.
Definition: IntrusivePtr.h:19
Halide::Internal::IntrusivePtr::same_as
HALIDE_ALWAYS_INLINE bool same_as(const IntrusivePtr &other) const
Definition: IntrusivePtr.h:168