-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgcptr.cpp
225 lines (188 loc) · 5.49 KB
/
gcptr.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
#include "gcptr.h"
#define NOMINMAX
#include <windows.h>
#pragma warning(disable : 4786)
#include <set>
#include <map>
#include <stdexcept>
#include <limits>
namespace
{
typedef std::set<gc_detail::pointer_base*> ptr_set;
typedef std::map<void*, gc_detail::node_t*> node_map;
struct data_t
{
data_t() : threshold(1024), allocated(0), collecting(false), current_mark(false)
{ InitializeCriticalSection(&cs); }
~data_t() { gc_collect(); DeleteCriticalSection(&cs); }
ptr_set pointers;
node_map nodes;
size_t threshold;
size_t allocated;
bool collecting;
bool current_mark;
CRITICAL_SECTION cs;
};
data_t& data() { static data_t instance; return instance; }
struct data_lock
{
data_lock() { EnterCriticalSection(&data().cs); }
~data_lock() { LeaveCriticalSection(&data().cs); }
};
void mark(gc_detail::pointer_base* ptr)
{
// Mark the node associated with the pointer and then recursively
// mark all data().pointers contained by the object pointed to.
gc_detail::node_t* node = ptr->node;
if (node && node->mark != data().current_mark)
{
node->mark = data().current_mark;
for (ptr_set::iterator i = data().pointers.begin(); i != data().pointers.end(); ++i)
{
gc_detail::pointer_base* next = *i;
if (node->contains(next))
mark(next);
}
}
}
};
namespace gc_detail
{
node_t::node_t(void* obj, int n) : object(obj), size(n), destroy(0), mark(data().current_mark)
{
}
node_t::~node_t()
{
if (object && destroy)
destroy(object);
}
bool node_t::contains(void* obj)
{
char* begin = static_cast<char*>(object);
char* end = begin + size;
return begin <= obj && obj < end;
}
pointer_base::pointer_base(node_t* node) : node(node)
{
data_lock lock;
data().pointers.insert(this);
}
pointer_base::~pointer_base()
{
data_lock lock;
data().pointers.erase(this);
}
node_t* get_node(void* obj, void (*destroy)(void*))
{
data_lock lock;
if (!obj)
return 0;
node_map::iterator i = data().nodes.find(obj);
if (i == data().nodes.end())
throw std::invalid_argument("Object was not created with new(gc)");
node_t* n = i->second;
if (n->destroy == 0)
n->destroy = destroy;
return n;
}
};
void* operator new(size_t size, const gc_detail::gc_t&)
{
data_lock lock;
bool collected = false;
if (data().threshold != std::numeric_limits<size_t>::max())
{
// Determine if we've exceeded the threshold and so should collect.
data().allocated += size;
if (data().allocated > data().threshold)
{
gc_collect();
collected = true;
}
}
// Attempt the first allocation. The standard requires new to throw
// on failure but user code may change this behavior and VC++ by default
// only returns 0. We'll catch exceptions and if we've already collected
// re-throw the exception.
void* obj = 0;
try { obj = ::operator new(size); } catch(...) { if (collected) throw; }
// If we've failed to allocate but new didn't throw an exception and we've
// not collected yet we'll collect and then re-try calling new. If new
// throws at this point we'll ignore it and let the caller handle it.
if (obj == 0 && !collected)
{
gc_collect();
obj = ::operator new(size);
}
// If we actually allocated memory with new then we need to add it to
// the node map.
if (obj != 0)
data().nodes.insert(node_map::value_type(obj, new gc_detail::node_t(obj, size)));
return obj;
}
void operator delete(void* obj, const gc_detail::gc_t&)
{
data_lock lock;
node_map::iterator i = data().nodes.find(obj);
if (i != data().nodes.end())
i->second->object = 0;
::operator delete(obj);
}
void gc_collect()
{
data_lock lock;
// During the sweep phase we'll be deleting objects that could cause
// a recursive call to 'collect' which would cause invalid results. So
// we prevent recursion here.
if (data().collecting)
return;
data().collecting = true;
// Toggle the 'current_mark' so that we can start over.
data().current_mark = !data().current_mark;
{ // Mark phase
// Loop through all of the pointers looking for 'root' pointers. A 'root'
// pointer is a pointer that's not contained within the object pointed
// to by any other pointer. When a 'root' pointer is found it is
// marked, and all the pointers referenced through the 'root' pointer
// are also marked.
for (ptr_set::iterator i = data().pointers.begin(); i != data().pointers.end(); ++i)
{
gc_detail::pointer_base* ptr = *i;
if (!ptr->node || ptr->node->mark == data().current_mark)
continue; // Don't need to check pointers that are marked.
bool root = true;
for (node_map::iterator j = data().nodes.begin(); j != data().nodes.end(); ++j)
{
gc_detail::node_t* node = j->second;
if (node->contains(ptr))
{
root = false;
break; // If any other pointer contains this pointer we're not a root.
}
}
if (root)
mark(ptr);
}
}
{ // Sweep phase
// Step through all of the nodes and delete any that are not marked.
for (node_map::iterator i = data().nodes.begin(); i != data().nodes.end(); /*nothing*/)
{
gc_detail::node_t* node = i->second;
if (node->mark != data().current_mark)
{
delete node;
i = data().nodes.erase(i);
}
else
++i;
}
}
data().collecting = false;
data().allocated = 0;
}
void gc_set_threshold(size_t bytes)
{
data_lock lock;
data().threshold = bytes;
}