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