bug fixes
[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 "chash.h"
7 #include "GenericHashtable.h"
8 #include <string.h>
9 #if defined(THREADS) || defined(DSTM) || defined(STM)
10 #include "thread.h"
11 #endif
12
13 #ifdef DMALLOC
14 #include "dmalloc.h"
15 #endif
16 #ifdef DSTM
17 #include "dstm.h"
18 #endif
19 #ifdef STM
20 #include "tm.h"
21 #endif
22
23 #define NUMPTRS 100
24
25 #define INITIALHEAPSIZE 128*1024*1024
26 #define GCPOINT(x) ((int)((x)*0.95))
27 /* This define takes in how full the heap is initially and returns a new heap size to use */
28 #define HEAPSIZE(x,y) ((int)(x+y))*2
29
30 #ifdef TASK
31 extern struct genhashtable * activetasks;
32 #ifndef MULTICORE
33 extern struct parameterwrapper * objectqueues[NUMCLASSES];
34 #endif
35 extern struct genhashtable * failedtasks;
36 extern struct taskparamdescriptor *currtpd;
37 extern struct ctable *forward;
38 extern struct ctable *reverse;
39 extern struct RuntimeHash *fdtoobject;
40 #endif
41
42 #if defined(THREADS) || defined(DSTM) || defined(STM)
43 int needtocollect=0;
44 struct listitem * list=NULL;
45 int listcount=0;
46 #endif
47
48 //Need to check if pointers are transaction pointers
49 //this also catches the special flag value of 1 for local copies
50 #ifdef DSTM
51 #define ENQUEUE(orig, dst) \
52   if ((!(((unsigned int)orig)&0x1))) { \
53     if (orig>=curr_heapbase&&orig<curr_heaptop) { \
54       void *copy; \
55       if (gc_createcopy(orig,&copy)) \
56         enqueue(orig);\
57       dst=copy; \
58     } \
59   }
60 #elif defined(STM)
61 #define ENQUEUE(orig, dst) \
62   if (orig>=curr_heapbase&&orig<curr_heaptop) { \
63     void *copy; \
64     if (gc_createcopy(orig,&copy)) \
65       enqueue(orig);\
66     dst=copy; \
67   }
68 #define SENQUEUE(orig, dst) \
69   { \
70     void *copy; \
71     if (gc_createcopy(orig,&copy)) \
72       enqueue(orig);\
73     dst=copy; \
74   }
75 #elif defined(FASTCHECK)
76 #define ENQUEUE(orig, dst) \
77   if (((unsigned int)orig)!=1) { \
78     void *copy; \
79     if (gc_createcopy(orig,&copy)) \
80       enqueue(orig);\
81     dst=copy; }
82 #else
83 #define ENQUEUE(orig, dst) \
84   void *copy; \
85   if (gc_createcopy(orig,&copy)) \
86     enqueue(orig);\
87   dst=copy
88 #endif
89
90 struct pointerblock {
91   void * ptrs[NUMPTRS];
92   struct pointerblock *next;
93 };
94
95 void * curr_heapbase=0;
96 void * curr_heapptr=0;
97 void * curr_heapgcpoint=0;
98 void * curr_heaptop=0;
99
100 void * to_heapbase=0;
101 void * to_heapptr=0;
102 void * to_heaptop=0;
103 long lastgcsize=0;
104
105 struct pointerblock *head=NULL;
106 int headindex=0;
107 struct pointerblock *tail=NULL;
108 int tailindex=0;
109 struct pointerblock *spare=NULL;
110
111 void enqueue(void *ptr) {
112   if (headindex==NUMPTRS) {
113     struct pointerblock * tmp;
114     if (spare!=NULL) {
115       tmp=spare;
116       spare=NULL;
117     } else
118       tmp=malloc(sizeof(struct pointerblock));
119     head->next=tmp;
120     head=tmp;
121     headindex=0;
122   }
123   head->ptrs[headindex++]=ptr;
124 }
125
126 void * dequeue() {
127   if (tailindex==NUMPTRS) {
128     struct pointerblock *tmp=tail;
129     tail=tail->next;
130     tailindex=0;
131     if (spare!=NULL)
132       free(tmp);
133     else
134       spare=tmp;
135   }
136   return tail->ptrs[tailindex++];
137 }
138
139 #ifdef STM
140 void fixtable(chashlistnode_t ** tc_table, unsigned int tc_size) {
141   unsigned int mask=(tc_size<<1)-1;
142   chashlistnode_t *node=calloc(tc_size, sizeof(chashlistnode_t));
143   chashlistnode_t *ptr=*tc_table;
144   chashlistnode_t *curr;
145   unsigned int i;
146   unsigned int index;
147   int isfirst;
148   for(i=0;i<tc_size;i++) {
149     curr=&ptr[i];
150     isfirst=1;
151     do {                      //Inner loop to go through linked lists
152       void * key;
153       chashlistnode_t *tmp,*next;
154       
155       if ((key=(void *)curr->key) == 0) {             //Exit inner loop if there the first element is 0
156         break;                  //key = val =0 for element if not present within the hash table
157       }
158       SENQUEUE(key, key);
159       if (key>=curr_heapbase&&key<curr_heaptop) {
160         SENQUEUE(curr->val, curr->val);
161       } else {
162         //rewrite transaction cache entry
163         void *ptr=curr->val;
164         int type=((int *)ptr)[0];
165         unsigned int *pointer=pointerarray[type];
166         if (pointer==0) {
167           //array of primitives - do nothing
168         } else if (((int)pointer)==1) {
169           //array of pointers
170           struct ArrayObject *ao=(struct ArrayObject *) ptr;
171           int length=ao->___length___;
172           int i;
173           for(i=0; i<length; i++) {
174             void *objptr=((void **)(((char *)&ao->___length___)+sizeof(int)))[i];
175             ENQUEUE(objptr, ((void **)(((char *)&ao->___length___)+sizeof(int)))[i]);
176           }
177         } else {
178           int size=pointer[0];
179           int i;
180           for(i=1; i<=size; i++) {
181             unsigned int offset=pointer[i];
182             void * objptr=*((void **)(((int)ptr)+offset));
183             ENQUEUE(objptr, *((void **)(((int)ptr)+offset)));
184           }
185         }
186       }
187
188       next = curr->next;
189       index = (((unsigned int)key) & mask) >>1;
190
191       curr->key=(unsigned int)key;
192       tmp=&node[index];
193       // Insert into the new table
194       if(tmp->key == 0) {
195         tmp->key = curr->key;
196         tmp->val = curr->val;
197         if (!isfirst) {
198           free(curr);
199         }
200       } else {
201         curr->next=tmp->next;
202         tmp->next=curr;
203       }
204       isfirst = 0;
205       curr = next;
206     } while(curr!=NULL);
207   }
208   free(ptr);
209   *tc_table=node;
210 }
211 #endif
212
213 int moreItems() {
214   if ((head==tail)&&(tailindex==headindex))
215     return 0;
216   return 1;
217 }
218
219 #ifdef TASK
220 struct pointerblock *taghead=NULL;
221 int tagindex=0;
222
223 void enqueuetag(struct ___TagDescriptor___ *ptr) {
224   if (tagindex==NUMPTRS) {
225     struct pointerblock * tmp=malloc(sizeof(struct pointerblock));
226     tmp->next=taghead;
227     taghead=tmp;
228     tagindex=0;
229   }
230   taghead->ptrs[tagindex++]=ptr;
231 }
232 #endif
233
234
235 void collect(struct garbagelist * stackptr) {
236 #if defined(THREADS)||defined(DSTM)||defined(STM)
237   needtocollect=1;
238   pthread_mutex_lock(&gclistlock);
239   while(1) {
240     if ((listcount+1)==threadcount) {
241       break; /* Have all other threads stopped */
242     }
243     pthread_cond_wait(&gccond, &gclistlock);
244   }
245 #endif
246
247   if (head==NULL) {
248     headindex=0;
249     tailindex=0;
250     head=tail=malloc(sizeof(struct pointerblock));
251   }
252
253 #ifdef TASK
254   if (taghead==NULL) {
255     tagindex=0;
256     taghead=malloc(sizeof(struct pointerblock));
257     taghead->next=NULL;
258   }
259 #endif
260
261   /* Check current stack */
262 #if defined(THREADS)||defined(DSTM)||defined(STM)
263   {
264     struct listitem *listptr=list;
265     while(1) {
266 #endif
267
268   while(stackptr!=NULL) {
269     int i;
270     for(i=0; i<stackptr->size; i++) {
271       void * orig=stackptr->array[i];
272       ENQUEUE(orig, stackptr->array[i]);
273     }
274     stackptr=stackptr->next;
275   }
276 #if defined(THREADS)||defined(DSTM)||defined(STM)
277   /* Go to next thread */
278   if (listptr!=NULL) {
279 #ifdef THREADS
280     void * orig=listptr->locklist;
281     ENQUEUE(orig, listptr->locklist);
282 #endif
283 #ifdef STM
284     if ((*listptr->tc_table)!=NULL)
285       fixtable(listptr->tc_table, listptr->tc_size);
286 #endif
287     stackptr=listptr->stackptr;
288     listptr=listptr->next;
289   } else
290     break;
291 }
292 }
293 #endif
294
295 #ifdef FASTCHECK
296   ENQUEUE(___fcrevert___, ___fcrevert___);
297 #endif
298
299 #ifdef TASK
300   {
301     /* Update objectsets */
302     int i;
303     for(i=0; i<NUMCLASSES; i++) {
304 #if !defined(MULTICORE)
305       struct parameterwrapper * p=objectqueues[i];
306       while(p!=NULL) {
307         struct ObjectHash * set=p->objectset;
308         struct ObjectNode * ptr=set->listhead;
309         while(ptr!=NULL) {
310           void *orig=(void *)ptr->key;
311           ENQUEUE(orig, *((void **)(&ptr->key)));
312           ptr=ptr->lnext;
313         }
314         ObjectHashrehash(set); /* Rehash the table */
315         p=p->next;
316       }
317 #endif
318     }
319   }
320
321 #ifndef FASTCHECK
322   if (forward!=NULL) {
323     struct cnode * ptr=forward->listhead;
324     while(ptr!=NULL) {
325       void * orig=(void *)ptr->key;
326       ENQUEUE(orig, *((void **)(&ptr->key)));
327       ptr=ptr->lnext;
328     }
329     crehash(forward); /* Rehash the table */
330   }
331
332   if (reverse!=NULL) {
333     struct cnode * ptr=reverse->listhead;
334     while(ptr!=NULL) {
335       void *orig=(void *)ptr->val;
336       ENQUEUE(orig, *((void**)(&ptr->val)));
337       ptr=ptr->lnext;
338     }
339   }
340 #endif
341
342   {
343     struct RuntimeNode * ptr=fdtoobject->listhead;
344     while(ptr!=NULL) {
345       void *orig=(void *)ptr->data;
346       ENQUEUE(orig, *((void**)(&ptr->data)));
347       ptr=ptr->lnext;
348     }
349   }
350
351   {
352     /* Update current task descriptor */
353     int i;
354     for(i=0; i<currtpd->numParameters; i++) {
355       void *orig=currtpd->parameterArray[i];
356       ENQUEUE(orig, currtpd->parameterArray[i]);
357     }
358
359   }
360
361   /* Update active tasks */
362   {
363     struct genpointerlist * ptr=activetasks->list;
364     while(ptr!=NULL) {
365       struct taskparamdescriptor *tpd=ptr->src;
366       int i;
367       for(i=0; i<tpd->numParameters; i++) {
368         void * orig=tpd->parameterArray[i];
369         ENQUEUE(orig, tpd->parameterArray[i]);
370       }
371       ptr=ptr->inext;
372     }
373     genrehash(activetasks);
374   }
375
376   /* Update failed tasks */
377   {
378     struct genpointerlist * ptr=failedtasks->list;
379     while(ptr!=NULL) {
380       struct taskparamdescriptor *tpd=ptr->src;
381       int i;
382       for(i=0; i<tpd->numParameters; i++) {
383         void * orig=tpd->parameterArray[i];
384         ENQUEUE(orig, tpd->parameterArray[i]);
385       }
386       ptr=ptr->inext;
387     }
388     genrehash(failedtasks);
389   }
390 #endif
391
392   while(moreItems()) {
393     void * ptr=dequeue();
394     void *cpy=((void **)ptr)[1];
395     int type=((int *)cpy)[0];
396     unsigned int * pointer;
397 #ifdef TASK
398     if(type==TAGTYPE) {
399       /* Enqueue Tag */
400       /* Nothing is inside */
401       enqueuetag(ptr);
402       continue;
403     }
404 #endif
405     pointer=pointerarray[type];
406     if (pointer==0) {
407       /* Array of primitives */
408       /* Do nothing */
409 #if defined(DSTM)||defined(FASTCHECK)
410       struct ArrayObject *ao=(struct ArrayObject *) ptr;
411       struct ArrayObject *ao_cpy=(struct ArrayObject *) cpy;
412       ENQUEUE((void *)ao->___nextobject___, *((void **)&ao_cpy->___nextobject___));
413       ENQUEUE((void *)ao->___localcopy___, *((void **)&ao_cpy->___localcopy___));
414 #endif
415     } else if (((int)pointer)==1) {
416       /* Array of pointers */
417       struct ArrayObject *ao=(struct ArrayObject *) ptr;
418       struct ArrayObject *ao_cpy=(struct ArrayObject *) cpy;
419 #if (defined(DSTM)||defined(FASTCHECK))
420       ENQUEUE((void *)ao->___nextobject___, *((void **)&ao_cpy->___nextobject___));
421       ENQUEUE((void *)ao->___localcopy___, *((void **)&ao_cpy->___localcopy___));
422 #endif
423       int length=ao->___length___;
424       int i;
425       for(i=0; i<length; i++) {
426         void *objptr=((void **)(((char *)&ao->___length___)+sizeof(int)))[i];
427         ENQUEUE(objptr, ((void **)(((char *)&ao_cpy->___length___)+sizeof(int)))[i]);
428       }
429     } else {
430       int size=pointer[0];
431       int i;
432       for(i=1; i<=size; i++) {
433         unsigned int offset=pointer[i];
434         void * objptr=*((void **)(((int)ptr)+offset));
435         ENQUEUE(objptr, *((void **)(((int)cpy)+offset)));
436       }
437     }
438   }
439 #ifdef TASK
440   fixtags();
441 #endif
442
443 #if defined(THREADS)||defined(DSTM)||defined(STM)
444   needtocollect=0;
445   pthread_mutex_unlock(&gclistlock);
446 #endif
447 }
448
449 #ifdef TASK
450 /* Fix up the references from tags.  This can't be done earlier,
451    because we don't want tags to keep objects alive */
452 void fixtags() {
453   while(taghead!=NULL) {
454     int i;
455     struct pointerblock *tmp=taghead->next;
456     for(i=0; i<tagindex; i++) {
457       struct ___TagDescriptor___ *tagd=taghead->ptrs[i];
458       struct ___Object___ *obj=tagd->flagptr;
459       struct ___TagDescriptor___ *copy=((struct ___TagDescriptor___**)tagd)[1];
460       if (obj==NULL) {
461         /* Zero object case */
462       } else if (obj->type==-1) {
463         /* Single object case */
464         copy->flagptr=((struct ___Object___**)obj)[1];
465       } else if (obj->type==OBJECTARRAYTYPE) {
466         /* Array case */
467         struct ArrayObject *ao=(struct ArrayObject *) obj;
468         int livecount=0;
469         int j;
470         int k=0;
471         struct ArrayObject *aonew;
472
473         /* Count live objects */
474         for(j=0; j<ao->___cachedCode___; j++) {
475           struct ___Object___ * tobj=ARRAYGET(ao, struct ___Object___ *, j);
476           if (tobj->type==-1)
477             livecount++;
478         }
479
480         livecount=((livecount-1)/OBJECTARRAYINTERVAL+1)*OBJECTARRAYINTERVAL;
481         aonew=(struct ArrayObject *) tomalloc(sizeof(struct ArrayObject)+sizeof(struct ___Object___*)*livecount);
482         memcpy(aonew, ao, sizeof(struct ArrayObject));
483         aonew->type=OBJECTARRAYTYPE;
484         aonew->___length___=livecount;
485         copy->flagptr=aonew;
486         for(j=0; j<ao->___cachedCode___; j++) {
487           struct ___Object___ * tobj=ARRAYGET(ao, struct ___Object___ *, j);
488           if (tobj->type==-1) {
489             struct ___Object___ * tobjcpy=((struct ___Object___**)tobj)[1];
490             ARRAYSET(aonew, struct ___Object___*, k++,tobjcpy);
491           }
492         }
493         aonew->___cachedCode___=k;
494         for(; k<livecount; k++) {
495           ARRAYSET(aonew, struct ___Object___*, k, NULL);
496         }
497       } else {
498         /* No object live anymore */
499         copy->flagptr=NULL;
500       }
501     }
502     free(taghead);
503     taghead=tmp;
504     tagindex=NUMPTRS;
505   }
506 }
507 #endif
508
509 void * tomalloc(int size) {
510   void * ptr=to_heapptr;
511   if ((size&7)!=0)
512     size+=(8-(size%8));
513   to_heapptr+=size;
514   return ptr;
515 }
516
517 #if defined(THREADS)||defined(DSTM)||defined(STM)
518 void checkcollect(void * ptr) {
519   struct listitem * tmp=stopforgc((struct garbagelist *)ptr);
520   pthread_mutex_lock(&gclock); // Wait for GC
521   restartaftergc(tmp);
522   pthread_mutex_unlock(&gclock);
523 }
524
525 #ifdef DSTM
526 void checkcollect2(void * ptr) {
527   int ptrarray[]={1, (int)ptr, (int) revertlist};
528   struct listitem * tmp=stopforgc((struct garbagelist *)ptrarray);
529   pthread_mutex_lock(&gclock); // Wait for GC
530   restartaftergc(tmp);
531   pthread_mutex_unlock(&gclock);
532   revertlist=(struct ___Object___*)ptrarray[2];
533 }
534 #endif
535
536
537 struct listitem * stopforgc(struct garbagelist * ptr) {
538   struct listitem * litem=malloc(sizeof(struct listitem));
539   litem->stackptr=ptr;
540 #ifdef THREADS
541   litem->locklist=pthread_getspecific(threadlocks);
542 #endif
543 #ifdef STM
544   litem->tc_size=c_size;
545   litem->tc_table=&c_table;
546 #endif
547   litem->prev=NULL;
548   pthread_mutex_lock(&gclistlock);
549   litem->next=list;
550   if(list!=NULL)
551     list->prev=litem;
552   list=litem;
553   listcount++;
554   pthread_cond_signal(&gccond);
555   pthread_mutex_unlock(&gclistlock);
556   return litem;
557 }
558
559 void restartaftergc(struct listitem * litem) {
560   pthread_mutex_lock(&gclistlock);
561 #ifdef THREADS
562   pthread_setspecific(threadlocks, litem->locklist);
563 #endif
564   if (litem->prev==NULL) {
565     list=litem->next;
566   } else {
567     litem->prev->next=litem->next;
568   }
569   if (litem->next!=NULL) {
570     litem->next->prev=litem->prev;
571   }
572   listcount--;
573   pthread_mutex_unlock(&gclistlock);
574   free(litem);
575 }
576 #endif
577
578 void * mygcmalloc(struct garbagelist * stackptr, int size) {
579   void *ptr;
580 #if defined(THREADS)||defined(DSTM)||defined(STM)
581   if (pthread_mutex_trylock(&gclock)!=0) {
582     struct listitem *tmp=stopforgc(stackptr);
583     pthread_mutex_lock(&gclock);
584     restartaftergc(tmp);
585   }
586 #endif
587   ptr=curr_heapptr;
588   if ((size&7)!=0)
589     size+=(8-(size%8));
590   curr_heapptr+=size;
591   if (curr_heapptr>curr_heapgcpoint) {
592     if (curr_heapbase==0) {
593       /* Need to allocate base heap */
594       curr_heapbase=malloc(INITIALHEAPSIZE);
595       bzero(curr_heapbase, INITIALHEAPSIZE);
596       curr_heaptop=curr_heapbase+INITIALHEAPSIZE;
597       curr_heapgcpoint=((char *) curr_heapbase)+GCPOINT(INITIALHEAPSIZE);
598       curr_heapptr=curr_heapbase+size;
599
600       to_heapbase=malloc(INITIALHEAPSIZE);
601       to_heaptop=to_heapbase+INITIALHEAPSIZE;
602       to_heapptr=to_heapbase;
603       ptr=curr_heapbase;
604 #if defined(THREADS)||defined(DSTM)||defined(STM)
605       pthread_mutex_unlock(&gclock);
606 #endif
607       return ptr;
608     }
609
610     /* Grow the to heap if necessary */
611     {
612       int curr_heapsize=curr_heaptop-curr_heapbase;
613       int to_heapsize=to_heaptop-to_heapbase;
614       int last_heapsize=0;
615       if (lastgcsize>0) {
616         last_heapsize=HEAPSIZE(lastgcsize, size);
617         if ((last_heapsize&7)!=0)
618           last_heapsize+=(8-(last_heapsize%8));
619       }
620       if (curr_heapsize>last_heapsize)
621         last_heapsize=curr_heapsize;
622       if (last_heapsize>to_heapsize) {
623         free(to_heapbase);
624         to_heapbase=malloc(last_heapsize);
625         if (to_heapbase==NULL) {
626           printf("Error Allocating enough memory\n");
627           exit(-1);
628         }
629         to_heaptop=to_heapbase+last_heapsize;
630         to_heapptr=to_heapbase;
631       }
632     }
633
634     /* Do our collection */
635     collect(stackptr);
636
637     /* Update stat on previous gc size */
638     lastgcsize=(to_heapptr-to_heapbase)+size;
639
640 #ifdef GARBAGESTATS
641     printf("Garbage collected: Old bytes: %u\n", curr_heapptr-curr_heapbase);
642     printf("New space: %u\n", to_heapptr-to_heapbase);
643     printf("Total space: %u\n", to_heaptop-to_heapbase);
644 #endif
645     /* Flip to/curr heaps */
646     {
647       void * tmp=to_heapbase;
648       to_heapbase=curr_heapbase;
649       curr_heapbase=tmp;
650
651       tmp=to_heaptop;
652       to_heaptop=curr_heaptop;
653       curr_heaptop=tmp;
654
655       tmp=to_heapptr;
656       curr_heapptr=to_heapptr+size;
657       curr_heapgcpoint=((char *) curr_heapbase)+GCPOINT(curr_heaptop-curr_heapbase);
658       to_heapptr=to_heapbase;
659
660       /* Not enough room :(, redo gc */
661       if (curr_heapptr>curr_heapgcpoint) {
662 #if defined(THREADS)||defined(DSTM)||defined(STM)
663         pthread_mutex_unlock(&gclock);
664 #endif
665         return mygcmalloc(stackptr, size);
666       }
667
668       bzero(tmp, curr_heaptop-tmp);
669 #if defined(THREADS)||defined(DSTM)||defined(STM)
670       pthread_mutex_unlock(&gclock);
671 #endif
672       return tmp;
673     }
674   } else {
675 #if defined(THREADS)||defined(DSTM)||defined(STM)
676     pthread_mutex_unlock(&gclock);
677 #endif
678     return ptr;
679   }
680 }
681
682
683 int gc_createcopy(void * orig, void ** copy_ptr) {
684   if (orig==0) {
685     *copy_ptr=NULL;
686     return 0;
687   } else {
688     int type=((int *)orig)[0];
689     if (type==-1) {
690       *copy_ptr=((void **)orig)[1];
691       return 0;
692     }
693     if (type<NUMCLASSES) {
694       /* We have a normal object */
695       int size=classsize[type];
696       void *newobj=tomalloc(size);
697       memcpy(newobj, orig, size);
698       ((int *)orig)[0]=-1;
699       ((void **)orig)[1]=newobj;
700       *copy_ptr=newobj;
701       return 1;
702     } else {
703       /* We have an array */
704       struct ArrayObject *ao=(struct ArrayObject *)orig;
705       int elementsize=classsize[type];
706       int length=ao->___length___;
707       int size=sizeof(struct ArrayObject)+length*elementsize;
708       void *newobj=tomalloc(size);
709       memcpy(newobj, orig, size);
710       ((int *)orig)[0]=-1;
711       ((void **)orig)[1]=newobj;
712       *copy_ptr=newobj;
713       return 1;
714     }
715   }
716 }