git.haldean.org expel / 3378257
gut the garbage collector it's hiding bugs (bugs which I wish had stayed hidden), and we don't need it yet. Haldean Brown 6 years ago
3 changed file(s) with 11 addition(s) and 211 deletion(s). Raw diff Collapse all Expand all
1818
1919 #include "expel/expel.h"
2020
21 /* The number of values in a page. */
22 #define XL_GC_PAGE_SIZE 8
23 /* The number of calls to xl_release before we start a GC sweep. */
24 #define XL_GC_TRIGGER_RELEASES (4 * XL_GC_PAGE_SIZE)
25
26 struct xl_alloc_page
27 {
28 /* Pages are stored in a linked list. */
29 struct xl_alloc_page *prev;
30 struct xl_alloc_page *next;
31
32 /* A pointer to the block in memory that this page represents. */
33 struct xl_value *values;
34
35 /* The free list of values that are available. This starts with a
36 * pointer to every element in the page, and as spots are used these
37 * pointers are popped off. When a value is freed, its pointer is added
38 * back onto this array. */
39 struct xl_value *open_values[XL_GC_PAGE_SIZE];
40
41 /* The number of valid open values in the open_values array. */
42 int64_t n_open_values;
43 };
44
4521 struct xl_gc_info
4622 {
47 int64_t releases_until_gc;
48
4923 #ifdef XL_GC_DEBUG
50 uint64_t n_page_allocs;
51 uint64_t n_page_frees;
5224 uint64_t n_val_allocs;
5325 uint64_t n_val_frees;
54 uint64_t n_gc_runs;
5526 uint64_t n_graph_allocs;
5627 uint64_t n_graph_frees;
5728 #endif
8455 void
8556 xl_gc_free_all();
8657
87 /* Runs garbage collection and removes empty pages.
88 *
89 * There should be no reason to call this; xl_release calls it
90 * when appropriate. This is provided as an API for garbage
91 * collection testing only. */
92 no_ignore xl_error
93 xl_gc_run();
94
2121
2222 #include <stdio.h>
2323 #define gc_out stderr
24 #define XL_GC_NOPAGES 1
2524
2625 #include <inttypes.h>
2726 #include <stdbool.h>
3736 #include "expel/util.h"
3837 #include "expel/vector.h"
3938
40 static struct xl_alloc_page *page_tail;
4139 static struct xl_gc_info *gc_stats;
4240
4341 static struct xl_vector graph_alloc = {0};
5351 char *buf;
5452 xl_error err;
5553
56 page_tail = NULL;
5754 if (unlikely(gc_stats != NULL))
5855 free(gc_stats);
59
6056 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);
6258
6359 trace_uri_str = getenv("EXPEL_TRACE_GRAPH");
6460 if (trace_uri_str != NULL)
125121 gc_stats->n_graph_allocs,
126122 gc_stats->n_graph_frees,
127123 (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);
129124
130125 fprintf(gc_out, "========================================\nleaked graphs:\n");
131126 any_leaked = false;
179174 void
180175 xl_gc_free_all()
181176 {
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 }
191177 }
192178
193179 no_ignore xl_error
251237 no_ignore xl_error
252238 xl_value_new(struct xl_value **v)
253239 {
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
266242 *v = calloc(1, sizeof(struct xl_value));
267243 if (*v == NULL)
268244 return xl_raise(ERR_NO_MEMORY, "new value");
269245 (*v)->tag = TAG_VALUE;
270246 (*v)->refcount = 1;
247
248 #if XL_GC_DEBUG
249 gc_stats->n_val_allocs++;
250 #endif
251
271252 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
331253 }
332254
333255 /* Takes a reference to the given tree. */
377299 return xl_raise(ERR_BAD_TAG, "take");
378300 }
379301
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
414302 /* Releases a reference to the given tree. */
415303 no_ignore static xl_error
416304 _release_value(struct xl_value *v)
417305 {
418306 xl_error err;
419 #if !XL_GC_NOPAGES
420 struct xl_alloc_page *p;
421 #endif
422307
423308 if (unlikely(v->refcount == 0))
424309 return xl_raise(ERR_REFCOUNT_UNDERFLOW, "release");
425310 v->refcount--;
426311
427 gc_stats->releases_until_gc--;
428312 #if XL_GC_DEBUG
429 gc_stats->n_val_frees++;
313 gc_stats->n_val_frees++;
430314 #endif
431315
432316 err = OK;
437321 err = xl_release(v->left.any);
438322 if (err == OK && (v->tag & (TAG_RIGHT_NODE | TAG_RIGHT_GRAPH)))
439323 err = xl_release(v->right.any);
440
441 #if XL_GC_NOPAGES
442324 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
461327 return err;
462328 }
463329
4646 #ifdef XL_GC_DEBUG
4747 assert(gc_stats.n_val_allocs == N_TEST_GC_VALUES);
4848 assert(gc_stats.n_val_frees == N_TEST_GC_VALUES);
49 assert(gc_stats.n_val_frees >= gc_stats.n_gc_runs);
50 #endif
51
52 assert(xl_gc_run() == OK);
53 xl_gc_get_stats(&gc_stats);
54 #ifdef XL_GC_DEBUG
55 assert(gc_stats.n_page_frees == gc_stats.n_page_allocs);
5649 #endif
5750
5851 xl_gc_free_all();
5952 xl_gc_start();
60
61 for (i = 0; i < XL_GC_PAGE_SIZE * 2; i++)
62 {
63 assert(xl_value_new(&vals[i]) == 0);
64 }
65 for (i = 0; i < XL_GC_PAGE_SIZE * 2; i++)
66 {
67 assert(xl_release(vals[i]) == 0);
68 }
69 for (i = 0; i < XL_GC_PAGE_SIZE * 2; i++)
70 {
71 assert(xl_value_new(&vals[i]) == 0);
72 }
73 for (i = 0; i < XL_GC_PAGE_SIZE * 2; i++)
74 {
75 assert(xl_release(vals[i]) == 0);
76 }
77
78 xl_gc_get_stats(&gc_stats);
79 #ifdef XL_GC_DEBUG
80 assert(gc_stats.n_page_allocs == 2);
81 #endif
8253
8354 return ok;
8455 }