summaryrefslogtreecommitdiffstats
path: root/gc.c
diff options
context:
space:
mode:
authorKaz Kylheku <kaz@kylheku.com>2019-11-08 20:04:31 -0800
committerKaz Kylheku <kaz@kylheku.com>2019-11-08 20:04:31 -0800
commit05243452efd861e872a6d5c612c23a2cdea86ec5 (patch)
treee28cc1881df53c94c7df3577b795c4e3e451f2b9 /gc.c
parente8a6f437306d6e6dc3576a47181aad3a2b741f2b (diff)
downloadtxr-05243452efd861e872a6d5c612c23a2cdea86ec5.tar.gz
txr-05243452efd861e872a6d5c612c23a2cdea86ec5.tar.bz2
txr-05243452efd861e872a6d5c612c23a2cdea86ec5.zip
gc: free heaps that become empty.
On glibc, our heap allocation requests are considered large and handled via mmap; when we free a heap, the memory is returned to the OS via munmap. * gc.c (sweep): If every object in a heap is freed, we free the entire heap, taking care to also reset the free list to the state before those objects were added to it. The free list may still contain objects from that same heap that were not just added to it (they were freed in a previous GC pass), so we must walk the free list to find the remaining objects and remove them. The Valgrind debugging logic (opening access and closing while walking the list) was too cumbersome so it's done in two passes: open access to the whole free list, process it, close off what is left.
Diffstat (limited to 'gc.c')
-rw-r--r--gc.c43
1 files changed, 41 insertions, 2 deletions
diff --git a/gc.c b/gc.c
index 93a91267..8e84af01 100644
--- a/gc.c
+++ b/gc.c
@@ -607,7 +607,7 @@ static int sweep_one(obj_t *block)
static int_ptr_t sweep(void)
{
int_ptr_t free_count = 0;
- heap_t *heap;
+ heap_t **pph;
#if HAVE_VALGRIND
const int vg_dbg = opt_vg_debug;
#endif
@@ -632,8 +632,11 @@ static int_ptr_t sweep(void)
#endif
- for (heap = heap_list; heap != 0; heap = heap->next) {
+ for (pph = &heap_list; *pph != 0; ) {
obj_t *block, *end;
+ heap_t *heap = *pph;
+ int_ptr_t old_count = free_count;
+ val old_free_list = free_list;
#if HAVE_VALGRIND
if (vg_dbg)
@@ -646,6 +649,42 @@ static int_ptr_t sweep(void)
{
free_count += sweep_one(block);
}
+
+ if (free_count - old_count == HEAP_SIZE) {
+ val *ppf;
+
+ free_list = old_free_list;
+#if HAVE_VALGRIND
+ if (vg_dbg) {
+ val iter;
+ for (iter = free_list; iter; iter = iter->t.next)
+ VALGRIND_MAKE_MEM_DEFINED(iter, sizeof *iter);
+ }
+#endif
+ for (ppf = &free_list; *ppf != nil; ) {
+ val block = *ppf;
+ if (block >= heap->block && block < end) {
+ *ppf = block->t.next;
+ } else {
+ ppf = &block->t.next;
+ }
+ }
+ if (free_list == 0)
+ free_tail = &free_list;
+ *pph = heap->next;
+ free(heap);
+#if HAVE_VALGRIND
+ if (vg_dbg) {
+ val iter, next;
+ for (iter = free_list; iter; iter = next) {
+ next = iter->t.next;
+ VALGRIND_MAKE_MEM_NOACCESS(iter, sizeof *iter);
+ }
+ }
+#endif
+ } else {
+ pph = &(*pph)->next;
+ }
}
return free_count;