1 | #include "state.h"
|
---|
2 |
|
---|
3 | #include <stdlib.hfa>
|
---|
4 |
|
---|
5 | //general purpouse includes
|
---|
6 | #include "tools.h"
|
---|
7 |
|
---|
8 | //platform abstraction includes
|
---|
9 | #include "allocate-pool.h"
|
---|
10 |
|
---|
11 | //gc internal includes
|
---|
12 | #include "collector.h"
|
---|
13 | #include "globals.h"
|
---|
14 | #include "memory_pool.h"
|
---|
15 | #include "object_header.h"
|
---|
16 | #include "tools/worklist.h"
|
---|
17 |
|
---|
18 | void gc_state_swap(gc_state *const this);
|
---|
19 | void gc_state_sweep_roots(gc_state *const this, worklist_t* worklist);
|
---|
20 | void gc_state_clear(gc_state *const this);
|
---|
21 | void gc_state_calc_usage(gc_state *const this);
|
---|
22 |
|
---|
23 | #ifndef NDEBUG
|
---|
24 | bool gc_state_roots_match(gc_state *const this);
|
---|
25 | bool gc_state_no_from_space_ref(gc_state *const this);
|
---|
26 | #endif
|
---|
27 |
|
---|
28 | static gc_state s;
|
---|
29 |
|
---|
30 | gc_state* gc_get_state()
|
---|
31 | {
|
---|
32 | if(!s.is_initialized) ctor(&s);
|
---|
33 | return &s;
|
---|
34 | }
|
---|
35 |
|
---|
36 | void ctor(gc_state *const this)
|
---|
37 | {
|
---|
38 | this->from_code = 0;
|
---|
39 | this->to_space = NULL;
|
---|
40 | this->from_space = NULL;
|
---|
41 | this->total_space = 0;
|
---|
42 | this->used_space = 0;
|
---|
43 | ctor(&this->pools_table);
|
---|
44 |
|
---|
45 | gc_allocate_pool(this);
|
---|
46 |
|
---|
47 | this->is_initialized = true;
|
---|
48 | }
|
---|
49 |
|
---|
50 | void dtor(gc_state *const this)
|
---|
51 | {
|
---|
52 | dtor(&this->pools_table);
|
---|
53 | this->is_initialized = false;
|
---|
54 | }
|
---|
55 |
|
---|
56 | bool gc_is_in_heap(const gc_state* const this, const void* const address)
|
---|
57 | {
|
---|
58 | gc_memory_pool* target_pool = gc_pool_of(address);
|
---|
59 |
|
---|
60 | gc_memory_pool** first = cbegin(&this->pools_table);
|
---|
61 | gc_memory_pool** last = cend(&this->pools_table);
|
---|
62 | gc_memory_pool** result = find(first, &last, target_pool);
|
---|
63 | return result != last && gc_pool_is_from_space(*result);
|
---|
64 | }
|
---|
65 |
|
---|
66 | bool gc_is_in_to_space(const gc_state* const this, const void* const address)
|
---|
67 | {
|
---|
68 | gc_memory_pool* target_pool = gc_pool_of(address);
|
---|
69 |
|
---|
70 | gc_memory_pool** first = cbegin(&this->pools_table);
|
---|
71 | gc_memory_pool** last = cend(&this->pools_table);
|
---|
72 | gc_memory_pool** result = find(first, &last, target_pool);
|
---|
73 | return result != last && !gc_pool_is_from_space(*result);
|
---|
74 | }
|
---|
75 |
|
---|
76 | gc_object_header* gc_get_object_for_ref(gc_state* state, void* member)
|
---|
77 | {
|
---|
78 | volatile int stage = 0;
|
---|
79 | intptr_t target = ((intptr_t)member);
|
---|
80 | if(!gc_is_in_heap(state, member)) return NULL;
|
---|
81 | stage++;
|
---|
82 |
|
---|
83 | gc_memory_pool* pool = gc_pool_of(member);
|
---|
84 | stage++;
|
---|
85 | gc_pool_object_iterator it = gc_pool_iterator_for(pool, member);
|
---|
86 | stage++;
|
---|
87 | gc_pool_object_iterator end = end(pool);
|
---|
88 | stage++;
|
---|
89 |
|
---|
90 | while(it != end)
|
---|
91 | {
|
---|
92 | gc_object_header* object = *it;
|
---|
93 | check(object);
|
---|
94 | check( is_valid(object) );
|
---|
95 | {
|
---|
96 | intptr_t start = ((intptr_t)object);
|
---|
97 | intptr_t end = ((intptr_t)start + object->size);
|
---|
98 | if(start < target && end > target)
|
---|
99 | {
|
---|
100 | return object;
|
---|
101 | }
|
---|
102 | }
|
---|
103 | stage++;
|
---|
104 | ++it;
|
---|
105 | }
|
---|
106 |
|
---|
107 | checkf( (int) 0, "is_in_heap() and iterator_for() return inconsistent data");
|
---|
108 | abort();
|
---|
109 | return NULL;
|
---|
110 | }
|
---|
111 |
|
---|
112 | void* gc_try_allocate(gc_state* const this, size_t size)
|
---|
113 | {
|
---|
114 | gc_memory_pool* pool = this->from_space;
|
---|
115 | while(pool != (gc_memory_pool*)0)
|
---|
116 | {
|
---|
117 | if(gc_pool_size_left(pool) > size)
|
---|
118 | {
|
---|
119 | return gc_pool_allocate(pool, size, true);
|
---|
120 | }
|
---|
121 | pool = pool->next;
|
---|
122 | }
|
---|
123 |
|
---|
124 | return (void*)0;
|
---|
125 | }
|
---|
126 |
|
---|
127 | void gc_allocate_pool(gc_state *const this)
|
---|
128 | {
|
---|
129 | gc_memory_pool* old_from_space = this->from_space;
|
---|
130 | gc_memory_pool* old_to_space = this->to_space;
|
---|
131 |
|
---|
132 | this->from_space = (gc_memory_pool*)(pal_allocPool(POOL_SIZE_BYTES, 1));
|
---|
133 | this->to_space = (gc_memory_pool*)(pal_allocPool(POOL_SIZE_BYTES, 1));
|
---|
134 |
|
---|
135 | this->from_space{ POOL_SIZE_BYTES, old_from_space, this->to_space, this->from_code };
|
---|
136 | this->to_space { POOL_SIZE_BYTES, old_to_space, this->from_space, (~this->from_code) & 0x01 };
|
---|
137 |
|
---|
138 | this->total_space += gc_pool_size_used(this->from_space);
|
---|
139 |
|
---|
140 | push_back(&this->pools_table, this->from_space);
|
---|
141 | push_back(&this->pools_table, this->to_space);
|
---|
142 | }
|
---|
143 |
|
---|
144 | void gc_collect(gc_state* const this)
|
---|
145 | {
|
---|
146 | // DEBUG("collecting");
|
---|
147 | // DEBUG("previous usage " << this->used_space << " / " << this->total_space);
|
---|
148 |
|
---|
149 | worklist_t worklist;
|
---|
150 | ctor(&worklist);
|
---|
151 | gc_state_sweep_roots(this, &worklist);
|
---|
152 |
|
---|
153 | while(!empty(&worklist))
|
---|
154 | {
|
---|
155 | intptr_t* ref = back(&worklist);
|
---|
156 | pop_back(&worklist);
|
---|
157 | gc_process_reference((void**)ref, &worklist);
|
---|
158 | }
|
---|
159 |
|
---|
160 | check(gc_state_roots_match(this));
|
---|
161 | check(gc_state_no_from_space_ref(this));
|
---|
162 |
|
---|
163 | gc_state_swap(this);
|
---|
164 |
|
---|
165 | gc_state_calc_usage(this);
|
---|
166 |
|
---|
167 | if(gc_needs_collect(this)) gc_allocate_pool(this);
|
---|
168 |
|
---|
169 | // DEBUG("done");
|
---|
170 | dtor(&worklist);
|
---|
171 | }
|
---|
172 |
|
---|
173 | void gc_state_swap(gc_state* const this)
|
---|
174 | {
|
---|
175 | swap(&this->from_space, &this->to_space);
|
---|
176 |
|
---|
177 | gc_memory_pool* pool = this->to_space;
|
---|
178 | while(pool)
|
---|
179 | {
|
---|
180 | gc_reset_pool(pool);
|
---|
181 | pool = pool->next;
|
---|
182 | }
|
---|
183 |
|
---|
184 | this->from_code = (~this->from_code) & 0x01;
|
---|
185 |
|
---|
186 | #ifndef NDEBUG
|
---|
187 | {
|
---|
188 | gc_memory_pool* pool = this->from_space;
|
---|
189 | while(pool)
|
---|
190 | {
|
---|
191 | check(gc_pool_is_from_space(pool));
|
---|
192 | pool = pool->next;
|
---|
193 | }
|
---|
194 |
|
---|
195 | pool = this->to_space;
|
---|
196 | while(pool)
|
---|
197 | {
|
---|
198 | check(!gc_pool_is_from_space(pool));
|
---|
199 | pool = pool->next;
|
---|
200 | }
|
---|
201 | }
|
---|
202 | #endif
|
---|
203 | }
|
---|
204 |
|
---|
205 | void gc_state_sweep_roots(gc_state* const this, worklist_t* worklist)
|
---|
206 | {
|
---|
207 | gc_memory_pool* pool = this->from_space;
|
---|
208 | while(pool)
|
---|
209 | {
|
---|
210 | gc_pool_object_iterator it = begin(pool);
|
---|
211 | gc_pool_object_iterator end = end(pool);
|
---|
212 | for(;it != end; ++it)
|
---|
213 | {
|
---|
214 | gc_object_header* object = *it;
|
---|
215 | if(!object->root_chain) continue;
|
---|
216 |
|
---|
217 | gc_copy_object(object);
|
---|
218 |
|
---|
219 | gc_scan_object(object->forward, worklist);
|
---|
220 | }
|
---|
221 |
|
---|
222 | pool = pool->next;
|
---|
223 | }
|
---|
224 | }
|
---|
225 |
|
---|
226 | void gc_state_clear(gc_state* const this)
|
---|
227 | {
|
---|
228 | gc_memory_pool* pool = this->from_space;
|
---|
229 | while(pool)
|
---|
230 | {
|
---|
231 | gc_reset_pool(pool);
|
---|
232 | pool = pool->next;
|
---|
233 | }
|
---|
234 |
|
---|
235 | pool = this->to_space;
|
---|
236 | while(pool)
|
---|
237 | {
|
---|
238 | gc_reset_pool(pool);
|
---|
239 | pool = pool->next;
|
---|
240 | }
|
---|
241 | }
|
---|
242 |
|
---|
243 | void gc_state_calc_usage(gc_state* const this)
|
---|
244 | {
|
---|
245 | this->total_space = 0;
|
---|
246 | this->used_space = 0;
|
---|
247 |
|
---|
248 | gc_memory_pool* pool = this->from_space;
|
---|
249 | while(pool)
|
---|
250 | {
|
---|
251 | size_t size = gc_pool_size_total(pool);
|
---|
252 | size_t used = gc_pool_size_used(pool);
|
---|
253 | check(used <= size);
|
---|
254 | this->total_space += size;
|
---|
255 | this->used_space += used;
|
---|
256 |
|
---|
257 | pool = pool->next;
|
---|
258 | }
|
---|
259 | }
|
---|
260 |
|
---|
261 | #ifndef NDEBUG
|
---|
262 | bool gc_state_roots_match(gc_state* const this)
|
---|
263 | {
|
---|
264 | gc_memory_pool* pool = this->to_space;
|
---|
265 | while(pool)
|
---|
266 | {
|
---|
267 | size_t size = 0;
|
---|
268 | gc_pool_object_iterator it = begin(pool);
|
---|
269 | gc_pool_object_iterator end = end(pool);
|
---|
270 | for(;it != end; ++it)
|
---|
271 | {
|
---|
272 | gc_object_header* object = *it;
|
---|
273 | size += object->size;
|
---|
274 |
|
---|
275 | gcpointer_t* ptr = object->root_chain;
|
---|
276 | while(ptr)
|
---|
277 | {
|
---|
278 | check(gc_get_object_ptr( (void*)ptr->ptr ) == object);
|
---|
279 | ptr = ptr->next;
|
---|
280 | }
|
---|
281 | }
|
---|
282 |
|
---|
283 | checkf(size + gc_pool_size_left(pool) == gc_pool_size_total(pool),
|
---|
284 | (const char*)"expected %lu + %lu == %lu\n",
|
---|
285 | (size_t)size,
|
---|
286 | (size_t)gc_pool_size_left(pool),
|
---|
287 | (size_t)gc_pool_size_total(pool));
|
---|
288 |
|
---|
289 | pool = pool->next;
|
---|
290 | }
|
---|
291 |
|
---|
292 | return true;
|
---|
293 | }
|
---|
294 |
|
---|
295 | bool gc_state_no_from_space_ref(gc_state* const this)
|
---|
296 | {
|
---|
297 | gc_memory_pool* pool = this->to_space;
|
---|
298 | while(pool)
|
---|
299 | {
|
---|
300 | void** potential_ref = (void**)pool->start_p;
|
---|
301 | while(potential_ref < (void**)pool->free_p)
|
---|
302 | {
|
---|
303 | check(!gc_is_in_heap(this, *potential_ref));
|
---|
304 | potential_ref++;
|
---|
305 | }
|
---|
306 |
|
---|
307 | pool = pool->next;
|
---|
308 | }
|
---|
309 |
|
---|
310 | return true;
|
---|
311 | }
|
---|
312 | #endif
|
---|