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