Halide 16.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:
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// @{
52template<typename T>
53RefCount &ref_count(const T *t) noexcept;
54template<typename T>
55void 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 */
67template<typename T>
69private:
70 void incref(T *p) {
71 if (p) {
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
90protected:
91 T *ptr = nullptr;
92
93public:
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
127 : ptr(other.ptr) {
128 incref(ptr);
129 }
130
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
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.
Expr cast(Expr a)
Cast an expression to the halide type corresponding to the C++ type T.
Definition IROperator.h:358
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
IntrusivePtr< T > & operator=(const IntrusivePtr< T > &other)