changes committed
[IRC.git] / Robust / src / Runtime / bamboo / multicoregccompact.c
1 #ifdef MULTICORE_GC
2 #include "multicoregccompact.h"
3 #include "runtime_arch.h"
4 #include "multicoreruntime.h"
5
6 extern int corenum;
7
8 INLINE bool gc_checkCoreStatus_I() {
9   for(int i = 0; i < NUMCORES4GC; ++i) {
10     if(gccorestatus[i] != 0)
11       return false;
12   }  
13   return true;
14 }
15
16 INLINE void compact2Heaptophelper_I(unsigned int coren,
17                                     unsigned int* p,
18                                     unsigned int* numblocks,
19                                     unsigned int* remain) {
20   unsigned int b;
21   unsigned int memneed = gcrequiredmems[coren] + BAMBOO_CACHE_LINE_SIZE;
22   if(STARTUPCORE == coren) {
23     gctomove = true;
24     gcmovestartaddr = *p;
25     gcdstcore = gctopcore;
26     gcblock2fill = *numblocks + 1;
27   } else {
28     send_msg_4(coren, GCMOVESTART, gctopcore, *p, (*numblocks) + 1, false);
29   }
30   if(memneed < *remain) {
31     *p = *p + memneed;
32     gcrequiredmems[coren] = 0;
33     gcloads[gctopcore] += memneed;
34     *remain = *remain - memneed;
35   } else {
36     // next available block
37     *p = *p + *remain;
38     gcfilledblocks[gctopcore] += 1;
39     unsigned int newbase = 0;
40     BASEPTR(gctopcore, gcfilledblocks[gctopcore], &newbase);
41     gcloads[gctopcore] = newbase;
42     gcrequiredmems[coren] -= *remain - BAMBOO_CACHE_LINE_SIZE;
43     gcstopblock[gctopcore]++;
44     gctopcore = NEXTTOPCORE(gctopblock);
45     gctopblock++;
46     *numblocks = gcstopblock[gctopcore];
47     *p = gcloads[gctopcore];
48     BLOCKINDEX(*p, &b);
49     *remain=GC_BLOCK_REMAIN_SIZE(b, (*p));
50   }  
51   gcmovepending--;
52
53
54 INLINE void compact2Heaptop() {
55   // no cores with spare mem and some cores are blocked with pending move
56   // find the current heap top and make them move to the heap top
57   unsigned int p;
58   unsigned int numblocks = gcfilledblocks[gctopcore];
59   p = gcloads[gctopcore];
60   unsigned int b;
61   BLOCKINDEX(p, &b);
62   unsigned int remain=GC_BLOCK_REMAIN_SIZE(b, p);
63   // check if the top core finishes
64   BAMBOO_ENTER_RUNTIME_MODE_FROM_CLIENT();
65   if(gccorestatus[gctopcore] != 0) {
66     // let the top core finishes its own work first
67     compact2Heaptophelper_I(gctopcore, &p, &numblocks, &remain);
68     BAMBOO_ENTER_CLIENT_MODE_FROM_RUNTIME();
69     return;
70   }
71   BAMBOO_ENTER_CLIENT_MODE_FROM_RUNTIME();
72
73   for(int i = 0; i < NUMCORES4GC; i++) {
74     BAMBOO_ENTER_RUNTIME_MODE_FROM_CLIENT();
75     if((gccorestatus[i] != 0) && (gcrequiredmems[i] > 0)) {
76       compact2Heaptophelper_I(i, &p, &numblocks, &remain);
77       if(gccorestatus[gctopcore] != 0) {
78         BAMBOO_ENTER_CLIENT_MODE_FROM_RUNTIME();
79         // the top core is not free now
80         return;
81       }
82     }  
83     BAMBOO_ENTER_CLIENT_MODE_FROM_RUNTIME();
84   } 
85 }
86
87 INLINE void resolvePendingMoveRequest() {
88   int i;
89   int j;
90   bool nosparemem = true;
91   bool haspending = false;
92   bool hasrunning = false;
93   bool noblock = false;
94   unsigned int dstcore = 0;       // the core who need spare mem
95   unsigned int sourcecore = 0;       // the core who has spare mem
96   for(i = j = 0; (i < NUMCORES4GC) && (j < NUMCORES4GC); ) {
97     if(nosparemem) {
98       // check if there are cores with spare mem
99       if(gccorestatus[i] == 0) {
100         // finished working, check if it still have spare mem
101         if(gcfilledblocks[i] < gcstopblock[i]) {
102           // still have spare mem
103           nosparemem = false;
104           sourcecore = i;
105         }  
106       }
107       i++;
108     }  
109     if(!haspending) {
110       if(gccorestatus[j] != 0) {
111         // not finished, check if it has pending move requests
112         if((gcfilledblocks[j]==gcstopblock[j])&&(gcrequiredmems[j]>0)) {
113           dstcore = j;
114           haspending = true;
115         } else {
116           hasrunning = true;
117         } 
118       } 
119       j++;
120     }  
121     if(!nosparemem && haspending) {
122       // find match
123       unsigned int tomove = 0;
124       unsigned int startaddr = 0;
125       BAMBOO_ENTER_RUNTIME_MODE_FROM_CLIENT();
126       gcrequiredmems[dstcore] = assignSpareMem_I(sourcecore,
127                                                  gcrequiredmems[dstcore],
128                                                  &tomove,
129                                                  &startaddr);
130       BAMBOO_ENTER_CLIENT_MODE_FROM_RUNTIME();
131       if(STARTUPCORE == dstcore) {
132         gcdstcore = sourcecore;
133         gctomove = true;
134         gcmovestartaddr = startaddr;
135         gcblock2fill = tomove;
136       } else {
137         send_msg_4(dstcore, GCMOVESTART, sourcecore,startaddr, tomove, false);
138       }
139       gcmovepending--;
140       nosparemem = true;
141       haspending = false;
142       noblock = true;
143     }
144   }  
145   
146   if(!hasrunning && !noblock) {
147     gcphase = SUBTLECOMPACTPHASE;
148     compact2Heaptop();
149   }
150
151
152 // If out of boundary of valid shared memory, return false, else return true
153 INLINE bool nextSBlock(struct moveHelper * orig) {
154   orig->blockbase = orig->blockbound;
155   
156   bool sbchanged = false;
157   unsigned int origptr = orig->ptr;
158   unsigned int blockbase = orig->blockbase;
159   unsigned int blockbound = orig->blockbound;
160   unsigned int bound = orig->bound;
161 outernextSBlock:
162   // check if across a big block
163   // TODO now do not zero out the whole memory, maybe the last two conditions
164   // are useless now
165   if((blockbase>=bound)||(origptr>=bound)
166     ||((origptr!=NULL)&&(*((int*)origptr))==0)||((*((int*)blockbase))==0)) {
167   innernextSBlock:
168     // end of current heap block, jump to next one
169     orig->numblocks++;
170     BASEPTR(BAMBOO_NUM_OF_CORE, orig->numblocks, &(orig->base));
171     if(orig->base >= gcbaseva + BAMBOO_SHARED_MEM_SIZE) {
172       // out of boundary
173       orig->ptr = orig->base; // set current ptr to out of boundary too
174       return false;
175     }
176     orig->blockbase = orig->base;
177     orig->sblockindex = 
178       (unsigned int)(orig->blockbase-gcbaseva)/BAMBOO_SMEM_SIZE;
179     sbchanged = true;
180     unsigned int blocknum = 0;
181     BLOCKINDEX(orig->base, &blocknum);
182     if(bamboo_smemtbl[blocknum] == 0) {
183       // goto next block
184       goto innernextSBlock;
185     }
186     // check the bamboo_smemtbl to decide the real bound
187     orig->bound = orig->base + bamboo_smemtbl[blocknum];
188   } else if(0 == (orig->blockbase%BAMBOO_SMEM_SIZE)) {
189     orig->sblockindex += 1;
190     sbchanged = true;
191   }  
192
193   // check if this sblock should be skipped or have special start point
194   int sbstart = gcsbstarttbl[orig->sblockindex];
195   if(sbstart == -1) {
196     // goto next sblock
197     orig->sblockindex += 1;
198     orig->blockbase += BAMBOO_SMEM_SIZE;
199     goto outernextSBlock;
200   } else if((sbstart != 0) && (sbchanged)) {
201     // the first time to access this SBlock
202     // not start from the very beginning
203     orig->blockbase = sbstart;
204   } 
205
206   // setup information for this sblock
207   orig->blockbound = orig->blockbase+(unsigned int)*((int*)(orig->blockbase));
208   orig->offset = BAMBOO_CACHE_LINE_SIZE;
209   orig->ptr = orig->blockbase + orig->offset;
210   if(orig->ptr >= orig->bound) {
211     // met a lobj, move to next block
212     goto innernextSBlock;
213   }
214
215   return true;
216
217
218 // return false if there are no available data to compact
219 INLINE bool initOrig_Dst(struct moveHelper * orig,
220                          struct moveHelper * to) {
221   // init the dst ptr
222   to->numblocks = 0;
223   to->top = to->offset = BAMBOO_CACHE_LINE_SIZE;
224   to->bound = BAMBOO_SMEM_SIZE_L;
225   BASEPTR(BAMBOO_NUM_OF_CORE, to->numblocks, &(to->base));
226
227   unsigned int tobase = to->base;
228   to->ptr = tobase + to->offset;
229
230   // init the orig ptr
231   orig->numblocks = 0;
232   orig->base = tobase;
233   unsigned int blocknum = 0;
234   BLOCKINDEX(orig->base, &blocknum);
235   unsigned int origbase = orig->base;
236   // check the bamboo_smemtbl to decide the real bound
237   orig->bound = origbase + (unsigned int)bamboo_smemtbl[blocknum];
238   orig->blockbase = origbase;
239   orig->sblockindex = (unsigned int)(origbase - gcbaseva) / BAMBOO_SMEM_SIZE;
240
241   int sbstart = gcsbstarttbl[orig->sblockindex];
242   if(sbstart == -1) {
243     // goto next sblock
244     orig->blockbound=gcbaseva+BAMBOO_SMEM_SIZE*(orig->sblockindex+1);
245     return nextSBlock(orig);
246   } else if(sbstart != 0) {
247     orig->blockbase = sbstart;
248   }
249   orig->blockbound = orig->blockbase + *((int*)(orig->blockbase));
250   orig->offset = BAMBOO_CACHE_LINE_SIZE;
251   orig->ptr = orig->blockbase + orig->offset;
252
253   return true;
254 }
255
256 INLINE void nextBlock(struct moveHelper * to) {
257   to->top = to->bound + BAMBOO_CACHE_LINE_SIZE; // header!
258   to->bound += BAMBOO_SMEM_SIZE;
259   to->numblocks++;
260   BASEPTR(BAMBOO_NUM_OF_CORE, to->numblocks, &(to->base));
261   to->offset = BAMBOO_CACHE_LINE_SIZE;
262   to->ptr = to->base + to->offset;
263 }
264
265 INLINE unsigned int findValidObj(struct moveHelper * orig,
266                                  struct moveHelper * to,
267                                  int * type) {
268   unsigned int size = 0;
269   while(true) {
270     CACHEADAPT_COMPLETE_PAGE_CONVERT(orig, to, to->ptr, false);
271     unsigned int origptr = (unsigned int)(orig->ptr);
272     unsigned int origbound = (unsigned int)orig->bound;
273     unsigned int origblockbound = (unsigned int)orig->blockbound;
274     if((origptr >= origbound) || (origptr == origblockbound)) {
275       if(!nextSBlock(orig)) {
276         // finished, no more data
277         return -1;
278       }
279       continue;
280     }
281     // check the obj's type, size and mark flag
282     *type = ((int *)(origptr))[0];
283     size = 0;
284     if(*type == 0) {
285       // end of this block, go to next one
286       if(!nextSBlock(orig)) {
287         // finished, no more data
288         return -1;
289       }
290       continue;
291     } else if(*type < NUMCLASSES) {
292       // a normal object
293       size = classsize[*type];
294     } else {
295       // an array
296       struct ArrayObject *ao=(struct ArrayObject *)(origptr);
297       unsigned int elementsize=classsize[*type];
298       unsigned int length=ao->___length___;
299       size=(unsigned int)sizeof(struct ArrayObject)
300         +(unsigned int)(length*elementsize);
301     }
302     return size;
303   }
304 }
305
306 // endaddr does not contain spaces for headers
307 INLINE bool moveobj(struct moveHelper * orig, struct moveHelper * to, unsigned int stopblock) {
308   if(stopblock == 0) {
309     return true;
310   }
311
312   int type = 0;
313   unsigned int size = findValidObj(orig, to, &type);
314   unsigned int isize = 0;
315
316   if(size == -1) {
317     // finished, no more data
318     return true;
319   }
320   ALIGNSIZE(size, &isize);       // no matter is the obj marked or not
321                                  // should be able to across
322   unsigned int origptr = (unsigned int)(orig->ptr);
323   if(((struct ___Object___ *)origptr)->marked == MARKED) {
324     unsigned int totop = (unsigned int)to->top;
325     unsigned int tobound = (unsigned int)to->bound;
326     GCPROFILE_RECORD_LIVE_OBJ();
327     // marked obj, copy it to current heap top
328     // check to see if remaining space is enough
329     if((unsigned int)(totop + isize) > tobound) {
330       // fill 0 indicating the end of this block
331       BAMBOO_MEMSET_WH(to->ptr,  '\0', tobound - totop);
332       // fill the header of this block and then go to next block
333       to->offset += tobound - totop;
334       CLOSEBLOCK(to->base, to->offset);
335 #ifdef GC_CACHE_ADAPT
336       unsigned int tmp_ptr = to->ptr;
337 #endif 
338       nextBlock(to);
339 #ifdef GC_CACHE_ADAPT
340       CACHEADAPT_COMPLETE_PAGE_CONVERT(orig, to, tmp_ptr, true);
341 #endif 
342       if(stopblock == to->numblocks) {
343         // already fulfilled the block
344         return true;
345       }  
346     } 
347     // set the mark field to 2, indicating that this obj has been moved
348     // and need to be flushed
349     ((struct ___Object___ *)origptr)->marked = COMPACTED;
350     unsigned int toptr = (unsigned int)to->ptr;
351     if(toptr != origptr) {
352       if((unsigned int)(origptr) < (unsigned int)(toptr+size)) {
353         memmove(toptr, origptr, size);
354       } else {
355         memcpy(toptr, origptr, size);
356       }
357       // fill the remaining space with -2
358       BAMBOO_MEMSET_WH((unsigned int)(toptr+size), -2, isize-size);
359     }
360     // store mapping info
361     gcmappingtbl[OBJMAPPINGINDEX((unsigned int)origptr)]=(unsigned int)toptr;
362     gccurr_heaptop -= isize;
363     to->ptr += isize;
364     to->offset += isize;
365     to->top += isize;
366 #ifdef GC_CACHE_ADAPT
367     unsigned int tmp_ptr = to->ptr;
368 #endif // GC_CACHE_ADAPT
369     if(to->top == to->bound) {
370       CLOSEBLOCK(to->base, to->offset);
371       nextBlock(to);
372     }
373 #ifdef GC_CACHE_ADAPT
374     CACHEADAPT_COMPLETE_PAGE_CONVERT(orig, to, tmp_ptr, true);
375 #endif
376   } 
377   
378   // move to next obj
379   orig->ptr += isize; 
380   
381   return ((((unsigned int)(orig->ptr) > (unsigned int)(orig->bound))
382            || ((unsigned int)(orig->ptr) == (unsigned int)(orig->blockbound)))
383           &&!nextSBlock(orig));
384
385
386 // should be invoked with interrupt closed
387 INLINE int assignSpareMem_I(unsigned int sourcecore,
388                             unsigned int * requiredmem,
389                             unsigned int * tomove,
390                             unsigned int * startaddr) {
391   unsigned int b = 0;
392   BLOCKINDEX(gcloads[sourcecore], &b);
393   unsigned int boundptr = (b<NUMCORES4GC) ? ((b+1)*BAMBOO_SMEM_SIZE_L)
394      : (BAMBOO_LARGE_SMEM_BOUND+(b-NUMCORES4GC+1)*BAMBOO_SMEM_SIZE);
395   unsigned int remain = boundptr - gcloads[sourcecore];
396   unsigned int memneed = requiredmem + BAMBOO_CACHE_LINE_SIZE;
397   *startaddr = gcloads[sourcecore];
398   *tomove = gcfilledblocks[sourcecore] + 1;
399   if(memneed < remain) {
400     gcloads[sourcecore] += memneed;
401     return 0;
402   } else {
403     // next available block
404     gcfilledblocks[sourcecore] += 1;
405     unsigned int newbase = 0;
406     BASEPTR(sourcecore, gcfilledblocks[sourcecore], &newbase);
407     gcloads[sourcecore] = newbase;
408     return requiredmem-remain;
409   }
410
411
412 // should be invoked with interrupt closed
413 INLINE bool gcfindSpareMem_I(unsigned int * startaddr,
414                              unsigned int * tomove,
415                              unsigned int * dstcore,
416                              unsigned int requiredmem,
417                              unsigned int requiredcore) {
418   for(int k = 0; k < NUMCORES4GC; k++) {
419     if((gccorestatus[k] == 0) && (gcfilledblocks[k] < gcstopblock[k])) {
420       // check if this stopped core has enough mem
421       assignSpareMem_I(k, requiredmem, tomove, startaddr);
422       *dstcore = k;
423       return true;
424     }
425   }
426   // if can not find spare mem right now, hold the request
427   gcrequiredmems[requiredcore] = requiredmem;
428   gcmovepending++;
429   return false;
430
431
432 INLINE bool compacthelper(struct moveHelper * orig,
433                           struct moveHelper * to,
434                           int * filledblocks,
435                           unsigned int * heaptopptr,
436                           bool * localcompact) {
437   // scan over all objs in this block, compact the marked objs
438   // loop stop when finishing either scanning all active objs or
439   // fulfilled the gcstopblock
440 innercompact:
441   while((unsigned int)(orig->ptr) < (unsigned int)gcmarkedptrbound) {
442     if(moveobj(orig, to, gcblock2fill)) {
443       break;
444     }
445   }
446   CACHEADAPT_SAMPLING_DATA_CONVERT(to->ptr);
447   // if no objs have been compact, do nothing,
448   // otherwise, fill the header of this block
449   if(to->offset > (unsigned int)BAMBOO_CACHE_LINE_SIZE) {
450     CLOSEBLOCK(to->base, to->offset);
451   } else {
452     to->offset = 0;
453     to->ptr = to->base;
454     to->top -= BAMBOO_CACHE_LINE_SIZE;
455   }  
456   if(*localcompact) {
457     *heaptopptr = to->ptr;
458     *filledblocks = to->numblocks;
459   }
460   
461   // send msgs to core coordinator indicating that the compact is finishing
462   // send compact finish message to core coordinator
463   if(STARTUPCORE == BAMBOO_NUM_OF_CORE) {
464     gcfilledblocks[BAMBOO_NUM_OF_CORE] = *filledblocks;
465     gcloads[BAMBOO_NUM_OF_CORE] = *heaptopptr;
466     if((unsigned int)(orig->ptr) < (unsigned int)gcmarkedptrbound) {
467       // ask for more mem
468       gctomove = false;
469       BAMBOO_ENTER_RUNTIME_MODE_FROM_CLIENT();
470       if(gcfindSpareMem_I(&gcmovestartaddr, &gcblock2fill, &gcdstcore,
471             gccurr_heaptop, BAMBOO_NUM_OF_CORE)) {
472         gctomove = true;
473       } else {
474         BAMBOO_ENTER_CLIENT_MODE_FROM_RUNTIME();
475         return false;
476       }
477       BAMBOO_ENTER_CLIENT_MODE_FROM_RUNTIME();
478     } else {
479       gccorestatus[BAMBOO_NUM_OF_CORE] = 0;
480       gctomove = false;
481       return true;
482     }
483   } else {
484     if((unsigned int)(orig->ptr) < (unsigned int)gcmarkedptrbound) {
485       // ask for more mem
486       gctomove = false;
487       send_msg_5(STARTUPCORE, GCFINISHCOMPACT, BAMBOO_NUM_OF_CORE,
488                  *filledblocks, *heaptopptr, gccurr_heaptop, false);
489     } else {
490       // finish compacting
491       send_msg_5(STARTUPCORE, GCFINISHCOMPACT, BAMBOO_NUM_OF_CORE,
492                  *filledblocks, *heaptopptr, 0, false);
493     }
494   } 
495
496   if(orig->ptr < gcmarkedptrbound) {
497     // still have unpacked obj
498     while(!gctomove)
499       ;
500     
501     gctomove = false;
502
503     to->ptr = gcmovestartaddr;
504     to->numblocks = gcblock2fill - 1;
505     to->bound = ((to->numblocks==0)?BAMBOO_SMEM_SIZE_L:BAMBOO_SMEM_SIZE_L)+BAMBOO_SMEM_SIZE*to->numblocks;
506     BASEPTR(gcdstcore, to->numblocks, &(to->base));
507     to->offset = to->ptr - to->base;
508     to->top = (to->numblocks==0)?(to->offset):(to->bound-BAMBOO_SMEM_SIZE+to->offset);
509     to->base = to->ptr;
510     to->offset = BAMBOO_CACHE_LINE_SIZE;
511     to->ptr += to->offset;   // for header
512     to->top += to->offset;
513     *localcompact = (gcdstcore == BAMBOO_NUM_OF_CORE);
514     CACHEADAPT_SAMPLING_DATA_REVISE_INIT();
515     goto innercompact;
516   }
517   return true;
518 }
519
520 void compact() {
521   BAMBOO_ASSERT(COMPACTPHASE == gcphase, 0xb025);
522   
523   // initialize pointers for comapcting
524   struct moveHelper * orig = (struct moveHelper *)RUNMALLOC(sizeof(struct moveHelper));
525   struct moveHelper * to = (struct moveHelper *)RUNMALLOC(sizeof(struct moveHelper));
526   if(!initOrig_Dst(orig, to)) {
527     // no available data to compact
528     // send compact finish msg to STARTUP core
529     send_msg_5(STARTUPCORE, GCFINISHCOMPACT, BAMBOO_NUM_OF_CORE,
530                0, to->base, 0, false);
531     RUNFREE(orig);
532     RUNFREE(to);
533   } else {
534     CACHEADAPT_SAMPLING_DATA_REVISE_INIT();
535
536     unsigned int filledblocks = 0;
537     unsigned int heaptopptr = 0;
538     bool localcompact = true;
539     compacthelper(orig, to, &filledblocks, &heaptopptr, &localcompact);
540     RUNFREE(orig);
541     RUNFREE(to);
542   }
543
544
545 void compact_master(struct moveHelper * orig, struct moveHelper * to) {
546   // initialize pointers for comapcting
547   initOrig_Dst(orig, to);
548   CACHEADAPT_SAMPLING_DATA_REVISE_INIT();
549   int filledblocks = 0;
550   unsigned int heaptopptr = 0;
551   bool finishcompact = false;
552   bool iscontinue = true;
553   bool localcompact = true;
554   while((COMPACTPHASE == gcphase) || (SUBTLECOMPACTPHASE == gcphase)) {
555     if((!finishcompact) && iscontinue) {
556       finishcompact = compacthelper(orig,to,&filledblocks,&heaptopptr,&localcompact);
557     }
558     
559     BAMBOO_ENTER_RUNTIME_MODE_FROM_CLIENT();
560     if(gc_checkCoreStatus_I()) {
561       // all cores have finished compacting
562       // restore the gcstatus of all cores
563       for(int i = 0; i < NUMCORES4GC; ++i) {
564         gccorestatus[i] = 1;
565       }
566       BAMBOO_ENTER_CLIENT_MODE_FROM_RUNTIME();
567       break;
568     } else {
569       BAMBOO_ENTER_CLIENT_MODE_FROM_RUNTIME();
570       // check if there are spare mem for pending move requires
571       if(COMPACTPHASE == gcphase) {
572         resolvePendingMoveRequest();
573       } else {
574         compact2Heaptop();
575       }
576     } 
577
578     if(gctomove) {
579       to->ptr = gcmovestartaddr;
580       to->numblocks = gcblock2fill - 1;
581       to->bound = (to->numblocks==0) ? BAMBOO_SMEM_SIZE_L : BAMBOO_SMEM_SIZE_L+BAMBOO_SMEM_SIZE*to->numblocks;
582       BASEPTR(gcdstcore, to->numblocks, &(to->base));
583       to->offset = to->ptr - to->base;
584       to->top = (to->numblocks==0)?(to->offset):(to->bound-BAMBOO_SMEM_SIZE+to->offset);
585       to->base = to->ptr;
586       to->offset = BAMBOO_CACHE_LINE_SIZE;
587       to->ptr += to->offset;  // for header
588       to->top += to->offset;
589       localcompact = (gcdstcore == BAMBOO_NUM_OF_CORE);
590       gctomove = false;
591       iscontinue = true;
592     } else if(!finishcompact) {
593       // still pending
594       iscontinue = false;
595     }
596   }
597 }
598
599 #endif // MULTICORE_GC