cpp

Coverage Report

Created: 2022-11-10 11:34

/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