Broom 1.0.0
A thread-local C++ Garbage Collector
Loading...
Searching...
No Matches
garbage-collector.h
Go to the documentation of this file.
1#pragma once
2
3#include <absl/container/btree_set.h>
4
5#include <list>
6#include <memory>
7#include <type_traits>
8
9#include "include/broom.h"
10#include "src/allocator.h"
11#include "src/globals.h"
12#include "src/macros.h"
13#include "src/roots.h"
14
15namespace broom {
16// A node representing each managed pointer in the GC. It comprises of the base
17// pointer to a managed allocation and, optionally, the destructor to run upon
18// its collection.
19class GcNode {
20 private:
21 static bool SetCompareFn(const GcNode& n1, const GcNode& n2) {
22 return SafeCast<MemoryAddress>(n1.Pointer()) <
23 SafeCast<MemoryAddress>(n2.Pointer());
24 }
25
26 public:
27 explicit GcNode(const void* pointer, Destructor destructor)
28 : pointer_(pointer), destructor_(destructor) {}
29 explicit GcNode(const void* pointer)
30 : pointer_(pointer), destructor_(nullptr) {}
31 const void* Pointer() const { return pointer_; }
32 bool HasDestructor() const { return destructor_ != nullptr; }
33 void RunDestructor() const { destructor_(pointer_); }
34
35 using SetCompare =
36 std::integral_constant<decltype(&SetCompareFn), &SetCompareFn>;
37
38 private:
39 const void* pointer_;
40 Destructor destructor_;
41 friend class GarbageCollector;
42};
43
44// The thread-local Broom garbage collector state.
46 private:
47 static constexpr auto cmp_helper = []<typename T>(const T& n1, const T& n2) {
48 return SafeCast<MemoryAddress>(n1.Pointer()) <
49 SafeCast<MemoryAddress>(n2.Pointer());
50 };
51
52 public:
53 // TODO(gc): Consider a swiss table instead.
54 using GcSet = absl::btree_set<GcNode, GcNode::SetCompare>;
56 absl::btree_set<ExternalRoot, ExternalRoot::SetCompare>;
57
59 : configuration_(config),
60 allocator_(allocator),
61 root_visitor_(RootVisitor(allocator, this)) {
62 BENSURE_NE(allocator_, nullptr, "Failed to allocate the allocator");
63 }
67 explicit GarbageCollector(Allocator* allocator)
68 : GarbageCollector({}, allocator) {}
70 BASSERT_EQ(external_roots_.size(), 0,
71 "Must deallocate all external roots, have: {}",
72 external_roots_.size());
73 BASSERT_EQ(precise_roots_.size(), 0,
74 "Must deallocate all precise roots, have {}",
75 precise_roots_.size());
76 BASSERT_EQ(to_collect_.size(), 0, "Exited during collection.");
77
78 // If we don't need to run the destructors, we don't need to iterate over
79 // the objects that are alive since deleting the allocator will unmap all
80 // the mappings it owns.
81 if (configuration_.run_destructors_on_shutdown) {
82 for (auto it = alive_.begin(); it != alive_.end(); ++it) {
83 if (it->HasDestructor()) {
84 it->RunDestructor();
85 }
86 }
87 }
88 delete allocator_;
89 }
90
92 AtomicMark();
94 }
96 AtomicMark();
98 }
99
100 void MarkAlive(const void* pointer) {
101 auto it = std::ranges::find(to_collect_, pointer, &GcNode::pointer_);
102 // If we can't find it, that means it must already be alive.
103 if (it == to_collect_.end()) return;
104 alive_.insert(*it);
105 to_collect_.erase(it);
106 }
108 external_roots_.emplace(std::move(root));
109 }
111 external_roots_.erase(std::move(root));
112 }
114 PreciseRoot& root_ref = precise_roots_.emplace_front(root);
115 auto it = precise_roots_.begin();
116 root_ref.UpdateReference(it);
117 return SafeCast<void*>(&*it);
118 }
121 precise_roots_.erase(root->GetReference());
122 }
123 void AtomicMark();
124 void AtomicSweep();
125
127 Destructor destructor = nullptr) {
128 alive_.emplace(GcNode{pointer, destructor});
129 }
130
131 BROOM_INLINE void Pin(const void* pointer, uint32_t how_many = 1) {
132 PinBasePointer(pointer, how_many);
133 }
134 BROOM_INLINE void Unpin(const void* pointer) { UnpinBasePointer(pointer); }
135
136 BROOM_INLINE void DisableAutomatedCollection() { collect_ = false; }
137 BROOM_INLINE void EnableAutomatedCollection() { collect_ = true; }
138
141 // clang-format off
142 // In the current version of Broom, this will never happen. We currently
143 // don't distinguish between slow and fast collection.
146 } else if (collect_ && pressure == MemoryPressure::kCritical) BROOM_LIKELY {
148 }
149 // clang-format on
150 }
151#ifdef BROOM_FOR_TESTING
152 // See include/broom.h for the implementation. We currently only use this for
153 // testing, however we might want to expose the GarbageCollector interface
154 // publicly at some point.
155 template <typename T, typename... Args>
156 T* Allocate(size_t count, Args&&... args) {
159 }
160#endif
161
162 // Checks for memory pressure and calls into the allocator. This function does
163 // **not** register the new allocation with the GC, it is intended to be used
164 // by the API functions.
167 return allocator_->Allocate(size);
168 }
169
170#ifdef BROOM_FOR_TESTING
171 // Only used during testing.
172 const GcSet& Alive() const { return alive_; }
173#endif
174
175 private:
176 BROOM_INLINE void PinBasePointer(const void* pointer, uint32_t how_many) {
178 }
179 BROOM_INLINE void UnpinBasePointer(const void* pointer) {
181 }
182
183 BROOM_UNUSED broom_configuration configuration_;
184 Allocator* allocator_;
185 ExternalRootSet external_roots_;
186 PreciseRoot::List precise_roots_;
187 RootVisitor root_visitor_;
188 GcSet to_collect_;
189 GcSet alive_;
190 bool collect_ = true;
191};
192} // namespace broom
#define __BROOM_IMPL_ALLOCATE_T(T, RegisterFunction, AllocateFunction, args)
Definition broom.h:41
static void UnpinBasePointer(const void *pointer)
Definition allocator.h:286
MemoryPressure CalculateMemoryPressure() const
Definition allocator.h:261
static void PinBasePointer(const void *pointer, uint32_t how_many)
Definition allocator.h:281
virtual void * Allocate(size_t requested_size)=0
BROOM_INLINE void RegisterExternalRoot(ExternalRoot root)
BROOM_INLINE void RegisterPointer(const void *pointer, Destructor destructor=nullptr)
absl::btree_set< GcNode, GcNode::SetCompare > GcSet
void * AllocateRawDontRegister(size_t size)
BROOM_INLINE void EnableAutomatedCollection()
void MarkAlive(const void *pointer)
BROOM_INLINE void UnregisterExternalRoot(ExternalRoot root)
BROOM_INLINE void DisableAutomatedCollection()
GarbageCollector(Allocator *allocator)
GarbageCollector(broom_configuration config)
BROOM_INLINE void * RegisterPreciseRoot(PreciseRoot root)
absl::btree_set< ExternalRoot, ExternalRoot::SetCompare > ExternalRootSet
GarbageCollector(broom_configuration config, Allocator *allocator)
BROOM_INLINE void Pin(const void *pointer, uint32_t how_many=1)
BROOM_INLINE void Unpin(const void *pointer)
BROOM_INLINE void CollectIfNecessary()
BROOM_INLINE void UnregisterPreciseRoot(void *type_erased_root)
GcNode(const void *pointer)
bool HasDestructor() const
GcNode(const void *pointer, Destructor destructor)
void RunDestructor() const
const void * Pointer() const
std::integral_constant< decltype(&SetCompareFn), &SetCompareFn > SetCompare
#define BASSERT_EQ(lhs, rhs, m,...)
Definition macros.h:58
#define BROOM_UNLIKELY
Definition macros.h:8
#define BROOM_LIKELY
Definition macros.h:9
#define BROOM_UNUSED
Definition macros.h:109
#define BENSURE_NE(lhs, rhs, m,...)
Definition macros.h:36
std::add_pointer< void(const void *)>::type Destructor
Definition broom.h:245
MemoryPressure
Definition allocator.h:225
std::queue< T, broom::deque< T > > queue
Definition broom-queue.h:12
bool run_destructors_on_shutdown
Definition broom.h:72