69f32a6a35ea3ac74dded56795ff868cd29099ee
[IRC.git] / Robust / src / Runtime / garbage.c
1 #include "garbage.h"
2 #include "runtime.h"
3 #include "structdefs.h"
4 #include "Queue.h"
5 #include "SimpleHash.h"
6 #include "GenericHashtable.h"
7 #include <string.h>
8 #ifdef THREADS
9 #include "thread.h"
10 #endif
11 #ifdef DMALLOC
12 #include "dmalloc.h"
13 #endif
14
15
16 #define NUMPTRS 100
17
18 #define INITIALHEAPSIZE 10*1024
19 #define GCPOINT(x) ((int)((x)*0.9))
20 /* This define takes in how full the heap is initially and returns a new heap size to use */
21 #define HEAPSIZE(x,y) (((int)((x)/0.6))+y)
22
23 #ifdef TASK
24 extern struct Queue * activetasks;
25 extern struct parameterwrapper * objectqueues[NUMCLASSES];
26 extern struct genhashtable * failedtasks;
27 extern struct taskparamdescriptor *currtpd;
28 extern struct RuntimeHash *forward;
29 extern struct RuntimeHash *reverse;
30 extern struct RuntimeHash *fdtoobject;
31 #endif
32
33 #ifdef THREADS
34 int needtocollect=0;
35 struct listitem * list=NULL;
36 int listcount=0;
37 #endif
38
39 struct pointerblock {
40   void * ptrs[NUMPTRS];
41   struct pointerblock *next;
42 };
43
44 struct pointerblock *head=NULL;
45 int headindex=0;
46 struct pointerblock *tail=NULL;
47 int tailindex=0;
48 struct pointerblock *spare=NULL;
49
50 void enqueue(void *ptr) {
51   if (headindex==NUMPTRS) {
52     struct pointerblock * tmp;
53     if (spare!=NULL) { 
54       tmp=spare;
55       spare=NULL;
56     } else 
57       tmp=malloc(sizeof(struct pointerblock));
58     head->next=tmp;
59     head=tmp;
60     headindex=0;
61   }
62   head->ptrs[headindex++]=ptr;
63 }
64
65 void * dequeue() {
66   if (tailindex==NUMPTRS) {
67     struct pointerblock *tmp=tail;
68     tail=tail->next;
69     tailindex=0;
70     if (spare!=NULL)
71       free(tmp);
72     else
73       spare=tmp;
74   }
75   return tail->ptrs[tailindex++];
76 }
77
78 int moreItems() {
79   if ((head==tail)&&(tailindex==headindex))
80     return 0;
81   return 1;
82 }
83
84 void collect(struct garbagelist * stackptr) {
85 #ifdef THREADS
86   needtocollect=1;
87   pthread_mutex_lock(&gclistlock);
88   while(1) {
89     if ((listcount+1)==threadcount) {
90       break; /* Have all other threads stopped */
91     }
92     pthread_cond_wait(&gccond, &gclistlock);
93   }
94 #endif
95
96   if (head==NULL) {
97     headindex=0;
98     tailindex=0;
99     head=tail=malloc(sizeof(struct pointerblock));
100   }
101   /* Check current stack */
102 #ifdef THREADS
103  {
104    struct listitem *listptr=list;
105    while(stackptr!=NULL) {
106 #endif
107      
108   while(stackptr!=NULL) {
109     int i;
110     for(i=0;i<stackptr->size;i++) {
111       void * orig=stackptr->array[i];
112       void * copy;
113       if (gc_createcopy(orig,&copy))
114         enqueue(orig);
115       stackptr->array[i]=copy;
116     }
117     stackptr=stackptr->next;
118   }
119 #ifdef THREADS
120   /* Go to next thread */
121   if (listptr!=NULL) {
122     void * orig=listptr->locklist;
123     void * copy;
124     if (gc_createcopy(orig,&copy))
125       enqueue(orig);
126     listptr->locklist=copy;
127     stackptr=listptr->stackptr;
128     listptr=listptr->next;
129   }
130    }
131  }
132 #endif
133   
134 #ifdef TASK
135   {
136     /* Update objectsets */
137     int i;
138     for(i=0;i<NUMCLASSES;i++) {
139       struct parameterwrapper * p=objectqueues[i];
140       while(p!=NULL) {
141         struct RuntimeHash * set=p->objectset;
142         struct RuntimeNode * ptr=set->listhead;
143         while(ptr!=NULL) {
144           void *orig=(void *)ptr->key;
145           void *copy;
146           if (gc_createcopy(orig, &copy))
147             enqueue(orig);
148           ptr->key=(int)copy;
149           
150           ptr=ptr->lnext;
151         }
152         RuntimeHashrehash(set); /* Rehash the table */
153         p=p->next;
154       }
155     }
156   }
157   
158   if (forward!=NULL) {
159     struct RuntimeNode * ptr=forward->listhead;
160     while(ptr!=NULL) {
161       void * orig=(void *)ptr->key;
162       void *copy;
163       if (gc_createcopy(orig, &copy))
164         enqueue(orig);
165       ptr->key=(int)copy;
166
167       ptr=ptr->lnext;
168     }
169     RuntimeHashrehash(forward); /* Rehash the table */
170   }
171
172   if (reverse!=NULL) {
173     struct RuntimeNode * ptr=reverse->listhead;
174     while(ptr!=NULL) {
175       void *orig=(void *)ptr->data;
176       void *copy;
177       if (gc_createcopy(orig, &copy))
178         enqueue(orig);
179       ptr->data=(int)copy;
180
181       ptr=ptr->lnext;
182     }
183   }
184
185   {
186     struct RuntimeNode * ptr=fdtoobject->listhead;
187     while(ptr!=NULL) {
188       void *orig=(void *)ptr->data;
189       void *copy;
190       if (gc_createcopy(orig, &copy))
191         enqueue(orig);
192       ptr->data=(int)copy;
193
194       ptr=ptr->lnext;
195     }
196   }
197
198   {
199     /* Update current task descriptor */
200     int i;
201     for(i=0;i<currtpd->numParameters;i++) {
202       void *orig=currtpd->parameterArray[i];
203       void *copy;
204       if (gc_createcopy(orig, &copy))
205         enqueue(orig);
206       currtpd->parameterArray[i]=copy;
207     }
208
209   }
210
211   {
212     /* Update active tasks */
213     struct QueueItem * ptr=activetasks->head;
214     while (ptr!=NULL) {
215       struct taskparamdescriptor *tpd=ptr->objectptr;
216       int i;
217       for(i=0;i<tpd->numParameters;i++) {
218         void *orig=tpd->parameterArray[i];
219         void *copy;
220         if (gc_createcopy(orig, &copy))
221           enqueue(orig);
222         tpd->parameterArray[i]=copy;
223       }
224       ptr=ptr->next;
225     }
226   }
227     /* Update failed tasks */
228   {
229     struct genpointerlist * ptr=failedtasks->list;
230     while(ptr!=NULL) {
231       struct taskparamdescriptor *tpd=ptr->src;
232       int i;
233       for(i=0;i<tpd->numParameters;i++) {
234         void * orig=tpd->parameterArray[i];
235         void * copy;
236         if (gc_createcopy(orig, &copy))
237           enqueue(orig);
238         tpd->parameterArray[i]=copy;
239       }
240       ptr=ptr->inext;
241     }
242     genrehash(failedtasks);
243   }
244 #endif
245
246   while(moreItems()) {
247     void * ptr=dequeue();
248     void *cpy=((void **)ptr)[1];
249     int type=((int *)cpy)[0];
250     int * pointer=pointerarray[type];
251     if (pointer==0) {
252       /* Array of primitives */
253       /* Do nothing */
254     } else if (((int)pointer)==1) {
255       /* Array of pointers */
256       struct ArrayObject *ao=(struct ArrayObject *) ptr;
257       struct ArrayObject *ao_cpy=(struct ArrayObject *) cpy;
258       int length=ao->___length___;
259       int i;
260       for(i=0;i<length;i++) {
261         void *objptr=((void **)(((char *)& ao->___length___)+sizeof(int)))[i];
262         void * copy;
263         if (gc_createcopy(objptr, &copy))
264           enqueue(objptr);
265         ((void **)(((char *)& ao_cpy->___length___)+sizeof(int)))[i]=copy;
266       }
267     } else {
268       int size=pointer[0];
269       int i;
270       for(i=1;i<=size;i++) {
271         int offset=pointer[i];
272         void * objptr=*((void **)(((int)ptr)+offset));
273         void * copy;
274         if (gc_createcopy(objptr, &copy))
275           enqueue(objptr);
276         *((void **) (((int)cpy)+offset))=copy;
277       }
278     }
279   }
280 #ifdef THREADS
281   needtocollect=0;
282   pthread_mutex_unlock(&gclistlock);
283 #endif
284 }
285
286 void * curr_heapbase=0;
287 void * curr_heapptr=0;
288 void * curr_heapgcpoint=0;
289 void * curr_heaptop=0;
290
291 void * to_heapbase=0;
292 void * to_heapptr=0;
293 void * to_heaptop=0;
294 long lastgcsize=0;
295
296 void * tomalloc(int size) {
297   void * ptr=to_heapptr;
298   if ((size%4)!=0)
299     size+=(4-(size%4));
300   to_heapptr+=size;
301   return ptr;
302 }
303
304 #ifdef THREADS
305
306 void checkcollect(void * ptr) {
307   if (needtocollect) {
308     struct listitem * tmp=stopforgc((struct garbagelist *)ptr);
309     pthread_mutex_lock(&gclock); // Wait for GC
310     restartaftergc(tmp);
311     pthread_mutex_unlock(&gclock);
312
313   }
314 }
315
316 struct listitem * stopforgc(struct garbagelist * ptr) {
317   struct listitem * litem=malloc(sizeof(struct listitem));
318   litem->stackptr=ptr;
319   litem->locklist=pthread_getspecific(threadlocks);
320   litem->prev=NULL;
321   pthread_mutex_lock(&gclistlock);
322   litem->next=list;
323   if(list!=NULL)
324     list->prev=litem;
325   list=litem;
326   listcount++;
327   pthread_cond_signal(&gccond);
328   pthread_mutex_unlock(&gclistlock);
329   return litem;
330 }
331
332 void restartaftergc(struct listitem * litem) {
333   pthread_mutex_lock(&gclistlock);
334   pthread_setspecific(threadlocks, litem->locklist);
335   if (litem->prev==NULL) {
336     list=litem->next;
337   } else {
338     litem->prev->next=litem->next;
339   }
340   if (litem->next!=NULL) {
341     litem->next->prev=litem->prev;
342   }
343   listcount--;
344   pthread_mutex_unlock(&gclistlock);
345   free(litem);
346 }
347 #endif
348
349 void * mygcmalloc(struct garbagelist * stackptr, int size) {
350   void *ptr;
351 #ifdef THREADS
352   if (pthread_mutex_trylock(&gclock)!=0) {
353     struct listitem *tmp=stopforgc(stackptr);
354     pthread_mutex_lock(&gclock);
355     restartaftergc(tmp);
356   }
357 #endif
358   ptr=curr_heapptr;
359   if ((size%4)!=0)
360     size+=(4-(size%4));
361   curr_heapptr+=size;
362   if (curr_heapptr>curr_heapgcpoint) {
363     if (curr_heapbase==0) {
364       /* Need to allocate base heap */
365       curr_heapbase=malloc(INITIALHEAPSIZE);
366       bzero(curr_heapbase, INITIALHEAPSIZE);
367       curr_heaptop=curr_heapbase+INITIALHEAPSIZE;
368       curr_heapgcpoint=((char *) curr_heapbase)+GCPOINT(INITIALHEAPSIZE);
369       curr_heapptr=curr_heapbase+size;
370
371       to_heapbase=malloc(INITIALHEAPSIZE);
372       to_heaptop=to_heapbase+INITIALHEAPSIZE;
373       to_heapptr=to_heapbase;
374       ptr=curr_heapbase;
375 #ifdef THREADS
376       pthread_mutex_unlock(&gclock);
377 #endif
378       return ptr;
379     }
380
381     /* Grow the to heap if necessary */
382     {
383       int curr_heapsize=curr_heaptop-curr_heapbase;
384       int to_heapsize=to_heaptop-to_heapbase;
385       int last_heapsize=0;
386       if (lastgcsize>0) {
387         last_heapsize=HEAPSIZE(lastgcsize, size);
388         if ((last_heapsize%4)!=0)
389           last_heapsize+=(4-(last_heapsize%4));
390       }
391       if (curr_heapsize>last_heapsize)
392         last_heapsize=curr_heapsize;
393       if (last_heapsize>to_heapsize) {
394         free(to_heapbase);
395         to_heapbase=malloc(last_heapsize);
396         to_heaptop=to_heapbase+last_heapsize;
397         to_heapptr=to_heapbase;
398       }
399     }
400    
401     /* Do our collection */
402     collect(stackptr);
403
404     /* Update stat on previous gc size */
405     lastgcsize=(to_heapptr-to_heapbase)+size;
406
407     /* Flip to/curr heaps */
408     {
409       void * tmp=to_heapbase;
410       to_heapbase=curr_heapbase;
411       curr_heapbase=tmp;
412
413       tmp=to_heaptop;
414       to_heaptop=curr_heaptop;
415       curr_heaptop=tmp;
416       
417       tmp=to_heapptr;
418       curr_heapptr=to_heapptr+size;
419       curr_heapgcpoint=((char *) curr_heapbase)+GCPOINT(curr_heaptop-curr_heapbase);
420       to_heapptr=to_heapbase;
421       
422       /* Not enough room :(, redo gc */
423       if (curr_heapptr>curr_heapgcpoint) {
424 #ifdef THREADS
425         pthread_mutex_unlock(&gclock);
426 #endif
427         return mygcmalloc(stackptr, size);
428       }
429       
430       bzero(tmp, curr_heaptop-tmp);
431 #ifdef THREADS
432       pthread_mutex_unlock(&gclock);
433 #endif
434       return tmp;
435     }
436   } else {
437 #ifdef THREADS
438     pthread_mutex_unlock(&gclock);
439 #endif
440     return ptr;
441   }
442 }
443
444
445 int gc_createcopy(void * orig, void ** copy_ptr) {
446   if (orig==0) {
447     *copy_ptr=NULL;
448     return 0;
449   } else {
450     int type=((int *)orig)[0];
451     if (type==-1) {
452       *copy_ptr=((void **)orig)[1];
453       return 0;
454     } if (type<NUMCLASSES) {
455       /* We have a normal object */
456       int size=classsize[type];
457       void *newobj=tomalloc(size);
458       memcpy(newobj, orig, size);
459       ((int *)orig)[0]=-1;
460       ((void **)orig)[1]=newobj;
461       *copy_ptr=newobj;
462       return 1;
463     } else {
464       /* We have an array */
465       struct ArrayObject *ao=(struct ArrayObject *)orig;
466       int elementsize=classsize[type];
467       int length=ao->___length___;
468       int size=sizeof(struct ArrayObject)+length*elementsize;
469       void *newobj=tomalloc(size);
470       memcpy(newobj, orig, size);
471       ((int *)orig)[0]=-1;
472       ((void **)orig)[1]=newobj;
473       *copy_ptr=newobj;
474       return 1;
475     }
476   }
477 }