21 | 21 |
|
22 | 22 |
#include <stdio.h>
|
23 | 23 |
#define gc_out stderr
|
24 | |
#define XL_GC_NOPAGES 1
|
25 | 24 |
|
26 | 25 |
#include <inttypes.h>
|
27 | 26 |
#include <stdbool.h>
|
|
37 | 36 |
#include "expel/util.h"
|
38 | 37 |
#include "expel/vector.h"
|
39 | 38 |
|
40 | |
static struct xl_alloc_page *page_tail;
|
41 | 39 |
static struct xl_gc_info *gc_stats;
|
42 | 40 |
|
43 | 41 |
static struct xl_vector graph_alloc = {0};
|
|
53 | 51 |
char *buf;
|
54 | 52 |
xl_error err;
|
55 | 53 |
|
56 | |
page_tail = NULL;
|
57 | 54 |
if (unlikely(gc_stats != NULL))
|
58 | 55 |
free(gc_stats);
|
59 | |
|
60 | 56 |
gc_stats = calloc(1, sizeof(struct xl_gc_info));
|
61 | |
gc_stats->releases_until_gc = XL_GC_TRIGGER_RELEASES;
|
|
57 |
xl_assert(gc_stats != NULL);
|
62 | 58 |
|
63 | 59 |
trace_uri_str = getenv("EXPEL_TRACE_GRAPH");
|
64 | 60 |
if (trace_uri_str != NULL)
|
|
125 | 121 |
gc_stats->n_graph_allocs,
|
126 | 122 |
gc_stats->n_graph_frees,
|
127 | 123 |
(int64_t) gc_stats->n_graph_allocs - gc_stats->n_graph_frees);
|
128 | |
fprintf(gc_out, "gc ran %lu times\n", gc_stats->n_gc_runs);
|
129 | 124 |
|
130 | 125 |
fprintf(gc_out, "========================================\nleaked graphs:\n");
|
131 | 126 |
any_leaked = false;
|
|
179 | 174 |
void
|
180 | 175 |
xl_gc_free_all()
|
181 | 176 |
{
|
182 | |
struct xl_alloc_page *p;
|
183 | |
|
184 | |
while (page_tail != NULL)
|
185 | |
{
|
186 | |
p = page_tail;
|
187 | |
page_tail = p->prev;
|
188 | |
free(p->values);
|
189 | |
free(p);
|
190 | |
}
|
191 | 177 |
}
|
192 | 178 |
|
193 | 179 |
no_ignore xl_error
|
|
251 | 237 |
no_ignore xl_error
|
252 | 238 |
xl_value_new(struct xl_value **v)
|
253 | 239 |
{
|
254 | |
#if !XL_GC_NOPAGES
|
255 | |
struct xl_alloc_page *p;
|
256 | |
size_t i;
|
257 | |
bool pages_full;
|
258 | |
#endif
|
259 | |
|
260 | |
#ifndef XL_RECKLESS
|
261 | |
if (unlikely(gc_stats == NULL))
|
262 | |
return xl_raise(ERR_NOT_STARTED, "gc not started");
|
263 | |
#endif
|
264 | |
|
265 | |
#if XL_GC_NOPAGES
|
|
240 |
xl_assert(gc_stats != NULL);
|
|
241 |
|
266 | 242 |
*v = calloc(1, sizeof(struct xl_value));
|
267 | 243 |
if (*v == NULL)
|
268 | 244 |
return xl_raise(ERR_NO_MEMORY, "new value");
|
269 | 245 |
(*v)->tag = TAG_VALUE;
|
270 | 246 |
(*v)->refcount = 1;
|
|
247 |
|
|
248 |
#if XL_GC_DEBUG
|
|
249 |
gc_stats->n_val_allocs++;
|
|
250 |
#endif
|
|
251 |
|
271 | 252 |
return OK;
|
272 | |
#else
|
273 | |
pages_full = true;
|
274 | |
p = page_tail;
|
275 | |
while (p != NULL)
|
276 | |
{
|
277 | |
if (p->n_open_values > 0)
|
278 | |
{
|
279 | |
pages_full = false;
|
280 | |
break;
|
281 | |
}
|
282 | |
p = p->prev;
|
283 | |
}
|
284 | |
|
285 | |
if (unlikely(pages_full))
|
286 | |
{
|
287 | |
#if XL_GC_DEBUG
|
288 | |
gc_stats->n_page_allocs++;
|
289 | |
#endif
|
290 | |
p = calloc(1, sizeof(struct xl_alloc_page));
|
291 | |
if (p == NULL)
|
292 | |
return xl_raise(ERR_NO_MEMORY, "new page");
|
293 | |
p->values = calloc(XL_GC_PAGE_SIZE, sizeof(struct xl_value));
|
294 | |
if (p->values == NULL)
|
295 | |
return xl_raise(ERR_NO_MEMORY, "new page values");
|
296 | |
|
297 | |
/* All values are open when we begin */
|
298 | |
for (i = 0; i < XL_GC_PAGE_SIZE; i++)
|
299 | |
{
|
300 | |
p->open_values[i] = &p->values[i];
|
301 | |
p->values[i].alloc_page = p;
|
302 | |
}
|
303 | |
p->n_open_values = XL_GC_PAGE_SIZE;
|
304 | |
|
305 | |
if (page_tail != NULL)
|
306 | |
{
|
307 | |
p->prev = page_tail;
|
308 | |
page_tail->next = p;
|
309 | |
}
|
310 | |
page_tail = p;
|
311 | |
}
|
312 | |
|
313 | |
*v = p->open_values[p->n_open_values - 1];
|
314 | |
(*v)->tag = TAG_VALUE;
|
315 | |
(*v)->refcount = 1;
|
316 | |
p->n_open_values--;
|
317 | |
|
318 | |
#if XL_GC_DEBUG && XL_GC_DEBUG_V
|
319 | |
fprintf(gc_out,
|
320 | |
"take slot %lu in page %04lx\n",
|
321 | |
((uintptr_t) *v - (uintptr_t) p->values)
|
322 | |
/ sizeof(struct xl_value),
|
323 | |
((uintptr_t) p) & 0xFFFF);
|
324 | |
#endif
|
325 | |
|
326 | |
#if XL_GC_DEBUG
|
327 | |
gc_stats->n_val_allocs++;
|
328 | |
#endif
|
329 | |
return OK;
|
330 | |
#endif
|
331 | 253 |
}
|
332 | 254 |
|
333 | 255 |
/* Takes a reference to the given tree. */
|
|
377 | 299 |
return xl_raise(ERR_BAD_TAG, "take");
|
378 | 300 |
}
|
379 | 301 |
|
380 | |
no_ignore xl_error
|
381 | |
xl_gc_run()
|
382 | |
{
|
383 | |
struct xl_alloc_page *p;
|
384 | |
struct xl_alloc_page *to_free;
|
385 | |
|
386 | |
#if XL_GC_DEBUG
|
387 | |
gc_stats->n_gc_runs++;
|
388 | |
#endif
|
389 | |
|
390 | |
p = page_tail;
|
391 | |
while (p != NULL)
|
392 | |
{
|
393 | |
to_free = p;
|
394 | |
p = p->prev;
|
395 | |
if (unlikely(to_free->n_open_values == XL_GC_PAGE_SIZE))
|
396 | |
{
|
397 | |
if (to_free->prev != NULL)
|
398 | |
to_free->prev->next = to_free->next;
|
399 | |
if (to_free->next != NULL)
|
400 | |
to_free->next->prev = to_free->prev;
|
401 | |
if (to_free == page_tail)
|
402 | |
page_tail = to_free->prev;
|
403 | |
free(to_free->values);
|
404 | |
free(to_free);
|
405 | |
#if XL_GC_DEBUG
|
406 | |
gc_stats->n_page_frees++;
|
407 | |
#endif
|
408 | |
}
|
409 | |
}
|
410 | |
gc_stats->releases_until_gc = XL_GC_TRIGGER_RELEASES;
|
411 | |
return OK;
|
412 | |
}
|
413 | |
|
414 | 302 |
/* Releases a reference to the given tree. */
|
415 | 303 |
no_ignore static xl_error
|
416 | 304 |
_release_value(struct xl_value *v)
|
417 | 305 |
{
|
418 | 306 |
xl_error err;
|
419 | |
#if !XL_GC_NOPAGES
|
420 | |
struct xl_alloc_page *p;
|
421 | |
#endif
|
422 | 307 |
|
423 | 308 |
if (unlikely(v->refcount == 0))
|
424 | 309 |
return xl_raise(ERR_REFCOUNT_UNDERFLOW, "release");
|
425 | 310 |
v->refcount--;
|
426 | 311 |
|
427 | |
gc_stats->releases_until_gc--;
|
428 | 312 |
#if XL_GC_DEBUG
|
429 | |
gc_stats->n_val_frees++;
|
|
313 |
gc_stats->n_val_frees++;
|
430 | 314 |
#endif
|
431 | 315 |
|
432 | 316 |
err = OK;
|
|
437 | 321 |
err = xl_release(v->left.any);
|
438 | 322 |
if (err == OK && (v->tag & (TAG_RIGHT_NODE | TAG_RIGHT_GRAPH)))
|
439 | 323 |
err = xl_release(v->right.any);
|
440 | |
|
441 | |
#if XL_GC_NOPAGES
|
442 | 324 |
free(v);
|
443 | |
#else
|
444 | |
p = v->alloc_page;
|
445 | |
p->open_values[p->n_open_values] = v;
|
446 | |
p->n_open_values++;
|
447 | |
|
448 | |
#if XL_GC_DEBUG && XL_GC_DEBUG_V
|
449 | |
fprintf(gc_out,
|
450 | |
"release slot %lu in page %04lx\n",
|
451 | |
((uintptr_t) v - (uintptr_t) p->values)
|
452 | |
/ sizeof(struct xl_value),
|
453 | |
((uintptr_t) p) & 0xFFFF);
|
454 | |
#endif
|
455 | |
#endif
|
456 | |
|
457 | |
}
|
458 | |
|
459 | |
if (unlikely(err == OK && gc_stats->releases_until_gc == 0))
|
460 | |
err = xl_gc_run();
|
|
325 |
}
|
|
326 |
|
461 | 327 |
return err;
|
462 | 328 |
}
|
463 | 329 |
|