on master help sign in
Collect C heap more frequently.
commitdf164c1cee5c9da39350fe803505eadb3b9911cc authorMatus Tejiscak <ziman@functor.sk> dateFri 12 Feb 2016, 14:51 (11 years ago) parentb6449de
4 files changed+60−18
rts/idris_heap.c+23−5 rts/idris_heap.h+24−3 rts/idris_rts.c+5−5 rts/idris_rts.h+8−5
rts/idris_heap.c+23−5
@@ -1,12 +1,17 @@
11#include "idris_heap.h"
22#include "idris_rts.h"
3+#include "idris_gc.h"
34
45#include <stdlib.h>
56#include <stddef.h>
67#include <stdio.h>
8+#include <assert.h>
79
8−static void c_heap_free_item(CHeapItem * item)
10+static void c_heap_free_item(CHeap * heap, CHeapItem * item)
911{
12+ assert(item->size <= heap->size);
13+ heap->size -= item->size;
14+
1015 // fix links
1116 if (item->next != NULL)
1217 {
@@ -21,11 +26,12 @@ static void c_heap_free_item(CHeapItem * item)
2126 free(item);
2227}
2328
24−CHeapItem * c_heap_create_item(void * data, CDataFinalizer * finalizer)
29+CHeapItem * c_heap_create_item(void * data, size_t size, CDataFinalizer * finalizer)
2530{
2631 CHeapItem * item = (CHeapItem *) malloc(sizeof(CHeapItem));
2732
2833 item->data = data;
34+ item->size = size;
2935 item->finalizer = finalizer;
3036 item->is_used = false;
3137 item->next = NULL;
@@ -34,7 +40,7 @@ CHeapItem * c_heap_create_item(void * data, CDataFinalizer * finalizer)
3440 return item;
3541}
3642
37−void c_heap_insert_if_needed(CHeap * heap, CHeapItem * item)
43+void c_heap_insert_if_needed(VM * vm, CHeap * heap, CHeapItem * item)
3844{
3945 if (item->prev_next != NULL) return; // already inserted
4046
@@ -47,6 +53,14 @@ void c_heap_insert_if_needed(CHeap * heap, CHeapItem * item)
4753 item->next = heap->first;
4854
4955 heap->first = item;
56+
57+ // at this point, links are done; let's calculate sizes
58+
59+ heap->size += item->size;
60+ if (heap->size >= heap->gc_trigger_size)
61+ {
62+ idris_gc(vm);
63+ }
5064}
5165
5266void c_heap_mark_item(CHeapItem * item)
@@ -69,21 +83,25 @@ void c_heap_sweep(CHeap * heap)
6983 CHeapItem * unused_item = p;
7084 p = p->next;
7185
72− c_heap_free_item(unused_item);
86+ c_heap_free_item(heap, unused_item);
7387 }
7488 }
89+
90+ heap->gc_trigger_size = C_HEAP_GC_TRIGGER_SIZE(heap->size);
7591}
7692
7793void c_heap_init(CHeap * heap)
7894{
7995 heap->first = NULL;
96+ heap->size = 0;
97+ heap->gc_trigger_size = C_HEAP_GC_TRIGGER_SIZE_INITIAL;
8098}
8199
82100void c_heap_destroy(CHeap * heap)
83101{
84102 while (heap->first != NULL)
85103 {
86− c_heap_free_item(heap->first); // will update heap->first via the backward link
104+ c_heap_free_item(heap, heap->first); // will update heap->first via the backward link
87105 }
88106}
89107
rts/idris_heap.h+24−3
@@ -11,12 +11,22 @@
1111 * of pointers coupled with their finalizers.
1212 */
1313
14+struct VM;
15+
16+#define C_HEAP_GC_TRIGGER_SIZE_INITIAL 4096
17+#define C_HEAP_GC_TRIGGER_SIZE(heap_size) (2 * heap_size)
18+
1419typedef void CDataFinalizer(void *);
1520
1621typedef struct CHeapItem {
1722 /// Payload.
1823 void * data;
1924
25+ /// Size of the item, in bytes.
26+ /// This does not have to be a precise size. It is used to assess
27+ /// whether the heap needs garbage collection.
28+ size_t size;
29+
2030 /// Finalizer that will be called on the payload pointer.
2131 /// Its job is to deallocate all associated resources,
2232 /// including the memory pointed to by `data` (if any).
@@ -36,6 +46,14 @@ typedef struct CHeapItem {
3646typedef struct CHeap {
3747 /// The first item in the heap. NULL if the heap is empty.
3848 CHeapItem * first;
49+
50+ /// Total size of the heap. (Sum of sizes of items.)
51+ /// This may not be a precise size since individual items'
52+ /// sizes may be just estimates.
53+ size_t size;
54+
55+ /// When heap reaches this size, GC will be triggered.
56+ size_t gc_trigger_size;
3957} CHeap;
4058
4159/// Create a C heap.
@@ -46,7 +64,8 @@ void c_heap_init(CHeap * c_heap);
4664void c_heap_destroy(CHeap * c_heap);
4765
4866/// Insert the given item into the heap if it's not there yet.
49−void c_heap_insert_if_needed(CHeap * c_heap, CHeapItem * item);
67+/// The VM pointer is needed because this operation may trigger GC.
68+void c_heap_insert_if_needed(struct VM * vm, CHeap * c_heap, CHeapItem * item);
5069
5170/// Mark the given item as used.
5271void c_heap_mark_item(CHeapItem * item);
@@ -54,8 +73,10 @@ void c_heap_mark_item(CHeapItem * item);
5473/// Sweep the C heap, finalizing and freeing unused blocks.
5574void c_heap_sweep(CHeap * c_heap);
5675
57−/// Create a C heap item from its payload and finalizer.
58−CHeapItem * c_heap_create_item(void * data, CDataFinalizer * finalizer);
76+/// Create a C heap item from its payload, size estimate, and finalizer.
77+/// The size does not have to be precise but it should roughly reflect
78+/// how big the item is for GC to work effectively.
79+CHeapItem * c_heap_create_item(void * data, size_t size, CDataFinalizer * finalizer);
5980
6081/* *** Idris heap **
6182 * Objects without finalizers. Cheney-collected.
rts/idris_rts.c+5−5
@@ -138,12 +138,12 @@ Stats terminate(VM* vm) {
138138CData cdata_allocate(size_t size, CDataFinalizer finalizer)
139139{
140140 void * data = (void *) malloc(size);
141− return cdata_manage(data, finalizer);
141+ return cdata_manage(data, size, finalizer);
142142}
143143
144−CData cdata_manage(void * data, CDataFinalizer finalizer)
144+CData cdata_manage(void * data, size_t size, CDataFinalizer finalizer)
145145{
146− return c_heap_create_item(data, finalizer);
146+ return c_heap_create_item(data, size, finalizer);
147147}
148148
149149void idris_requireAlloc(size_t size) {
@@ -290,7 +290,7 @@ VAL MKCDATA(VM* vm, CHeapItem * item) {
290290 Closure* cl = allocate(sizeof(Closure), 0);
291291 SETTY(cl, CDATA);
292292 cl->info.c_heap_item = item;
293− c_heap_insert_if_needed(&vm->c_heap, item);
293+ c_heap_insert_if_needed(vm, &vm->c_heap, item);
294294 return cl;
295295}
296296
@@ -298,7 +298,7 @@ VAL MKCDATAc(VM* vm, CHeapItem * item) {
298298 Closure* cl = allocate(sizeof(Closure), 1);
299299 SETTY(cl, CDATA);
300300 cl->info.c_heap_item = item;
301− c_heap_insert_if_needed(&vm->c_heap, item);
301+ c_heap_insert_if_needed(vm, &vm->c_heap, item);
302302 return cl;
303303}
304304
rts/idris_rts.h+8−5
@@ -72,16 +72,16 @@ typedef struct Closure {
7272 } info;
7373} Closure;
7474
75−struct VM_t;
75+struct VM;
7676
7777struct Msg_t {
78− struct VM_t* sender;
78+ struct VM* sender;
7979 VAL msg;
8080};
8181
8282typedef struct Msg_t Msg;
8383
84−struct VM_t {
84+struct VM {
8585 int active; // 0 if no longer running; keep for message passing
8686 // TODO: If we're going to have lots of concurrent threads,
8787 // we really need to be cleverer than this!
@@ -112,7 +112,7 @@ struct VM_t {
112112 VAL reg1;
113113};
114114
115−typedef struct VM_t VM;
115+typedef struct VM VM;
116116
117117
118118/* C data interface: allocation on the C heap.
@@ -138,7 +138,10 @@ typedef CHeapItem * CData;
138138CData cdata_allocate(size_t size, CDataFinalizer * finalizer);
139139
140140/// Wrap a pointer as a C data block.
141−CData cdata_manage(void * data, CDataFinalizer * finalizer);
141+/// The size should be an estimate of how much memory, in bytes,
142+/// is associated with the pointer. This estimate need not be absolutely precise
143+/// but it is necessary for GC to work effectively.
144+CData cdata_manage(void * data, size_t size, CDataFinalizer * finalizer);
142145
143146
144147// Create a new VM