on master help sign in
Collect C heap more frequently.
df164c1Matus Tejiscak
rts/idris_heap.c+23−5
@@ -1,12 +1,17 @@
1#include "idris_heap.h"
1#include "idris_heap.h"
2#include "idris_rts.h"
2#include "idris_rts.h"
3+#include "idris_gc.h"
3
4
4#include <stdlib.h>
5#include <stdlib.h>
5#include <stddef.h>
6#include <stddef.h>
6#include <stdio.h>
7#include <stdio.h>
8+#include <assert.h>
7
9
8−static void c_heap_free_item(CHeapItem * item)
10+static void c_heap_free_item(CHeap * heap, CHeapItem * item)
9{
11{
12+ assert(item->size <= heap->size);
13+ heap->size -= item->size;
14+
10 // fix links
15 // fix links
11 if (item->next != NULL)
16 if (item->next != NULL)
12 {
17 {
@@ -21,11 +26,12 @@ static void c_heap_free_item(CHeapItem * item)
21 free(item);
26 free(item);
22}
27}
23
28
24−CHeapItem * c_heap_create_item(void * data, CDataFinalizer * finalizer)
29+CHeapItem * c_heap_create_item(void * data, size_t size, CDataFinalizer * finalizer)
25{
30{
26 CHeapItem * item = (CHeapItem *) malloc(sizeof(CHeapItem));
31 CHeapItem * item = (CHeapItem *) malloc(sizeof(CHeapItem));
27
32
28 item->data = data;
33 item->data = data;
34+ item->size = size;
29 item->finalizer = finalizer;
35 item->finalizer = finalizer;
30 item->is_used = false;
36 item->is_used = false;
31 item->next = NULL;
37 item->next = NULL;
@@ -34,7 +40,7 @@ CHeapItem * c_heap_create_item(void * data, CDataFinalizer * finalizer)
34 return item;
40 return item;
35}
41}
36
42
37−void c_heap_insert_if_needed(CHeap * heap, CHeapItem * item)
43+void c_heap_insert_if_needed(VM * vm, CHeap * heap, CHeapItem * item)
38{
44{
39 if (item->prev_next != NULL) return; // already inserted
45 if (item->prev_next != NULL) return; // already inserted
40
46
@@ -47,6 +53,14 @@ void c_heap_insert_if_needed(CHeap * heap, CHeapItem * item)
47 item->next = heap->first;
53 item->next = heap->first;
48
54
49 heap->first = item;
55 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+ }
50}
64}
51
65
52void c_heap_mark_item(CHeapItem * item)
66void c_heap_mark_item(CHeapItem * item)
@@ -69,21 +83,25 @@ void c_heap_sweep(CHeap * heap)
69 CHeapItem * unused_item = p;
83 CHeapItem * unused_item = p;
70 p = p->next;
84 p = p->next;
71
85
72− c_heap_free_item(unused_item);
86+ c_heap_free_item(heap, unused_item);
73 }
87 }
74 }
88 }
89+
90+ heap->gc_trigger_size = C_HEAP_GC_TRIGGER_SIZE(heap->size);
75}
91}
76
92
77void c_heap_init(CHeap * heap)
93void c_heap_init(CHeap * heap)
78{
94{
79 heap->first = NULL;
95 heap->first = NULL;
96+ heap->size = 0;
97+ heap->gc_trigger_size = C_HEAP_GC_TRIGGER_SIZE_INITIAL;
80}
98}
81
99
82void c_heap_destroy(CHeap * heap)
100void c_heap_destroy(CHeap * heap)
83{
101{
84 while (heap->first != NULL)
102 while (heap->first != NULL)
85 {
103 {
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
87 }
105 }
88}
106}
89
107
rts/idris_heap.h+24−3
@@ -11,12 +11,22 @@
11 * of pointers coupled with their finalizers.
11 * of pointers coupled with their finalizers.
12 */
12 */
13
13
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+
14typedef void CDataFinalizer(void *);
19typedef void CDataFinalizer(void *);
15
20
16typedef struct CHeapItem {
21typedef struct CHeapItem {
17 /// Payload.
22 /// Payload.
18 void * data;
23 void * data;
19
24
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+
20 /// Finalizer that will be called on the payload pointer.
30 /// Finalizer that will be called on the payload pointer.
21 /// Its job is to deallocate all associated resources,
31 /// Its job is to deallocate all associated resources,
22 /// including the memory pointed to by `data` (if any).
32 /// including the memory pointed to by `data` (if any).
@@ -36,6 +46,14 @@ typedef struct CHeapItem {
36typedef struct CHeap {
46typedef struct CHeap {
37 /// The first item in the heap. NULL if the heap is empty.
47 /// The first item in the heap. NULL if the heap is empty.
38 CHeapItem * first;
48 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;
39} CHeap;
57} CHeap;
40
58
41/// Create a C heap.
59/// Create a C heap.
@@ -46,7 +64,8 @@ void c_heap_init(CHeap * c_heap);
46void c_heap_destroy(CHeap * c_heap);
64void c_heap_destroy(CHeap * c_heap);
47
65
48/// Insert the given item into the heap if it's not there yet.
66/// Insert the given item into the heap if it's not there yet.
67+/// The VM pointer is needed because this operation may trigger GC.
49−void c_heap_insert_if_needed(CHeap * c_heap, CHeapItem * item);
68+void c_heap_insert_if_needed(struct VM * vm, CHeap * c_heap, CHeapItem * item);
50
69
51/// Mark the given item as used.
70/// Mark the given item as used.
52void c_heap_mark_item(CHeapItem * item);
71void c_heap_mark_item(CHeapItem * item);
@@ -54,8 +73,10 @@ void c_heap_mark_item(CHeapItem * item);
54/// Sweep the C heap, finalizing and freeing unused blocks.
73/// Sweep the C heap, finalizing and freeing unused blocks.
55void c_heap_sweep(CHeap * c_heap);
74void c_heap_sweep(CHeap * c_heap);
56
75
57−/// Create a C heap item from its payload and 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.
58−CHeapItem * c_heap_create_item(void * data, CDataFinalizer * finalizer);
79+CHeapItem * c_heap_create_item(void * data, size_t size, CDataFinalizer * finalizer);
59
80
60/* *** Idris heap **
81/* *** Idris heap **
61 * Objects without finalizers. Cheney-collected.
82 * Objects without finalizers. Cheney-collected.
rts/idris_rts.c+5−5
@@ -138,12 +138,12 @@ Stats terminate(VM* vm) {
138CData cdata_allocate(size_t size, CDataFinalizer finalizer)
138CData cdata_allocate(size_t size, CDataFinalizer finalizer)
139{
139{
140 void * data = (void *) malloc(size);
140 void * data = (void *) malloc(size);
141− return cdata_manage(data, finalizer);
141+ return cdata_manage(data, size, finalizer);
142}
142}
143
143
144−CData cdata_manage(void * data, CDataFinalizer finalizer)
144+CData cdata_manage(void * data, size_t size, CDataFinalizer finalizer)
145{
145{
146− return c_heap_create_item(data, finalizer);
146+ return c_heap_create_item(data, size, finalizer);
147}
147}
148
148
149void idris_requireAlloc(size_t size) {
149void idris_requireAlloc(size_t size) {
@@ -290,7 +290,7 @@ VAL MKCDATA(VM* vm, CHeapItem * item) {
290 Closure* cl = allocate(sizeof(Closure), 0);
290 Closure* cl = allocate(sizeof(Closure), 0);
291 SETTY(cl, CDATA);
291 SETTY(cl, CDATA);
292 cl->info.c_heap_item = item;
292 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);
294 return cl;
294 return cl;
295}
295}
296
296
@@ -298,7 +298,7 @@ VAL MKCDATAc(VM* vm, CHeapItem * item) {
298 Closure* cl = allocate(sizeof(Closure), 1);
298 Closure* cl = allocate(sizeof(Closure), 1);
299 SETTY(cl, CDATA);
299 SETTY(cl, CDATA);
300 cl->info.c_heap_item = item;
300 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);
302 return cl;
302 return cl;
303}
303}
304
304
rts/idris_rts.h+8−5
@@ -72,16 +72,16 @@ typedef struct Closure {
72 } info;
72 } info;
73} Closure;
73} Closure;
74
74
75−struct VM_t;
75+struct VM;
76
76
77struct Msg_t {
77struct Msg_t {
78− struct VM_t* sender;
78+ struct VM* sender;
79 VAL msg;
79 VAL msg;
80};
80};
81
81
82typedef struct Msg_t Msg;
82typedef struct Msg_t Msg;
83
83
84−struct VM_t {
84+struct VM {
85 int active; // 0 if no longer running; keep for message passing
85 int active; // 0 if no longer running; keep for message passing
86 // TODO: If we're going to have lots of concurrent threads,
86 // TODO: If we're going to have lots of concurrent threads,
87 // we really need to be cleverer than this!
87 // we really need to be cleverer than this!
@@ -112,7 +112,7 @@ struct VM_t {
112 VAL reg1;
112 VAL reg1;
113};
113};
114
114
115−typedef struct VM_t VM;
115+typedef struct VM VM;
116
116
117
117
118/* C data interface: allocation on the C heap.
118/* C data interface: allocation on the C heap.
@@ -138,7 +138,10 @@ typedef CHeapItem * CData;
138CData cdata_allocate(size_t size, CDataFinalizer * finalizer);
138CData cdata_allocate(size_t size, CDataFinalizer * finalizer);
139
139
140/// Wrap a pointer as a C data block.
140/// Wrap a pointer as a C data block.
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.
141−CData cdata_manage(void * data, CDataFinalizer * finalizer);
144+CData cdata_manage(void * data, size_t size, CDataFinalizer * finalizer);
142
145
143
146
144// Create a new VM
147// Create a new VM