Halide 19.0.0
Halide compiler and libraries
Loading...
Searching...
No Matches
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
15namespace Halide {
16namespace Internal {
17
18/** A class representing a reference count to be used with IntrusivePtr */
19class RefCount {
20 std::atomic<int> count;
21
22public:
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// @{
55template<typename T>
56RefCount &ref_count(const T *t) noexcept;
57template<typename T>
58void 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 */
70template<typename T>
72private:
73 void incref(T *p) {
74 if (p) {
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
93protected:
94 T *ptr = nullptr;
95
96public:
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
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
A class representing a reference count to be used with IntrusivePtr.
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.
HALIDE_ALWAYS_INLINE IntrusivePtr()=default
IntrusivePtr< T > & operator=(IntrusivePtr< T > &&other) noexcept
HALIDE_ALWAYS_INLINE IntrusivePtr(T *p)
HALIDE_ALWAYS_INLINE bool defined() const
HALIDE_ALWAYS_INLINE bool same_as(const IntrusivePtr &other) const
T * get() const
Access the raw pointer in a variety of ways.
HALIDE_ALWAYS_INLINE bool operator<(const IntrusivePtr< T > &other) const
HALIDE_ALWAYS_INLINE IntrusivePtr(const IntrusivePtr< T > &other) noexcept
HALIDE_ALWAYS_INLINE IntrusivePtr(IntrusivePtr< T > &&other) noexcept
HALIDE_ALWAYS_INLINE bool is_sole_reference() const
IntrusivePtr< T > & operator=(const IntrusivePtr< T > &other)