/home/andy/git/oilshell/oil/mycpp/marksweep_heap.h
Line | Count | Source (jump to first uncovered line) |
1 | | #ifndef MARKSWEEP_HEAP_H |
2 | | #define MARKSWEEP_HEAP_H |
3 | | |
4 | | #include <unordered_set> |
5 | | #include <vector> |
6 | | |
7 | | class MarkSweepHeap; // forward decl for circular dep |
8 | | |
9 | | // The set of objects where the mark and sweep algorithm starts. Terminology: |
10 | | // to "root" an object means "add it to the root set". |
11 | | class RootSet { |
12 | | public: |
13 | | // start with 1 live frame and e.g. 32 reserved ones |
14 | 35 | explicit RootSet(int num_reserved) : num_frames_(1) { |
15 | 35 | assert(num_reserved > 1); |
16 | 0 | stack_.reserve(num_reserved); |
17 | 1.15k | for (int i = 0; i < num_reserved; ++i) { |
18 | 1.12k | stack_.emplace_back(); // Construct std::vector frame IN PLACE. |
19 | 1.12k | stack_.back().reserve(16); // Reserve 16 rooted variables per frame. |
20 | 1.12k | } |
21 | 35 | } |
22 | | |
23 | | // Called on function entry |
24 | 425 | void PushScope() { |
25 | | // Construct more std::vector frames if necessary. We reuse vectors to |
26 | | // avoid constructing one on every function call. |
27 | 425 | int num_constructed = stack_.size(); |
28 | 425 | if (num_frames_ >= num_constructed) { |
29 | 70 | stack_.emplace_back(); |
30 | 70 | stack_.back().reserve(16); |
31 | | #if 0 |
32 | | num_constructed = roots_.size(); |
33 | | log("num_frames_ %d, num_constructed %d", num_frames_, num_constructed); |
34 | | assert(num_frames_ + 1 == num_constructed); |
35 | | #endif |
36 | 70 | } |
37 | | |
38 | 425 | num_frames_++; |
39 | | // log("PushScope -> %d", num_frames_); |
40 | 425 | } |
41 | | |
42 | | // Called on function exit |
43 | 355 | void PopScope() { |
44 | | // Remove all roots owned by the top frame. We're REUSING frames, so not |
45 | | // calling vector<>::pop(). |
46 | 355 | stack_[num_frames_ - 1].clear(); |
47 | 355 | num_frames_--; |
48 | | // log("PopScope -> %d", num_frames_); |
49 | 355 | } |
50 | | |
51 | | // Called when returning a value (except in trivial passthrough case) |
52 | 10.3k | void RootOnReturn(Obj* root) { |
53 | | // log("RootOnReturn %p %d", root, num_frames_); |
54 | | |
55 | 10.3k | if (root == nullptr) { // No reason to add it |
56 | 1 | return; |
57 | 1 | } |
58 | | // We should have 2 frames because we start with 1 for main(), and main() |
59 | | // itself can't return GC objects. |
60 | 10.3k | assert(num_frames_ > 1); |
61 | | |
62 | | // Owned by the frame BELOW |
63 | 0 | stack_[num_frames_ - 2].push_back(root); |
64 | 10.3k | } |
65 | | |
66 | | // Called in 2 situations: |
67 | | // - the "leaf" Allocate(), which does not have a RootingScope |
68 | | // - when catching exceptions: |
69 | | // catch (IOError e) { gHeap.RootInCurrentFrame(e); } |
70 | 3 | void RootInCurrentFrame(Obj* root) { |
71 | 3 | if (root == nullptr) { // No reason to add it |
72 | 0 | return; |
73 | 0 | } |
74 | 3 | assert(num_frames_ > 0); |
75 | 0 | stack_[num_frames_ - 1].push_back(root); |
76 | 3 | } |
77 | | |
78 | | // For testing |
79 | 13 | int NumFrames() { |
80 | 13 | return num_frames_; |
81 | 13 | } |
82 | | |
83 | | // Calculate size of root set, for unit tests only. |
84 | 11 | int NumRoots() { |
85 | 11 | int result = 0; |
86 | 37 | for (int i = 0; i < num_frames_; ++i) { |
87 | 26 | result += stack_[i].size(); |
88 | 26 | } |
89 | 11 | return result; |
90 | 11 | } |
91 | | |
92 | | void MarkRoots(MarkSweepHeap* heap); |
93 | | |
94 | | // A stack of frames that's updated in parallel the call stack. |
95 | | // This representation is appropriate since multiple stack frames are "in |
96 | | // play" at once. That is, RootOnReturn() may mutate root_set_[1] while |
97 | | // root_set_[2] is being pushed/popped/modified. |
98 | | std::vector<std::vector<Obj*>> stack_; |
99 | | int num_frames_ = 0; // frames 0 to N-1 are valid |
100 | | }; |
101 | | |
102 | | class MarkSweepHeap { |
103 | | public: |
104 | | // reserve 32 frames to start |
105 | 32 | MarkSweepHeap() : root_set_(32) { |
106 | 32 | } |
107 | | |
108 | | void Init(); // use default threshold |
109 | | void Init(int gc_threshold); |
110 | | |
111 | | // |
112 | | // OLD Local Var Rooting |
113 | | // |
114 | | |
115 | 65.5k | void PushRoot(Obj** p) { |
116 | 65.5k | roots_.push_back(p); |
117 | 65.5k | } |
118 | | |
119 | 65.5k | void PopRoot() { |
120 | 65.5k | roots_.pop_back(); |
121 | 65.5k | } |
122 | | |
123 | | // |
124 | | // NEW Return Value Rooting |
125 | | // |
126 | | |
127 | 317 | void RootOnReturn(Obj* root) { |
128 | 317 | root_set_.RootOnReturn(root); |
129 | 317 | } |
130 | | |
131 | 3 | void RootInCurrentFrame(Obj* root) { |
132 | 3 | root_set_.RootInCurrentFrame(root); |
133 | 3 | } |
134 | | |
135 | | void* Allocate(size_t num_bytes); |
136 | | void* Reallocate(void* p, size_t num_bytes); |
137 | | int Collect(); |
138 | | void MarkObjects(Obj* obj); |
139 | | void Sweep(); |
140 | | |
141 | | // Cleanup at the end of main() to remain ASAN-safe |
142 | | void CleanProcessExit(); |
143 | | |
144 | | // Faster exit |
145 | | void FastProcessExit(); |
146 | | |
147 | | void Report(); |
148 | | |
149 | | bool is_initialized_ = true; // mark/sweep doesn't need to be initialized |
150 | | |
151 | | // In number of live objects, since we aren't keeping track of total bytes |
152 | | int gc_threshold_; |
153 | | |
154 | | // Cumulative stats |
155 | | int64_t num_allocated_ = 0; |
156 | | int64_t bytes_allocated_ = 0; |
157 | | int64_t num_collections_ = 0; |
158 | | int max_live_ = 0; // max # live after a collection |
159 | | |
160 | | // current stats |
161 | | int num_live_ = 0; |
162 | | // Should we keep track of sizes? |
163 | | // int64_t bytes_live_ = 0; |
164 | | |
165 | | // OLD rooting |
166 | | std::vector<Obj**> roots_; |
167 | | |
168 | | // NEW rooting |
169 | | RootSet root_set_; |
170 | | |
171 | | std::vector<void*> live_objs_; |
172 | | std::unordered_set<void*> marked_; |
173 | | |
174 | | private: |
175 | | void DoProcessExit(bool fast_exit); |
176 | | |
177 | | DISALLOW_COPY_AND_ASSIGN(MarkSweepHeap); |
178 | | }; |
179 | | |
180 | | #if defined(MARK_SWEEP) && !defined(BUMP_LEAK) |
181 | | extern MarkSweepHeap gHeap; |
182 | | #endif |
183 | | |
184 | | #endif // MARKSWEEP_HEAP_H |