memory stuff
[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 32*1024*1024
26 #define GCPOINT(x) ((int)((x)*0.9))
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)/0.6))+y)
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 STM
61 #define ENQUEUE(orig, dst) \
62   if ((!(((unsigned int)orig)&0x1))) { \
63     if (orig>=curr_heapbase&&orig<curr_heaptop) { \
64       void *copy; \
65       if (gc_createcopy(orig,&copy)) \
66         enqueue(orig);\
67       dst=copy; \
68     } \
69   }
70 #elif defined(FASTCHECK)
71 #define ENQUEUE(orig, dst) \
72   if (((unsigned int)orig)!=1) { \
73     void *copy; \
74     if (gc_createcopy(orig,&copy)) \
75       enqueue(orig);\
76     dst=copy; }
77 #else
78 #define ENQUEUE(orig, dst) \
79   void *copy; \
80   if (gc_createcopy(orig,&copy)) \
81     enqueue(orig);\
82   dst=copy
83 #endif
84
85 struct pointerblock {
86   void * ptrs[NUMPTRS];
87   struct pointerblock *next;
88 };
89
90 void * curr_heapbase=0;
91 void * curr_heapptr=0;
92 void * curr_heapgcpoint=0;
93 void * curr_heaptop=0;
94
95 void * to_heapbase=0;
96 void * to_heapptr=0;
97 void * to_heaptop=0;
98 long lastgcsize=0;
99
100 struct pointerblock *head=NULL;
101 int headindex=0;
102 struct pointerblock *tail=NULL;
103 int tailindex=0;
104 struct pointerblock *spare=NULL;
105
106 void enqueue(void *ptr) {
107   if (headindex==NUMPTRS) {
108     struct pointerblock * tmp;
109     if (spare!=NULL) {
110       tmp=spare;
111       spare=NULL;
112     } else
113       tmp=malloc(sizeof(struct pointerblock));
114     head->next=tmp;
115     head=tmp;
116     headindex=0;
117   }
118   head->ptrs[headindex++]=ptr;
119 }
120
121 void * dequeue() {
122   if (tailindex==NUMPTRS) {
123     struct pointerblock *tmp=tail;
124     tail=tail->next;
125     tailindex=0;
126     if (spare!=NULL)
127       free(tmp);
128     else
129       spare=tmp;
130   }
131   return tail->ptrs[tailindex++];
132 }
133
134 int moreItems() {
135   if ((head==tail)&&(tailindex==headindex))
136     return 0;
137   return 1;
138 }
139
140 #ifdef TASK
141 struct pointerblock *taghead=NULL;
142 int tagindex=0;
143
144 void enqueuetag(struct ___TagDescriptor___ *ptr) {
145   if (tagindex==NUMPTRS) {
146     struct pointerblock * tmp=malloc(sizeof(struct pointerblock));
147     tmp->next=taghead;
148     taghead=tmp;
149     tagindex=0;
150   }
151   taghead->ptrs[tagindex++]=ptr;
152 }
153 #endif
154
155
156 void collect(struct garbagelist * stackptr) {
157 #if defined(THREADS)||defined(DSTM)||defined(STM)
158   needtocollect=1;
159   pthread_mutex_lock(&gclistlock);
160   while(1) {
161     if ((listcount+1)==threadcount) {
162       break; /* Have all other threads stopped */
163     }
164     pthread_cond_wait(&gccond, &gclistlock);
165   }
166 #endif
167
168   if (head==NULL) {
169     headindex=0;
170     tailindex=0;
171     head=tail=malloc(sizeof(struct pointerblock));
172   }
173
174 #ifdef TASK
175   if (taghead==NULL) {
176     tagindex=0;
177     taghead=malloc(sizeof(struct pointerblock));
178     taghead->next=NULL;
179   }
180 #endif
181
182   /* Check current stack */
183 #if defined(THREADS)||defined(DSTM)||defined(STM)
184   {
185     struct listitem *listptr=list;
186     while(1) {
187 #endif
188
189   while(stackptr!=NULL) {
190     int i;
191     for(i=0; i<stackptr->size; i++) {
192       void * orig=stackptr->array[i];
193       ENQUEUE(orig, stackptr->array[i]);
194     }
195     stackptr=stackptr->next;
196   }
197 #if defined(THREADS)||defined(DSTM)||defined(STM)
198   /* Go to next thread */
199   if (listptr!=NULL) {
200     void * orig=listptr->locklist;
201     ENQUEUE(orig, listptr->locklist);
202     stackptr=listptr->stackptr;
203     listptr=listptr->next;
204   } else
205     break;
206 }
207 }
208 #endif
209
210 #ifdef FASTCHECK
211   ENQUEUE(___fcrevert___, ___fcrevert___);
212 #endif
213
214 #ifdef TASK
215   {
216     /* Update objectsets */
217     int i;
218     for(i=0; i<NUMCLASSES; i++) {
219 #if !defined(MULTICORE)
220       struct parameterwrapper * p=objectqueues[i];
221       while(p!=NULL) {
222         struct ObjectHash * set=p->objectset;
223         struct ObjectNode * ptr=set->listhead;
224         while(ptr!=NULL) {
225           void *orig=(void *)ptr->key;
226           ENQUEUE(orig, *((void **)(&ptr->key)));
227           ptr=ptr->lnext;
228         }
229         ObjectHashrehash(set); /* Rehash the table */
230         p=p->next;
231       }
232 #endif
233     }
234   }
235
236 #ifndef FASTCHECK
237   if (forward!=NULL) {
238     struct cnode * ptr=forward->listhead;
239     while(ptr!=NULL) {
240       void * orig=(void *)ptr->key;
241       ENQUEUE(orig, *((void **)(&ptr->key)));
242       ptr=ptr->lnext;
243     }
244     crehash(forward); /* Rehash the table */
245   }
246
247   if (reverse!=NULL) {
248     struct cnode * ptr=reverse->listhead;
249     while(ptr!=NULL) {
250       void *orig=(void *)ptr->val;
251       ENQUEUE(orig, *((void**)(&ptr->val)));
252       ptr=ptr->lnext;
253     }
254   }
255 #endif
256
257   {
258     struct RuntimeNode * ptr=fdtoobject->listhead;
259     while(ptr!=NULL) {
260       void *orig=(void *)ptr->data;
261       ENQUEUE(orig, *((void**)(&ptr->data)));
262       ptr=ptr->lnext;
263     }
264   }
265
266   {
267     /* Update current task descriptor */
268     int i;
269     for(i=0; i<currtpd->numParameters; i++) {
270       void *orig=currtpd->parameterArray[i];
271       ENQUEUE(orig, currtpd->parameterArray[i]);
272     }
273
274   }
275
276   /* Update active tasks */
277   {
278     struct genpointerlist * ptr=activetasks->list;
279     while(ptr!=NULL) {
280       struct taskparamdescriptor *tpd=ptr->src;
281       int i;
282       for(i=0; i<tpd->numParameters; i++) {
283         void * orig=tpd->parameterArray[i];
284         ENQUEUE(orig, tpd->parameterArray[i]);
285       }
286       ptr=ptr->inext;
287     }
288     genrehash(activetasks);
289   }
290
291   /* Update failed tasks */
292   {
293     struct genpointerlist * ptr=failedtasks->list;
294     while(ptr!=NULL) {
295       struct taskparamdescriptor *tpd=ptr->src;
296       int i;
297       for(i=0; i<tpd->numParameters; i++) {
298         void * orig=tpd->parameterArray[i];
299         ENQUEUE(orig, tpd->parameterArray[i]);
300       }
301       ptr=ptr->inext;
302     }
303     genrehash(failedtasks);
304   }
305 #endif
306
307   while(moreItems()) {
308     void * ptr=dequeue();
309     void *cpy=((void **)ptr)[1];
310     int type=((int *)cpy)[0];
311     unsigned int * pointer;
312 #ifdef TASK
313     if(type==TAGTYPE) {
314       /* Enqueue Tag */
315       /* Nothing is inside */
316       enqueuetag(ptr);
317       continue;
318     }
319 #endif
320     pointer=pointerarray[type];
321     if (pointer==0) {
322       /* Array of primitives */
323       /* Do nothing */
324 #if defined(DSTM)||defined(FASTCHECK)
325       struct ArrayObject *ao=(struct ArrayObject *) ptr;
326       struct ArrayObject *ao_cpy=(struct ArrayObject *) cpy;
327       ENQUEUE((void *)ao->___nextobject___, *((void **)&ao_cpy->___nextobject___));
328       ENQUEUE((void *)ao->___localcopy___, *((void **)&ao_cpy->___localcopy___));
329 #endif
330     } else if (((int)pointer)==1) {
331       /* Array of pointers */
332       struct ArrayObject *ao=(struct ArrayObject *) ptr;
333       struct ArrayObject *ao_cpy=(struct ArrayObject *) cpy;
334 #if (defined(DSTM)||defined(FASTCHECK))
335       ENQUEUE((void *)ao->___nextobject___, *((void **)&ao_cpy->___nextobject___));
336       ENQUEUE((void *)ao->___localcopy___, *((void **)&ao_cpy->___localcopy___));
337 #endif
338       int length=ao->___length___;
339       int i;
340       for(i=0; i<length; i++) {
341         void *objptr=((void **)(((char *)&ao->___length___)+sizeof(int)))[i];
342         ENQUEUE(objptr, ((void **)(((char *)&ao_cpy->___length___)+sizeof(int)))[i]);
343       }
344     } else {
345       int size=pointer[0];
346       int i;
347       for(i=1; i<=size; i++) {
348         unsigned int offset=pointer[i];
349         void * objptr=*((void **)(((int)ptr)+offset));
350         ENQUEUE(objptr, *((void **)(((int)cpy)+offset)));
351       }
352     }
353   }
354 #ifdef TASK
355   fixtags();
356 #endif
357
358 #if defined(THREADS)||defined(DSTM)
359   needtocollect=0;
360   pthread_mutex_unlock(&gclistlock);
361 #endif
362 }
363
364 #ifdef TASK
365 /* Fix up the references from tags.  This can't be done earlier,
366    because we don't want tags to keep objects alive */
367 void fixtags() {
368   while(taghead!=NULL) {
369     int i;
370     struct pointerblock *tmp=taghead->next;
371     for(i=0; i<tagindex; i++) {
372       struct ___TagDescriptor___ *tagd=taghead->ptrs[i];
373       struct ___Object___ *obj=tagd->flagptr;
374       struct ___TagDescriptor___ *copy=((struct ___TagDescriptor___**)tagd)[1];
375       if (obj==NULL) {
376         /* Zero object case */
377       } else if (obj->type==-1) {
378         /* Single object case */
379         copy->flagptr=((struct ___Object___**)obj)[1];
380       } else if (obj->type==OBJECTARRAYTYPE) {
381         /* Array case */
382         struct ArrayObject *ao=(struct ArrayObject *) obj;
383         int livecount=0;
384         int j;
385         int k=0;
386         struct ArrayObject *aonew;
387
388         /* Count live objects */
389         for(j=0; j<ao->___cachedCode___; j++) {
390           struct ___Object___ * tobj=ARRAYGET(ao, struct ___Object___ *, j);
391           if (tobj->type==-1)
392             livecount++;
393         }
394
395         livecount=((livecount-1)/OBJECTARRAYINTERVAL+1)*OBJECTARRAYINTERVAL;
396         aonew=(struct ArrayObject *) tomalloc(sizeof(struct ArrayObject)+sizeof(struct ___Object___*)*livecount);
397         memcpy(aonew, ao, sizeof(struct ArrayObject));
398         aonew->type=OBJECTARRAYTYPE;
399         aonew->___length___=livecount;
400         copy->flagptr=aonew;
401         for(j=0; j<ao->___cachedCode___; j++) {
402           struct ___Object___ * tobj=ARRAYGET(ao, struct ___Object___ *, j);
403           if (tobj->type==-1) {
404             struct ___Object___ * tobjcpy=((struct ___Object___**)tobj)[1];
405             ARRAYSET(aonew, struct ___Object___*, k++,tobjcpy);
406           }
407         }
408         aonew->___cachedCode___=k;
409         for(; k<livecount; k++) {
410           ARRAYSET(aonew, struct ___Object___*, k, NULL);
411         }
412       } else {
413         /* No object live anymore */
414         copy->flagptr=NULL;
415       }
416     }
417     free(taghead);
418     taghead=tmp;
419     tagindex=NUMPTRS;
420   }
421 }
422 #endif
423
424 void * tomalloc(int size) {
425   void * ptr=to_heapptr;
426   if ((size&7)!=0)
427     size+=(8-(size%8));
428   to_heapptr+=size;
429   return ptr;
430 }
431
432 #if defined(THREADS)||defined(DSTM)||defined(STM)
433 void checkcollect(void * ptr) {
434   struct listitem * tmp=stopforgc((struct garbagelist *)ptr);
435   pthread_mutex_lock(&gclock); // Wait for GC
436   restartaftergc(tmp);
437   pthread_mutex_unlock(&gclock);
438 }
439
440 #ifdef DSTM
441 void checkcollect2(void * ptr) {
442   int ptrarray[]={1, (int)ptr, (int) revertlist};
443   struct listitem * tmp=stopforgc((struct garbagelist *)ptrarray);
444   pthread_mutex_lock(&gclock); // Wait for GC
445   restartaftergc(tmp);
446   pthread_mutex_unlock(&gclock);
447   revertlist=(struct ___Object___*)ptrarray[2];
448 }
449 #endif
450
451
452 struct listitem * stopforgc(struct garbagelist * ptr) {
453   struct listitem * litem=malloc(sizeof(struct listitem));
454   litem->stackptr=ptr;
455 #ifdef THREADS
456   litem->locklist=pthread_getspecific(threadlocks);
457 #endif
458 #ifdef STM
459   litem->tc_size=c_size;
460   litem->tc_mask=c_mask;
461   litem->tc_table=&c_table;
462 #endif
463   litem->prev=NULL;
464   pthread_mutex_lock(&gclistlock);
465   litem->next=list;
466   if(list!=NULL)
467     list->prev=litem;
468   list=litem;
469   listcount++;
470   pthread_cond_signal(&gccond);
471   pthread_mutex_unlock(&gclistlock);
472   return litem;
473 }
474
475 void restartaftergc(struct listitem * litem) {
476   pthread_mutex_lock(&gclistlock);
477   pthread_setspecific(threadlocks, litem->locklist);
478   if (litem->prev==NULL) {
479     list=litem->next;
480   } else {
481     litem->prev->next=litem->next;
482   }
483   if (litem->next!=NULL) {
484     litem->next->prev=litem->prev;
485   }
486   listcount--;
487   pthread_mutex_unlock(&gclistlock);
488   free(litem);
489 }
490 #endif
491
492 void * mygcmalloc(struct garbagelist * stackptr, int size) {
493   void *ptr;
494 #if defined(THREADS)||defined(DSTM)||defined(STM)
495   if (pthread_mutex_trylock(&gclock)!=0) {
496     struct listitem *tmp=stopforgc(stackptr);
497     pthread_mutex_lock(&gclock);
498     restartaftergc(tmp);
499   }
500 #endif
501   ptr=curr_heapptr;
502   if ((size&7)!=0)
503     size+=(8-(size%8));
504   curr_heapptr+=size;
505   if (curr_heapptr>curr_heapgcpoint) {
506     if (curr_heapbase==0) {
507       /* Need to allocate base heap */
508       curr_heapbase=malloc(INITIALHEAPSIZE);
509       bzero(curr_heapbase, INITIALHEAPSIZE);
510       curr_heaptop=curr_heapbase+INITIALHEAPSIZE;
511       curr_heapgcpoint=((char *) curr_heapbase)+GCPOINT(INITIALHEAPSIZE);
512       curr_heapptr=curr_heapbase+size;
513
514       to_heapbase=malloc(INITIALHEAPSIZE);
515       to_heaptop=to_heapbase+INITIALHEAPSIZE;
516       to_heapptr=to_heapbase;
517       ptr=curr_heapbase;
518 #if defined(THREADS)||defined(DSTM)||defined(STM)
519       pthread_mutex_unlock(&gclock);
520 #endif
521       return ptr;
522     }
523
524     /* Grow the to heap if necessary */
525     {
526       int curr_heapsize=curr_heaptop-curr_heapbase;
527       int to_heapsize=to_heaptop-to_heapbase;
528       int last_heapsize=0;
529       if (lastgcsize>0) {
530         last_heapsize=HEAPSIZE(lastgcsize, size);
531         if ((last_heapsize&7)!=0)
532           last_heapsize+=(8-(last_heapsize%8));
533       }
534       if (curr_heapsize>last_heapsize)
535         last_heapsize=curr_heapsize;
536       if (last_heapsize>to_heapsize) {
537         free(to_heapbase);
538         to_heapbase=malloc(last_heapsize);
539         if (to_heapbase==NULL) {
540           printf("Error Allocating enough memory\n");
541           exit(-1);
542         }
543         to_heaptop=to_heapbase+last_heapsize;
544         to_heapptr=to_heapbase;
545       }
546     }
547
548     /* Do our collection */
549     collect(stackptr);
550
551     /* Update stat on previous gc size */
552     lastgcsize=(to_heapptr-to_heapbase)+size;
553
554     /* Flip to/curr heaps */
555     {
556       void * tmp=to_heapbase;
557       to_heapbase=curr_heapbase;
558       curr_heapbase=tmp;
559
560       tmp=to_heaptop;
561       to_heaptop=curr_heaptop;
562       curr_heaptop=tmp;
563
564       tmp=to_heapptr;
565       curr_heapptr=to_heapptr+size;
566       curr_heapgcpoint=((char *) curr_heapbase)+GCPOINT(curr_heaptop-curr_heapbase);
567       to_heapptr=to_heapbase;
568
569       /* Not enough room :(, redo gc */
570       if (curr_heapptr>curr_heapgcpoint) {
571 #if defined(THREADS)||defined(DSTM)||defined(STM)
572         pthread_mutex_unlock(&gclock);
573 #endif
574         return mygcmalloc(stackptr, size);
575       }
576
577       bzero(tmp, curr_heaptop-tmp);
578 #if defined(THREADS)||defined(DSTM)||defined(STM)
579       pthread_mutex_unlock(&gclock);
580 #endif
581       return tmp;
582     }
583   } else {
584 #if defined(THREADS)||defined(DSTM)||defined(STM)
585     pthread_mutex_unlock(&gclock);
586 #endif
587     return ptr;
588   }
589 }
590
591
592 int gc_createcopy(void * orig, void ** copy_ptr) {
593   if (orig==0) {
594     *copy_ptr=NULL;
595     return 0;
596   } else {
597     int type=((int *)orig)[0];
598     if (type==-1) {
599       *copy_ptr=((void **)orig)[1];
600       return 0;
601     }
602     if (type<NUMCLASSES) {
603       /* We have a normal object */
604       int size=classsize[type];
605       void *newobj=tomalloc(size);
606       memcpy(newobj, orig, size);
607       ((int *)orig)[0]=-1;
608       ((void **)orig)[1]=newobj;
609       *copy_ptr=newobj;
610       return 1;
611     } else {
612       /* We have an array */
613       struct ArrayObject *ao=(struct ArrayObject *)orig;
614       int elementsize=classsize[type];
615       int length=ao->___length___;
616       int size=sizeof(struct ArrayObject)+length*elementsize;
617       void *newobj=tomalloc(size);
618       memcpy(newobj, orig, size);
619       ((int *)orig)[0]=-1;
620       ((void **)orig)[1]=newobj;
621       *copy_ptr=newobj;
622       return 1;
623     }
624   }
625 }