test setinfo FULL_EA_INFORMATION in gentest
[gd/samba/.git] / source4 / lib / appweb / ejs-2.0 / ejs / ejsGarbage.c
1 /*
2  *      @file   ejsGarbage.c
3  *      @brief  EJS Garbage collector.
4  *      @overview This implements a generational mark and sweep collection scheme.
5  */
6 /********************************* Copyright **********************************/
7 /*
8  *      @copy   default
9  *      
10  *      Copyright (c) Mbedthis Software LLC, 2003-2006. All Rights Reserved.
11  *      
12  *      This software is distributed under commercial and open source licenses.
13  *      You may use the GPL open source license described below or you may acquire 
14  *      a commercial license from Mbedthis Software. You agree to be fully bound 
15  *      by the terms of either license. Consult the LICENSE.TXT distributed with 
16  *      this software for full details.
17  *      
18  *      This software is open source; you can redistribute it and/or modify it 
19  *      under the terms of the GNU General Public License as published by the 
20  *      Free Software Foundation; either version 2 of the License, or (at your 
21  *      option) any later version. See the GNU General Public License for more 
22  *      details at: http://www.mbedthis.com/downloads/gplLicense.html
23  *      
24  *      This program is distributed WITHOUT ANY WARRANTY; without even the 
25  *      implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
26  *      
27  *      This GPL license does NOT permit incorporating this software into 
28  *      proprietary programs. If you are unable to comply with the GPL, you must
29  *      acquire a commercial license to use this software. Commercial licenses 
30  *      for this software and support services are available from Mbedthis 
31  *      Software at http://www.mbedthis.com 
32  *      
33  *      @end
34  */
35 /********************************** Includes **********************************/
36
37 #include        "ejs.h"
38
39 #if BLD_FEATURE_EJS
40
41 /****************************** Forward Declarations **************************/
42
43 static void     mark(Ejs *ep);
44 static void             markObjByVar(Ejs *ep, EjsVar *op);
45 static void     markObj(EjsObj *obj);
46 static void     markPerm(Ejs *ep, uint gen);
47 static int              sweep(Ejs *ep, uint gen);
48 static EjsGCLink *ejsAlloc(EJS_LOC_DEC(ep, loc), int slabIndex);
49 static void     ejsGracefulDegrade(Ejs *ep);
50 static void     resetMarks(Ejs *ep, EjsSlab *slab);
51
52 #if FUTURE
53 static void     ageGenerations(Ejs *ep);
54 #endif
55
56 #if BLD_DEBUG && (!BREW || BREW_SIMULATOR)
57 uint breakAddr;
58 #endif
59
60 /************************************* Code ***********************************/
61
62 void ejsGCInit(Ejs *ep, int objInc, int propInc, int varInc, int strInc)
63 {
64         EjsSlab         *slab;
65
66         if (ep->service && ep->service->globalClass) {
67                 ep->service->globalClass->objectState->gcMarked = 1;
68         }
69
70         slab = &ep->slabs[EJS_SLAB_OBJ];
71         slab->allocIncrement = objInc;
72         slab->size = EJS_ALLOC_ALIGN(sizeof(EjsObj));
73
74         slab = &ep->slabs[EJS_SLAB_PROPERTY];
75         slab->allocIncrement = propInc;
76         slab->size = EJS_ALLOC_ALIGN(sizeof(EjsProperty));
77
78         slab = &ep->slabs[EJS_SLAB_VAR];
79         slab->allocIncrement = varInc;
80         slab->size = EJS_ALLOC_ALIGN(sizeof(EjsVar));
81
82         /*
83          *      Initialize GC.
84          *      Enable GC both idle and demand collections.
85          *      Set no limits and garbage collect if the slabs are
86          *      empty and we have used more than the THRESHOLD of ram.
87          */
88         ep->gc.debugLevel = 0;
89         ep->gc.enable = 1;
90         ep->gc.enableIdleCollect = 1;
91         ep->gc.enableDemandCollect = 1;
92         ep->gc.workQuota = EJS_GC_WORK_QUOTA;
93         ep->gc.maxMemory = 0;
94 }
95
96
97 /******************************************************************************/
98 #if BLD_FEATURE_ALLOC_STATS
99
100 void ejsPrintAllocReport(Ejs *ep, bool printLeakReport)
101 {
102         EjsSlab         *slab;
103         char            *name;
104         int                     slabIndex, isObj;
105         
106         for (slabIndex = 0; slabIndex < EJS_SLAB_MAX; slabIndex++) {
107                 slab = &ep->slabs[slabIndex];
108                 if (slabIndex == EJS_SLAB_VAR) {
109                         name = "var";
110                 } else if (slabIndex == EJS_SLAB_PROPERTY) {
111                         name = "prop";
112                 } else {
113                         name = "obj";
114                 }
115                 mprLog(ep, 0, " ");
116                 mprLog(ep, 0, "  GC \"%s\" local slab", name);
117                 mprLog(ep, 0, "  Total blocks           %14d", 
118                         slab->allocCount + slab->freeCount);
119                 mprLog(ep, 0, "  Block size             %14d", slab->size);
120                 mprLog(ep, 0, "  Slab RAM allocated     %14d", 
121                         (slab->allocCount + slab->freeCount) * slab->size);
122                 mprLog(ep, 0, "  Slab RAM in use        %14d", 
123                         slab->allocCount * slab->size);
124                 mprLog(ep, 0, "  Blocks in use          %14d", slab->allocCount);
125                 mprLog(ep, 0, "  Free blocks            %14d", slab->freeCount);
126                 mprLog(ep, 0, "  Peak allocated         %14d", slab->peakAllocated);
127                 mprLog(ep, 0, "  Peak free              %14d", slab->peakFree);
128                 mprLog(ep, 0, "  Total allocations      %14d", slab->totalAlloc);
129                 mprLog(ep, 0, "  Total blocks reclaimed %14d", slab->totalReclaimed);
130                 mprLog(ep, 0, "  Total sweeps           %14d", slab->totalSweeps);
131                 mprLog(ep, 0, "  Allocation inc         %14d", slab->allocIncrement);
132         }
133
134         mprLog(ep, 0, " ");
135         mprLog(ep, 0, "  Total EJS memory in use    %10d", ejsGetUsedMemory(ep));
136         mprLog(ep, 0, "  Total EJS memory allocated %10d", 
137                 ejsGetAllocatedMemory(ep));
138
139         if (printLeakReport) {
140                 mprLog(ep, 0, " ");
141                 for (slabIndex = 0; slabIndex < EJS_SLAB_MAX; slabIndex++) {
142                         int             size;
143
144                         slab = &ep->slabs[slabIndex];
145
146                         isObj = 0;
147                         mprLog(ep, 0, " ");
148                         if (slabIndex == EJS_SLAB_VAR) {
149                                 name = "var";
150                                 size = sizeof(EjsVar);
151                         } else if (slabIndex == EJS_SLAB_PROPERTY) {
152                                 name = "prop";
153                                 size = sizeof(EjsProperty);
154                         } else {
155                                 name = "obj";
156                                 size = sizeof(EjsObj);
157                                 isObj++;
158                         }
159 #if BLD_FEATURE_ALLOC_LEAK_TRACK
160 {
161                         EjsGCLink       *lp;
162                         EjsObj          *obj;
163                         int                     count;
164
165                         mprLog(ep, 0, "EJS Leak Report for \"%s\"", name);
166                         count = 0;
167
168                         for (lp = slab->allocList[0].next; lp; lp = lp->next) {
169                                 mprLog(ep, 0, "  %-20s           %10d", lp->allocatedBy, size);
170                                 if (isObj) {
171                                         obj = (EjsObj*) lp;
172                                         mprLog(ep, 0, "  %-20s           %10d %s %s", 
173                                                 lp->allocatedBy, size,
174                                                 obj->permanent ? "permanent" : "", 
175                                                 obj->alive ? "alive" : ""
176                                         );
177                                 } else {
178                                         mprLog(ep, 0, "  %-20s           %10d", lp->allocatedBy, 
179                                                 size);
180                                 }
181                                 count++;
182                         }
183                         mprLog(ep, 0, "  Total blocks               %14d", count);
184 }
185 #endif
186                 }
187                 mprLog(ep, 0, " ");
188         }
189 }
190
191 #endif
192 /******************************************************************************/
193 /*
194  *      Slab allocator
195  */
196
197 static EjsGCLink *ejsAlloc(EJS_LOC_DEC(ep, loc), int slabIndex)
198 {
199         EjsSlab         *slab;
200         EjsGCLink       *block;
201         EjsGC           *gc;
202         uint            allocatedMemory;
203         int                     i;
204
205         mprStackCheck(ep);
206
207         if (slabIndex < 0 || slabIndex >= EJS_SLAB_MAX) {
208                 mprAssert(0);
209                 return 0;
210         }
211
212         /*
213          *      See if the slab has some free blocks
214          */
215         slab = &ep->slabs[slabIndex];
216         if ((block = slab->freeList.next) == 0) {
217
218                 allocatedMemory = ejsGetAllocatedMemory(ep);
219                 gc = &ep->gc;
220
221                 /*
222                  *      No blocks available. If demand collection is enabled, try
223                  *      to garbage collect first. We collect if we have done a good 
224                  *      work quota or we are over the max memory limit.
225                  */
226                 if (slabIndex != EJS_SLAB_VAR && 
227                                 ep->gc.enable && ep->gc.enableDemandCollect) {
228                         if ((ep->gc.workDone > ep->gc.workQuota) || 
229                            (gc->maxMemory > 0 && allocatedMemory > gc->maxMemory)) {
230
231 #if DEBUG_USE_ONLY
232                                 if (ep->gc.debugLevel > 0) {
233                                         mprLog(ep, 0, "Need GC, EJS RAM %d, MPR RAM %d\n",
234                                                 allocatedMemory, mprGetAllocatedMemory(ep));
235                                         if (ep->gc.debugLevel > 4) {
236                                                 ejsPrintAllocReport(ep, 0);
237                                         }
238                                 }
239 #endif
240                                 if (ejsCollectGarbage(ep, slabIndex) == 0) {
241                                         block = slab->freeList.next;
242                                 }
243                         }
244                 }
245
246                 if (block == 0) {
247                         if (gc->maxMemory > 0 && allocatedMemory > gc->maxMemory) {
248                                 /*
249                                  *      We are above the max memory limit. We will fail this
250                                  *      memory allocation, but allow subsequent allocations to 
251                                  *      permit error recovery. We gracefully degrade by setting 
252                                  *      slab chunk sizes to 1. This minimizes real memory
253                                  *      consumption. This allows us to create 
254                                  *      an exception block to be created by upper layers.
255                                  */
256                                 if (! gc->degraded) {
257                                         ejsGracefulDegrade(ep);
258                                         return 0;
259                                 }
260                         }
261
262                         /*
263                          *      Still non available, so allocate more memory for a set of blocks
264                          *      OPT -- should bypass mprAlloc. Need mprMalloc.
265                          */
266                         block = mprAlloc(ep->slabAllocContext, 
267                                 slab->size * slab->allocIncrement);
268                         if (block == 0) {
269                                 /*
270                                  *      Now we're in trouble. We should really never get here
271                                  *      as the graceful degrade will have signaled a memory 
272                                  *      allocation failure.
273                                  */
274                                 mprAssert(block != 0);
275                                 return 0;
276                         }
277
278                         /*
279                          *      Chain all the blocks together onto the slab free list
280                          */
281                         for (i = slab->allocIncrement - 1; i >= 0; i--) {
282                                 block->next = slab->freeList.next;
283 #if BLD_DEBUG
284                                 block->magic = EJS_MAGIC_FREE;
285 #endif
286                                 slab->freeList.next = block;
287                                 block = (EjsGCLink*) ((char*) block + slab->size);
288                         }
289
290                         block = slab->freeList.next;
291
292 #if BLD_FEATURE_ALLOC_STATS
293                         slab->freeCount += slab->allocIncrement;
294                         if (slab->freeCount > slab->peakFree) {
295                                 slab->peakFree = slab->freeCount;
296                         }
297 #endif
298                 }
299         }
300
301         /*
302          *      We use block to point to the user data in the block. We only
303          *      store the magic number (if debug). No other data is stored in the
304          *      user block.
305          */
306 #if BLD_DEBUG
307         mprAssert(block->magic == EJS_MAGIC_FREE);
308 #endif
309
310         /*
311          *      Remove from the free list
312          */
313         slab->freeList.next = block->next;
314
315         /*
316          *      Zero block
317          */
318         memset(block, 0, slab->size);
319
320 #if BLD_DEBUG
321         block->magic = EJS_MAGIC;
322 #endif
323
324 #if BLD_FEATURE_ALLOC_STATS
325         slab->totalAlloc++;
326         if (++slab->allocCount > slab->peakAllocated) {
327                 slab->peakAllocated = slab->allocCount;
328         }
329         slab->freeCount--;
330 #endif
331
332 #if BLD_DEBUG && (!BREW || BREW_SIMULATOR)
333         if ((uint) block == breakAddr) {
334                 mprBreakpoint(MPR_LOC, "Watched Block");
335         }
336 #endif
337         return block;
338 }
339
340
341 /******************************************************************************/
342
343 EjsObj *ejsAllocObj(EJS_LOC_DEC(ep, loc))
344 {
345         EjsObj          *obj;
346         EjsSlab         *slab;
347
348         obj = (EjsObj*) ejsAlloc(EJS_LOC_PASS(ep, loc), EJS_SLAB_OBJ);
349
350         /*
351          *      Add to the allocated block list for the New generation.
352          */
353         if (obj) {
354                 slab = &ep->slabs[EJS_SLAB_OBJ];
355                 obj->gc.next = slab->allocList[EJS_GEN_NEW].next;
356
357 #if BLD_FEATURE_ALLOC_LEAK_TRACK
358                 obj->gc.allocatedBy = loc;
359 #endif
360
361                 obj->ejs = ep;
362                 slab->allocList[EJS_GEN_NEW].next = (EjsGCLink*) obj;
363
364                 ep->gc.workDone++;
365         }
366
367         return obj;
368 }
369
370
371 /******************************************************************************/
372
373 EjsProperty *ejsAllocProperty(EJS_LOC_DEC(ep, loc))
374 {
375         EjsProperty             *prop;
376
377         prop = (EjsProperty*) ejsAlloc(EJS_LOC_PASS(ep, loc), EJS_SLAB_PROPERTY);
378         mprAssert(prop);
379
380         if (prop) {
381                 prop->var.type = EJS_TYPE_NULL;
382                 prop->var.isProperty = 1;
383 #if BLD_FEATURE_ALLOC_LEAK_TRACK
384                 prop->var.gc.allocatedBy = loc;
385 #endif
386         }
387         return prop;
388 }
389
390
391 /******************************************************************************/
392
393 EjsVar *ejsAllocVar(EJS_LOC_DEC(ep, loc))
394 {
395         EjsVar  *vp;
396
397         vp = (EjsVar*) ejsAlloc(EJS_LOC_PASS(ep, loc), EJS_SLAB_VAR);
398         mprAssert(vp);
399
400         if (vp) {
401 #if BLD_FEATURE_ALLOC_LEAK_TRACK
402                 EjsSlab *slab;
403                 vp->gc.allocatedBy = loc;
404                 slab = &ep->slabs[EJS_SLAB_VAR];
405                 vp->gc.next = slab->allocList[EJS_GEN_NEW].next;
406                 slab->allocList[EJS_GEN_NEW].next = (EjsGCLink*) vp;
407 #endif
408 #if BLD_DEBUG
409                 vp->propertyName = 0;
410 #endif
411         }
412         return vp;
413 }
414
415
416 /******************************************************************************/
417 /*
418  *      Return the block back to the relevant slab
419  */
420
421 void ejsFree(Ejs *ep, void *ptr, int slabIndex)
422 {
423         EjsSlab         *slab;
424         EjsGCLink       *block;
425
426         mprAssert(ep);
427         mprAssert(ptr);
428
429         if (slabIndex < 0 || slabIndex >= EJS_SLAB_MAX) {
430                 mprAssert(slabIndex >= 0 && slabIndex < EJS_SLAB_MAX);
431                 return;
432         }
433         slab = &ep->slabs[slabIndex];
434
435 #if BLD_FEATURE_ALLOC_LEAK_TRACK
436         if (slabIndex == EJS_SLAB_VAR) {
437                 EjsVar          *vp, *np, *prev;
438
439                 /*
440                  *      Remove the block rom the alloc list. WARNING: this is slow
441                  *      and should not be used in production code.
442                  */
443                 vp = (EjsVar*) ptr;
444                 prev = 0;
445                 for (np = (EjsVar*) slab->allocList[0].next; np; 
446                                 np = (EjsVar*) np->gc.next) {
447                         if (vp == np) {
448                                 if (prev) {
449                                         prev->gc.next = (EjsGCLink*) np->gc.next;
450                                 } else {
451                                         slab->allocList[0].next = (EjsGCLink*) np->gc.next;
452                                 }
453                                 break;
454                         }
455                         prev = np;
456                 }
457                 if (np == 0) {
458                         mprAssert(0);
459                 }
460         }
461 #endif
462
463         /*
464          *      Insert into the free list. Only use the next ptr
465          */
466         block = (EjsGCLink*) ptr;
467
468 #if BLD_DEBUG
469 #if !BREW || BREW_SIMULATOR
470         if ((uint) block == breakAddr) {
471                 mprBreakpoint(MPR_LOC, "Watched Block");
472         }
473 #endif
474         mprAssert(block->magic == EJS_MAGIC);
475         block->magic = EJS_MAGIC_FREE;
476 #endif
477
478         block->next = slab->freeList.next;
479         slab->freeList.next = block;
480
481 #if BLD_FEATURE_ALLOC_STATS
482         slab->allocCount--;
483         if (++slab->freeCount >= slab->peakFree) {
484                 slab->peakFree = slab->freeCount;
485         }
486         slab->totalReclaimed++;
487         if (slabIndex != 2) {
488                 slabIndex = slabIndex;
489         }
490 #endif
491 }
492
493 /******************************************************************************/
494 /*
495  *      Mark an object as being in-use. Traverse all properties for referenced 
496  *      objects and base classes.
497  */
498
499 static void markObjByVar(Ejs *ep, EjsVar *obj)
500 {
501         EjsProperty             *pp;
502         EjsVar                  *vp, *baseClass;
503
504         mprAssert(ep);
505         mprAssert(obj);
506
507         obj->objectState->gcMarked = 1;
508
509 #if BLD_DEBUG
510         if (ep->gc.debugLevel >= 3) {
511                 int indent = min(ep->gc.gcIndent * 2, 32);
512                 mprLog(ep, 0, "%.*s %-24s %.*s 0x%08X", 
513                         indent, "                                 ",
514                         obj->propertyName,
515                         32 - indent, "................................ ",
516                         (uint) obj->objectState);
517                 ep->gc.gcIndent++;
518         }
519         ep->gc.objectsInUse++;
520 #endif
521
522         /*
523          *      Traverse all referenced objects
524          *      OPT -- optimize by directly accessing the object links and not using
525          *      ejsGetFirst/NextProperty. Then just examine objects
526          *      OPT -- first property in global is global. Should optimize this.
527          */
528         pp = ejsGetFirstProperty(obj, EJS_ENUM_ALL);
529         while (pp) {
530                 vp = ejsGetVarPtr(pp);
531                 if (vp->type == EJS_TYPE_OBJECT) {
532                         if (!vp->objectState->gcMarked) {
533 #if FUTURE
534                                 /*
535                                  *      OPT -- we can use the dirty bit on objects to avoid 
536                                  *      visiting permanent objects that are clean. If so, don't
537                                  *      forget the else case below.
538                                  */
539                                 obj = vp->objectState;
540                                 if ((!obj->alive && !obj->permanent) || obj->dirty)
541 #endif
542                                 markObjByVar(ep, vp);
543                         }
544
545                 } else {
546 #if BLD_DEBUG
547                         if (ep->gc.debugLevel >= 3) {
548                                 int indent = min(ep->gc.gcIndent * 2, 32);
549                                 mprLog(ep, 0, "%.*s %-24s %.*s %s", 
550                                         indent, "                                 ",
551                                         vp->propertyName,
552                                         32 - indent, "................................ ",
553                                         ejsGetVarTypeAsString(vp));
554                         }
555                         ep->gc.propertiesInUse++;
556 #endif
557                 }
558                 pp = ejsGetNextProperty(pp, EJS_ENUM_ALL);
559         }
560
561         /*
562          *      Traverse the base class
563          */
564         baseClass = obj->objectState->baseClass;
565         if (baseClass) {
566                 mprAssert(baseClass->type == EJS_TYPE_OBJECT);
567                 mprAssert(baseClass->objectState);
568                 if (baseClass->objectState) {
569                         if (! baseClass->objectState->gcMarked) {
570                                 markObjByVar(ep, baseClass);
571                         }
572                 }
573         }
574 #if BLD_DEBUG
575         if (ep->gc.debugLevel >= 3) {
576                 ep->gc.gcIndent--;
577         }
578 #endif
579 }
580
581
582 /******************************************************************************/
583 /*
584  *      Mark phase. Examine all variable frames and the return result.
585  */
586
587 static void mark(Ejs *ep)
588 {
589         EjsVar  *vp;
590         int             i;
591
592 #if BLD_DEBUG
593         if (ep->gc.debugLevel >= 3) {
594                 mprLog(ep, 0, " ");
595                 mprLog(ep, 0, "GC: Marked Blocks:");
596         }
597 #endif
598
599         if (ep->frames) {
600                 for (i = 0; i < mprGetItemCount(ep->frames); i++) {
601
602                         vp = (EjsVar*) mprGetItem(ep->frames, i);
603                         mprAssert(vp->type == EJS_TYPE_OBJECT);
604
605                         if (! vp->objectState->gcMarked) {
606                                 markObjByVar(ep, vp);
607                         }
608                 }
609         }
610
611         vp = ep->result;
612         if (vp && vp->type == EJS_TYPE_OBJECT && ! vp->objectState->gcMarked) {
613                 markObjByVar(ep, vp);
614         }
615
616         vp = ep->currentObj;
617         if (vp && vp->type == EJS_TYPE_OBJECT && ! vp->objectState->gcMarked) {
618                 markObjByVar(ep, vp);
619         }
620
621         vp = ejsGetVarPtr(ep->currentProperty);
622         if (vp && vp->type == EJS_TYPE_OBJECT && ! vp->objectState->gcMarked) {
623                 markObjByVar(ep, vp);
624         }
625
626         /*
627          *      OPT -- we could mark master as "mark permanent" somehow and
628          *      then we would not need to walk the master objects.
629          */
630         if (ep->slabAllocContext == ep->service->master) {
631                 if (ep->service->master->global) {
632                         markObjByVar(ep, ep->service->master->global);
633                 }
634         }
635
636 #if BLD_DEBUG
637         if (ep->gc.debugLevel >= 3) {
638                 mprLog(ep, 0, " ");
639         }
640 #endif
641 }
642
643
644 /******************************************************************************/
645 #if UNUSED
646
647 static void resetMark(EjsVar *obj)
648 {
649         EjsProperty             *pp;
650         EjsVar                  *vp, *baseClass;
651
652         obj->objectState->gcMarked = 0;
653         obj->objectState->visited = 1;
654
655         pp = ejsGetFirstProperty(obj, EJS_ENUM_ALL);
656         while (pp) {
657                 vp = ejsGetVarPtr(pp);
658                 if (vp->type == EJS_TYPE_OBJECT && !vp->objectState->visited) {
659                         resetMark(vp);                  
660                 }
661                 pp = ejsGetNextProperty(pp, EJS_ENUM_ALL);
662         }
663
664         baseClass = obj->objectState->baseClass;
665         if (baseClass) {
666                 mprAssert(baseClass->type == EJS_TYPE_OBJECT);
667                 mprAssert(baseClass->objectState);
668                 if (baseClass->objectState) {
669                         if (! baseClass->objectState->visited) {
670                                 resetMark(baseClass);
671                         }
672                 }
673         }
674         obj->objectState->visited = 0;
675 }
676
677 /******************************************************************************/
678 /*
679  *      Mark phase. Examine all variable frames and the return result.
680  */
681
682 static void resetAllMarks(Ejs *ep)
683 {
684         EjsVar  *vp;
685         int             i;
686
687         for (i = 0; i < mprGetItemCount(ep->frames); i++) {
688                 vp = (EjsVar*) mprGetItem(ep->frames, i);
689                 resetMark(vp);
690         }
691
692         if (ep->result && ep->result->type == EJS_TYPE_OBJECT &&
693                         ! ep->result->objectState->gcMarked) {
694                 resetMark(ep->result);
695         }
696 }
697
698 #endif
699 /******************************************************************************/
700 /*
701  *      Sweep up the garbage
702  */
703
704 static void resetMarks(Ejs *ep, EjsSlab *slab)
705 {
706         EjsVar          *vp;
707         EjsObj          *obj;
708         int                     gen, i;
709
710         for (gen = EJS_GEN_NEW; gen < EJS_GEN_MAX; gen++) {
711                 obj = (EjsObj*) slab->allocList[gen].next;
712                 for (; obj; obj = (EjsObj*) obj->gc.next) {
713                         obj->gcMarked = 0;
714                         obj->visited = 0;
715                 }
716         }
717
718         if (ep->frames) {
719                 for (i = 0; i < mprGetItemCount(ep->frames); i++) {
720
721                         vp = (EjsVar*) mprGetItem(ep->frames, i);
722                         mprAssert(vp->type == EJS_TYPE_OBJECT);
723
724                         vp->objectState->gcMarked = 0;
725                         vp->objectState->visited = 0;
726                 }
727         }
728
729         if (ep->result && ep->result->type == EJS_TYPE_OBJECT) {
730                 ep->result->objectState->gcMarked = 0;
731         }
732 }
733
734 /******************************************************************************/
735 /*
736  *      Mark all permanent and non-alive objects
737  */
738
739 static void markPerm(Ejs *ep, uint gen)
740 {
741         EjsSlab         *slab;
742         EjsObj          *obj;
743
744         slab = &ep->slabs[EJS_SLAB_OBJ];
745
746         for (obj = (EjsObj*) slab->allocList[gen].next; obj; ) {
747
748                 if (! obj->gcMarked) {
749                         if (!obj->alive || obj->permanent) {
750                                 markObj(obj);
751                         }
752                 }
753                 obj = (EjsObj*) obj->gc.next;
754
755         }
756 }
757
758 /******************************************************************************/
759
760 static void markObj(EjsObj *obj)
761 {
762         EjsProperty             *pp;
763         EjsPropLink             *lp, *head;
764         EjsObj                  *op;
765
766         mprAssert(obj);
767
768         obj->gcMarked = 1;
769
770         head = &obj->link;
771         for (lp = head->next; lp != head; lp = lp->next) {
772
773                 pp = ejsGetPropertyFromLink(lp);
774
775                 if (pp->var.type == EJS_TYPE_OBJECT) {
776                         op = pp->var.objectState;
777                         if (op != 0 && !op->gcMarked) {
778                                 markObj(op);
779                         }
780                 }
781         }
782 }
783
784 /******************************************************************************/
785 /*
786  *      Sweep up the garbage. Return the number of objects freed.
787  */
788
789 static int sweep(Ejs *ep, uint gen)
790 {
791         EjsSlab         *slab;
792         EjsObj          *obj, *next, *prev;
793         int                     count;
794
795         slab = &ep->slabs[EJS_SLAB_OBJ];
796
797         /*
798          *      Examine allocated objects in the specified generation (only).
799          *      NOTE: we only sweep object allocated to this interpreter and so
800          *      we do not sweep any permanent objects in the default interpreter.
801          */
802         prev = 0;
803         count = 0;
804         for (obj = (EjsObj*) slab->allocList[gen].next; obj; obj = next) {
805
806                 next = (EjsObj*) obj->gc.next;
807
808 #if BLD_DEBUG && (!BREW || BREW_SIMULATOR)
809                 if ((uint) obj == breakAddr) {
810                         mprBreakpoint(MPR_LOC, "Watched Block");
811                 }
812 #endif
813
814                 /*
815                  *      If object has not been marked inuse and is not a permanent
816                  *      object, then free it.
817                  */
818                 if (! obj->gcMarked && obj->alive && !obj->permanent) {
819
820 #if BLD_DEBUG
821                         if (ep->gc.debugLevel >= 2) {
822                                 if (obj->objName) {
823                                         mprLog(ep, 0, "GC: destroy %-18s   %10d, %8X", 
824                                                 obj->objName, (uint) obj, (uint) obj);
825                                 } else {
826                                         mprLog(ep, 0, "GC: destroy UNKNOWN %x", (uint) obj);
827                                 }
828                         }
829 #endif
830                         if (ejsDestroyObj(ep, obj) < 0) {
831                                 prev = obj;
832                                 obj->gcMarked = 0;
833                                 continue;
834                         }
835
836                         if (prev) {
837                                 prev->gc.next = (EjsGCLink*) next;
838                         } else {
839                                 slab->allocList[gen].next = (EjsGCLink*) next;
840                         }
841                         count++;
842
843                 } else {
844                         prev = obj;
845                         /* Reset for next time */
846                         obj->gcMarked = 0;
847                 } 
848         }
849
850         if (gen == (EJS_GEN_OLD - 1)) {
851                 slab->lastRecentBlock = prev;
852         }
853 #if BLD_FEATURE_ALLOC_STATS
854         slab->totalSweeps++;
855 #endif
856 #if BLD_DEBUG
857         if (ep->gc.debugLevel > 0) {
858                 mprLog(ep, 0, "GC: Sweep freed %d objects", count);
859         }
860 #endif
861         return count;
862 }
863
864 /******************************************************************************/
865 /*
866  *      Sweep all variables
867  */
868
869 void ejsSweepAll(Ejs *ep)
870 {
871         EjsSlab         *slab;
872         EjsObj          *obj, *next, *prev;
873         int                     gen;
874
875         slab = &ep->slabs[EJS_SLAB_OBJ];
876
877         for (gen = EJS_GEN_NEW; gen < EJS_GEN_MAX; gen++) {
878                 prev = 0;
879                 for (obj = (EjsObj*) slab->allocList[gen].next; obj; obj = next) {
880                         next = (EjsObj*) obj->gc.next;
881                         ejsDestroyObj(ep, obj);
882                 }
883                 break;
884         }
885 }
886
887 /******************************************************************************/
888
889 bool ejsObjIsCollectable(EjsVar *vp)
890 {
891         if (vp == 0 || !ejsVarIsObject(vp)) {
892                 return 0;
893         }
894         return (vp->objectState->alive && !vp->objectState->permanent);
895 }
896
897 /******************************************************************************/
898 #if FUTURE
899
900 static void ageGenerations(Ejs *ep)
901 {
902         EjsSlab         *slab;
903         EjsGCLink       *oldList;
904         int                     gen;
905
906         slab = &ep->slabs[EJS_SLAB_OBJ];
907
908         /*
909          *      Age all blocks. First append all (old - 1) blocks onto the old 
910          *      alloc list 
911          */
912         oldList = &slab->allocList[EJS_GEN_OLD];
913
914         if (slab->lastRecentBlock) {
915                 slab->lastRecentBlock->gc.next = oldList->next;
916                 oldList->next = (EjsGCLink*) slab->lastRecentBlock;
917         }
918
919         /*
920          *      Now simply copy all allocation lists up one generation
921          */
922         for (gen = EJS_GEN_OLD - 1; gen > 0; gen--) {
923                 slab->allocList[gen] = slab->allocList[gen - 1];
924         }
925         slab->allocList[0].next = 0;
926 }
927
928 #endif
929 /******************************************************************************/
930 /*
931  *      Collect the garbage. This is a mark and sweep over all possible objects.
932  *      If an object is not referenced, it and all contained properties will be
933  *      freed. If a slabIndex is provided, the collection halts when a block is 
934  *      available for allocation on that slab.
935  *
936  *      Return 0 if memory is now available after collecting garbage. Otherwise,
937  *      return MPR_ERR_MEMORY.
938  */
939
940 int ejsCollectGarbage(Ejs *ep, int slabIndex)
941 {
942         EjsGeneration   gen;
943         
944         if (ep->flags & EJS_FLAGS_DONT_GC) {
945                 return -1;
946         }
947
948         /*
949          *      Prevent destructors invoking the garbage collector
950          */
951         if (ep->gc.collecting) {
952                 return 0;
953         }
954         ep->gc.collecting = 1;
955
956         resetMarks(ep, &ep->slabs[EJS_SLAB_OBJ]);
957
958         /*
959          *      Examine each generation of objects starting with the most recent 
960          *      generation. Stop scanning when we have a free block to use.
961          */
962         for (gen = EJS_GEN_NEW; gen < EJS_GEN_MAX; gen++) {
963
964                 if (slabIndex >= 0 && ep->slabs[slabIndex].freeList.next) {
965                         break;
966                 }
967
968                 /*
969                  *      FUTURE OPT. Should mark objects in new generation and those 
970                  *      with a dirty bit set in older generations. Don't need to mark
971                  *      entire heap. But how to keep list of dirty objects. 
972                  */
973                 mark(ep);
974                 markPerm(ep, gen);
975                 sweep(ep, gen);
976
977                 /* FUTURE - not using generations yet */
978                 break;
979         }
980
981         /*
982          *      FUTURE -- not using generations yet.  
983          *
984          *              ageGenerations(ep);
985          */
986
987         ep->gc.workDone = 0;
988         ep->gc.collecting = 0;
989
990         return (gen < EJS_GEN_MAX) ? 0 : MPR_ERR_MEMORY;
991 }
992  
993
994 /******************************************************************************/
995 /*
996  *      Should be called when the app has been idle for a little while and when it
997  *      is likely to be idle a bit longer. Call ejsIsTimeForGC to see if this is
998  *      true. Return the count of objects collected .
999  */
1000
1001 int ejsIncrementalCollectGarbage(Ejs *ep)
1002 {
1003         int             count;
1004
1005         if (ep->gc.collecting) {
1006                 return 0;
1007         }
1008
1009         ep->gc.collecting = 1;
1010
1011         resetMarks(ep, &ep->slabs[EJS_SLAB_OBJ]);
1012         mark(ep);
1013
1014         /* Not generational yet */
1015         count = sweep(ep, EJS_GEN_NEW);
1016
1017         ep->gc.collecting = 0;
1018         ep->gc.workDone = 0;
1019
1020         return count;
1021 }
1022
1023 /******************************************************************************/
1024 #if BLD_DEBUG
1025
1026 void ejsDumpObjects(Ejs *ep)
1027 {
1028         int             oldDebugLevel;
1029
1030         mprLog(ep, 0, "Dump of objects in use\n");
1031
1032         oldDebugLevel = ep->gc.debugLevel;
1033
1034         ep->gc.debugLevel = 3;
1035         ep->gc.objectsInUse = 0;
1036         ep->gc.propertiesInUse = 0;
1037         ep->gc.collecting = 1;
1038
1039         resetMarks(ep, &ep->slabs[EJS_SLAB_OBJ]);
1040         mark(ep);
1041
1042         ep->gc.collecting = 0;
1043         ep->gc.debugLevel = oldDebugLevel;
1044
1045         mprLog(ep, 0, "%d objects and %d properties in use",
1046                 ep->gc.objectsInUse, ep->gc.propertiesInUse);
1047         mprLog(ep, 0, "%d object bytes, %d property bytes and %d total",
1048                 (int) (ep->gc.objectsInUse * sizeof(EjsObj)),
1049                 (int) (ep->gc.propertiesInUse * sizeof(EjsProperty)),
1050                 (int) ((ep->gc.objectsInUse * sizeof(EjsObj) +
1051                                 ep->gc.propertiesInUse * sizeof(EjsProperty))));
1052 }
1053
1054 #endif
1055 /******************************************************************************/
1056 /*
1057  *      Return true if there is time to do a garbage collection and if we will
1058  *      benefit from it.
1059  */
1060
1061 int ejsIsTimeForGC(Ejs *ep, int timeTillNextEvent)
1062 {
1063         EjsGC           *gc;
1064
1065         if (timeTillNextEvent < EJS_MIN_TIME_FOR_GC) {
1066                 /*
1067                  *      Not enough time to complete a collection
1068                  */
1069                 return 0;
1070         }
1071
1072         gc = &ep->gc;
1073
1074         /*
1075          *      Return if we haven't done enough work to warrant a collection
1076          *      Trigger a little short of the work quota to try to run GC before
1077          *      a demand allocation requires it.
1078          */
1079         if (!gc->enable || !gc->enableIdleCollect || 
1080                         (gc->workDone < (gc->workQuota - EJS_GC_MIN_WORK_QUOTA))) {
1081                 return 0;
1082         }
1083
1084 #if UNUSED
1085         mprLog(ep, 0, "Time for GC. Work done %d, time till next event %d",
1086                 gc->workDone, timeTillNextEvent);
1087 #endif
1088         return 1;
1089 }
1090
1091 /******************************************************************************/
1092 /*
1093  *      Return the amount of memory in use by EJS
1094  */
1095
1096 uint ejsGetUsedMemory(Ejs *ep)
1097 {
1098 #if BLD_FEATURE_ALLOC_STATS
1099         EjsSlab         *slab;
1100         int                     i, totalMemory, slabMemory;
1101
1102         totalMemory = 0;
1103         for (i = 0; i < EJS_SLAB_MAX; i++) {
1104                 slab = &ep->slabs[i];
1105                 slabMemory = slab->allocCount * slab->size;
1106                 totalMemory += slabMemory;
1107         }
1108         return totalMemory;
1109 #else
1110         return 0;
1111 #endif
1112 }
1113
1114 /******************************************************************************/
1115 /*
1116  *      Return the amount of memory allocated by EJS
1117  */
1118
1119 uint ejsGetAllocatedMemory(Ejs *ep)
1120 {
1121 #if BLD_FEATURE_ALLOC_STATS
1122         EjsSlab         *slab;
1123         int                     i, totalMemory, slabMemory;
1124
1125         totalMemory = 0;
1126         for (i = 0; i < EJS_SLAB_MAX; i++) {
1127                 slab = &ep->slabs[i];
1128                 slabMemory = (slab->allocCount + slab->freeCount) * slab->size;
1129                 totalMemory += slabMemory;
1130         }
1131         return totalMemory;
1132 #else
1133         return 0;
1134 #endif
1135 }
1136
1137 /******************************************************************************/
1138 /*
1139  *      On a memory allocation failure, go into graceful degrade mode. Set all
1140  *      slab allocation chunk increments to 1 so we can create an exception block 
1141  *      to throw.
1142  */
1143
1144 static void ejsGracefulDegrade(Ejs *ep)
1145 {
1146         EjsSlab         *slab;
1147         int                     i;
1148
1149         mprLog(ep, 1, "WARNING: Memory almost depleted. In graceful degrade mode");
1150         for (i = 0; i < EJS_SLAB_MAX; i++) {
1151                 slab = &ep->slabs[i];
1152                 slab->allocIncrement = 8;
1153         }
1154         ep->gc.degraded = 1;
1155 }
1156
1157 /******************************************************************************/
1158
1159 int ejsSetGCDebugLevel(Ejs *ep, int debugLevel)
1160 {
1161         int             old;
1162
1163         old = ep->gc.debugLevel;
1164         ep->gc.debugLevel = debugLevel;
1165         return old;
1166 }
1167
1168 /******************************************************************************/
1169
1170 int ejsSetGCMaxMemory(Ejs *ep, uint maxMemory)
1171 {
1172         int             old;
1173
1174         old = ep->gc.maxMemory;
1175         ep->gc.maxMemory = maxMemory;
1176
1177         return old;
1178 }
1179
1180 /******************************************************************************/
1181
1182 bool ejsBlockInUseInt(EjsVar *vp)
1183 {
1184         if (vp) {
1185 #if BLD_DEBUG
1186                 if (vp->gc.magic != EJS_MAGIC) {
1187                         return 0;
1188                 }
1189                 if (vp->type == EJS_TYPE_OBJECT && vp->objectState && 
1190                         vp->objectState->gc.magic != EJS_MAGIC) {
1191                         return 0;
1192                 }
1193 #endif
1194                 return 1;
1195         }
1196         return 1;
1197 }
1198
1199 /******************************************************************************/
1200 #else
1201 void ejsGarbageDummy() {}
1202
1203 #endif /* BLD_FEATURE_EJS */
1204
1205 /******************************************************************************/
1206 /*
1207  * Local variables:
1208  * tab-width: 4
1209  * c-basic-offset: 4
1210  * End:
1211  * vim:tw=78
1212  * vim600: sw=4 ts=4 fdm=marker
1213  * vim<600: sw=4 ts=4
1214  */