Halide  12.0.1
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 };
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
This file declares the routines used by Halide internally in its runtime.
#define HALIDE_ALWAYS_INLINE
Definition: HalideRuntime.h:38
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:68
HALIDE_ALWAYS_INLINE IntrusivePtr()=default
HALIDE_ALWAYS_INLINE IntrusivePtr(T *p)
Definition: IntrusivePtr.h:120
HALIDE_ALWAYS_INLINE bool defined() const
Definition: IntrusivePtr.h:161
IntrusivePtr< T > & operator=(IntrusivePtr< T > &&other) noexcept
Definition: IntrusivePtr.h:154
HALIDE_ALWAYS_INLINE bool same_as(const IntrusivePtr &other) const
Definition: IntrusivePtr.h:168
IntrusivePtr< T > & operator=(const IntrusivePtr< T > &other)
Definition: IntrusivePtr.h:138
HALIDE_ALWAYS_INLINE bool operator<(const IntrusivePtr< T > &other) const
Definition: IntrusivePtr.h:173
HALIDE_ALWAYS_INLINE IntrusivePtr(const IntrusivePtr< T > &other) noexcept
Definition: IntrusivePtr.h:126
HALIDE_ALWAYS_INLINE IntrusivePtr(IntrusivePtr< T > &&other) noexcept
Definition: IntrusivePtr.h:132
T * get() const
Access the raw pointer in a variety of ways.
Definition: IntrusivePtr.h:99