diff options
Diffstat (limited to 'source4/lib/appweb/ejs-2.0/ejs/ejsGarbage.c')
-rwxr-xr-x | source4/lib/appweb/ejs-2.0/ejs/ejsGarbage.c | 1214 |
1 files changed, 0 insertions, 1214 deletions
diff --git a/source4/lib/appweb/ejs-2.0/ejs/ejsGarbage.c b/source4/lib/appweb/ejs-2.0/ejs/ejsGarbage.c deleted file mode 100755 index 264da05721..0000000000 --- a/source4/lib/appweb/ejs-2.0/ejs/ejsGarbage.c +++ /dev/null @@ -1,1214 +0,0 @@ -/* - * @file ejsGarbage.c - * @brief EJS Garbage collector. - * @overview This implements a generational mark and sweep collection scheme. - */ -/********************************* Copyright **********************************/ -/* - * @copy default - * - * Copyright (c) Mbedthis Software LLC, 2003-2006. All Rights Reserved. - * - * This software is distributed under commercial and open source licenses. - * You may use the GPL open source license described below or you may acquire - * a commercial license from Mbedthis Software. You agree to be fully bound - * by the terms of either license. Consult the LICENSE.TXT distributed with - * this software for full details. - * - * This software is open source; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. See the GNU General Public License for more - * details at: http://www.mbedthis.com/downloads/gplLicense.html - * - * This program is distributed WITHOUT ANY WARRANTY; without even the - * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * - * This GPL license does NOT permit incorporating this software into - * proprietary programs. If you are unable to comply with the GPL, you must - * acquire a commercial license to use this software. Commercial licenses - * for this software and support services are available from Mbedthis - * Software at http://www.mbedthis.com - * - * @end - */ -/********************************** Includes **********************************/ - -#include "ejs.h" - -#if BLD_FEATURE_EJS - -/****************************** Forward Declarations **************************/ - -static void mark(Ejs *ep); -static void markObjByVar(Ejs *ep, EjsVar *op); -static void markObj(EjsObj *obj); -static void markPerm(Ejs *ep, uint gen); -static int sweep(Ejs *ep, uint gen); -static EjsGCLink *ejsAlloc(EJS_LOC_DEC(ep, loc), int slabIndex); -static void ejsGracefulDegrade(Ejs *ep); -static void resetMarks(Ejs *ep, EjsSlab *slab); - -#if FUTURE -static void ageGenerations(Ejs *ep); -#endif - -#if BLD_DEBUG && (!BREW || BREW_SIMULATOR) -uint breakAddr; -#endif - -/************************************* Code ***********************************/ - -void ejsGCInit(Ejs *ep, int objInc, int propInc, int varInc, int strInc) -{ - EjsSlab *slab; - - if (ep->service && ep->service->globalClass) { - ep->service->globalClass->objectState->gcMarked = 1; - } - - slab = &ep->slabs[EJS_SLAB_OBJ]; - slab->allocIncrement = objInc; - slab->size = EJS_ALLOC_ALIGN(sizeof(EjsObj)); - - slab = &ep->slabs[EJS_SLAB_PROPERTY]; - slab->allocIncrement = propInc; - slab->size = EJS_ALLOC_ALIGN(sizeof(EjsProperty)); - - slab = &ep->slabs[EJS_SLAB_VAR]; - slab->allocIncrement = varInc; - slab->size = EJS_ALLOC_ALIGN(sizeof(EjsVar)); - - /* - * Initialize GC. - * Enable GC both idle and demand collections. - * Set no limits and garbage collect if the slabs are - * empty and we have used more than the THRESHOLD of ram. - */ - ep->gc.debugLevel = 0; - ep->gc.enable = 1; - ep->gc.enableIdleCollect = 1; - ep->gc.enableDemandCollect = 1; - ep->gc.workQuota = EJS_GC_WORK_QUOTA; - ep->gc.maxMemory = 0; -} - - -/******************************************************************************/ -#if BLD_FEATURE_ALLOC_STATS - -void ejsPrintAllocReport(Ejs *ep, bool printLeakReport) -{ - EjsSlab *slab; - char *name; - int slabIndex, isObj; - - for (slabIndex = 0; slabIndex < EJS_SLAB_MAX; slabIndex++) { - slab = &ep->slabs[slabIndex]; - if (slabIndex == EJS_SLAB_VAR) { - name = "var"; - } else if (slabIndex == EJS_SLAB_PROPERTY) { - name = "prop"; - } else { - name = "obj"; - } - mprLog(ep, 0, " "); - mprLog(ep, 0, " GC \"%s\" local slab", name); - mprLog(ep, 0, " Total blocks %14d", - slab->allocCount + slab->freeCount); - mprLog(ep, 0, " Block size %14d", slab->size); - mprLog(ep, 0, " Slab RAM allocated %14d", - (slab->allocCount + slab->freeCount) * slab->size); - mprLog(ep, 0, " Slab RAM in use %14d", - slab->allocCount * slab->size); - mprLog(ep, 0, " Blocks in use %14d", slab->allocCount); - mprLog(ep, 0, " Free blocks %14d", slab->freeCount); - mprLog(ep, 0, " Peak allocated %14d", slab->peakAllocated); - mprLog(ep, 0, " Peak free %14d", slab->peakFree); - mprLog(ep, 0, " Total allocations %14d", slab->totalAlloc); - mprLog(ep, 0, " Total blocks reclaimed %14d", slab->totalReclaimed); - mprLog(ep, 0, " Total sweeps %14d", slab->totalSweeps); - mprLog(ep, 0, " Allocation inc %14d", slab->allocIncrement); - } - - mprLog(ep, 0, " "); - mprLog(ep, 0, " Total EJS memory in use %10d", ejsGetUsedMemory(ep)); - mprLog(ep, 0, " Total EJS memory allocated %10d", - ejsGetAllocatedMemory(ep)); - - if (printLeakReport) { - mprLog(ep, 0, " "); - for (slabIndex = 0; slabIndex < EJS_SLAB_MAX; slabIndex++) { - int size; - - slab = &ep->slabs[slabIndex]; - - isObj = 0; - mprLog(ep, 0, " "); - if (slabIndex == EJS_SLAB_VAR) { - name = "var"; - size = sizeof(EjsVar); - } else if (slabIndex == EJS_SLAB_PROPERTY) { - name = "prop"; - size = sizeof(EjsProperty); - } else { - name = "obj"; - size = sizeof(EjsObj); - isObj++; - } -#if BLD_FEATURE_ALLOC_LEAK_TRACK -{ - EjsGCLink *lp; - EjsObj *obj; - int count; - - mprLog(ep, 0, "EJS Leak Report for \"%s\"", name); - count = 0; - - for (lp = slab->allocList[0].next; lp; lp = lp->next) { - mprLog(ep, 0, " %-20s %10d", lp->allocatedBy, size); - if (isObj) { - obj = (EjsObj*) lp; - mprLog(ep, 0, " %-20s %10d %s %s", - lp->allocatedBy, size, - obj->permanent ? "permanent" : "", - obj->alive ? "alive" : "" - ); - } else { - mprLog(ep, 0, " %-20s %10d", lp->allocatedBy, - size); - } - count++; - } - mprLog(ep, 0, " Total blocks %14d", count); -} -#endif - } - mprLog(ep, 0, " "); - } -} - -#endif -/******************************************************************************/ -/* - * Slab allocator - */ - -static EjsGCLink *ejsAlloc(EJS_LOC_DEC(ep, loc), int slabIndex) -{ - EjsSlab *slab; - EjsGCLink *block; - EjsGC *gc; - uint allocatedMemory; - int i; - - mprStackCheck(ep); - - if (slabIndex < 0 || slabIndex >= EJS_SLAB_MAX) { - mprAssert(0); - return 0; - } - - /* - * See if the slab has some free blocks - */ - slab = &ep->slabs[slabIndex]; - if ((block = slab->freeList.next) == 0) { - - allocatedMemory = ejsGetAllocatedMemory(ep); - gc = &ep->gc; - - /* - * No blocks available. If demand collection is enabled, try - * to garbage collect first. We collect if we have done a good - * work quota or we are over the max memory limit. - */ - if (slabIndex != EJS_SLAB_VAR && - ep->gc.enable && ep->gc.enableDemandCollect) { - if ((ep->gc.workDone > ep->gc.workQuota) || - (gc->maxMemory > 0 && allocatedMemory > gc->maxMemory)) { - -#if DEBUG_USE_ONLY - if (ep->gc.debugLevel > 0) { - mprLog(ep, 0, "Need GC, EJS RAM %d, MPR RAM %d\n", - allocatedMemory, mprGetAllocatedMemory(ep)); - if (ep->gc.debugLevel > 4) { - ejsPrintAllocReport(ep, 0); - } - } -#endif - if (ejsCollectGarbage(ep, slabIndex) == 0) { - block = slab->freeList.next; - } - } - } - - if (block == 0) { - if (gc->maxMemory > 0 && allocatedMemory > gc->maxMemory) { - /* - * We are above the max memory limit. We will fail this - * memory allocation, but allow subsequent allocations to - * permit error recovery. We gracefully degrade by setting - * slab chunk sizes to 1. This minimizes real memory - * consumption. This allows us to create - * an exception block to be created by upper layers. - */ - if (! gc->degraded) { - ejsGracefulDegrade(ep); - return 0; - } - } - - /* - * Still non available, so allocate more memory for a set of blocks - * OPT -- should bypass mprAlloc. Need mprMalloc. - */ - block = mprAlloc(ep->slabAllocContext, - slab->size * slab->allocIncrement); - if (block == 0) { - /* - * Now we're in trouble. We should really never get here - * as the graceful degrade will have signaled a memory - * allocation failure. - */ - mprAssert(block != 0); - return 0; - } - - /* - * Chain all the blocks together onto the slab free list - */ - for (i = slab->allocIncrement - 1; i >= 0; i--) { - block->next = slab->freeList.next; -#if BLD_DEBUG - block->magic = EJS_MAGIC_FREE; -#endif - slab->freeList.next = block; - block = (EjsGCLink*) ((char*) block + slab->size); - } - - block = slab->freeList.next; - -#if BLD_FEATURE_ALLOC_STATS - slab->freeCount += slab->allocIncrement; - if (slab->freeCount > slab->peakFree) { - slab->peakFree = slab->freeCount; - } -#endif - } - } - - /* - * We use block to point to the user data in the block. We only - * store the magic number (if debug). No other data is stored in the - * user block. - */ -#if BLD_DEBUG - mprAssert(block->magic == EJS_MAGIC_FREE); -#endif - - /* - * Remove from the free list - */ - slab->freeList.next = block->next; - - /* - * Zero block - */ - memset(block, 0, slab->size); - -#if BLD_DEBUG - block->magic = EJS_MAGIC; -#endif - -#if BLD_FEATURE_ALLOC_STATS - slab->totalAlloc++; - if (++slab->allocCount > slab->peakAllocated) { - slab->peakAllocated = slab->allocCount; - } - slab->freeCount--; -#endif - -#if BLD_DEBUG && (!BREW || BREW_SIMULATOR) - if ((uint) block == breakAddr) { - mprBreakpoint(MPR_LOC, "Watched Block"); - } -#endif - return block; -} - - -/******************************************************************************/ - -EjsObj *ejsAllocObj(EJS_LOC_DEC(ep, loc)) -{ - EjsObj *obj; - EjsSlab *slab; - - obj = (EjsObj*) ejsAlloc(EJS_LOC_PASS(ep, loc), EJS_SLAB_OBJ); - - /* - * Add to the allocated block list for the New generation. - */ - if (obj) { - slab = &ep->slabs[EJS_SLAB_OBJ]; - obj->gc.next = slab->allocList[EJS_GEN_NEW].next; - -#if BLD_FEATURE_ALLOC_LEAK_TRACK - obj->gc.allocatedBy = loc; -#endif - - obj->ejs = ep; - slab->allocList[EJS_GEN_NEW].next = (EjsGCLink*) obj; - - ep->gc.workDone++; - } - - return obj; -} - - -/******************************************************************************/ - -EjsProperty *ejsAllocProperty(EJS_LOC_DEC(ep, loc)) -{ - EjsProperty *prop; - - prop = (EjsProperty*) ejsAlloc(EJS_LOC_PASS(ep, loc), EJS_SLAB_PROPERTY); - mprAssert(prop); - - if (prop) { - prop->var.type = EJS_TYPE_NULL; - prop->var.isProperty = 1; -#if BLD_FEATURE_ALLOC_LEAK_TRACK - prop->var.gc.allocatedBy = loc; -#endif - } - return prop; -} - - -/******************************************************************************/ - -EjsVar *ejsAllocVar(EJS_LOC_DEC(ep, loc)) -{ - EjsVar *vp; - - vp = (EjsVar*) ejsAlloc(EJS_LOC_PASS(ep, loc), EJS_SLAB_VAR); - mprAssert(vp); - - if (vp) { -#if BLD_FEATURE_ALLOC_LEAK_TRACK - EjsSlab *slab; - vp->gc.allocatedBy = loc; - slab = &ep->slabs[EJS_SLAB_VAR]; - vp->gc.next = slab->allocList[EJS_GEN_NEW].next; - slab->allocList[EJS_GEN_NEW].next = (EjsGCLink*) vp; -#endif -#if BLD_DEBUG - vp->propertyName = 0; -#endif - } - return vp; -} - - -/******************************************************************************/ -/* - * Return the block back to the relevant slab - */ - -void ejsFree(Ejs *ep, void *ptr, int slabIndex) -{ - EjsSlab *slab; - EjsGCLink *block; - - mprAssert(ep); - mprAssert(ptr); - - if (slabIndex < 0 || slabIndex >= EJS_SLAB_MAX) { - mprAssert(slabIndex >= 0 && slabIndex < EJS_SLAB_MAX); - return; - } - slab = &ep->slabs[slabIndex]; - -#if BLD_FEATURE_ALLOC_LEAK_TRACK - if (slabIndex == EJS_SLAB_VAR) { - EjsVar *vp, *np, *prev; - - /* - * Remove the block rom the alloc list. WARNING: this is slow - * and should not be used in production code. - */ - vp = (EjsVar*) ptr; - prev = 0; - for (np = (EjsVar*) slab->allocList[0].next; np; - np = (EjsVar*) np->gc.next) { - if (vp == np) { - if (prev) { - prev->gc.next = (EjsGCLink*) np->gc.next; - } else { - slab->allocList[0].next = (EjsGCLink*) np->gc.next; - } - break; - } - prev = np; - } - if (np == 0) { - mprAssert(0); - } - } -#endif - - /* - * Insert into the free list. Only use the next ptr - */ - block = (EjsGCLink*) ptr; - -#if BLD_DEBUG -#if !BREW || BREW_SIMULATOR - if ((uint) block == breakAddr) { - mprBreakpoint(MPR_LOC, "Watched Block"); - } -#endif - mprAssert(block->magic == EJS_MAGIC); - block->magic = EJS_MAGIC_FREE; -#endif - - block->next = slab->freeList.next; - slab->freeList.next = block; - -#if BLD_FEATURE_ALLOC_STATS - slab->allocCount--; - if (++slab->freeCount >= slab->peakFree) { - slab->peakFree = slab->freeCount; - } - slab->totalReclaimed++; - if (slabIndex != 2) { - slabIndex = slabIndex; - } -#endif -} - -/******************************************************************************/ -/* - * Mark an object as being in-use. Traverse all properties for referenced - * objects and base classes. - */ - -static void markObjByVar(Ejs *ep, EjsVar *obj) -{ - EjsProperty *pp; - EjsVar *vp, *baseClass; - - mprAssert(ep); - mprAssert(obj); - - obj->objectState->gcMarked = 1; - -#if BLD_DEBUG - if (ep->gc.debugLevel >= 3) { - int indent = min(ep->gc.gcIndent * 2, 32); - mprLog(ep, 0, "%.*s %-24s %.*s 0x%08X", - indent, " ", - obj->propertyName, - 32 - indent, "................................ ", - (uint) obj->objectState); - ep->gc.gcIndent++; - } - ep->gc.objectsInUse++; -#endif - - /* - * Traverse all referenced objects - * OPT -- optimize by directly accessing the object links and not using - * ejsGetFirst/NextProperty. Then just examine objects - * OPT -- first property in global is global. Should optimize this. - */ - pp = ejsGetFirstProperty(obj, EJS_ENUM_ALL); - while (pp) { - vp = ejsGetVarPtr(pp); - if (vp->type == EJS_TYPE_OBJECT) { - if (!vp->objectState->gcMarked) { -#if FUTURE - /* - * OPT -- we can use the dirty bit on objects to avoid - * visiting permanent objects that are clean. If so, don't - * forget the else case below. - */ - obj = vp->objectState; - if ((!obj->alive && !obj->permanent) || obj->dirty) -#endif - markObjByVar(ep, vp); - } - - } else { -#if BLD_DEBUG - if (ep->gc.debugLevel >= 3) { - int indent = min(ep->gc.gcIndent * 2, 32); - mprLog(ep, 0, "%.*s %-24s %.*s %s", - indent, " ", - vp->propertyName, - 32 - indent, "................................ ", - ejsGetVarTypeAsString(vp)); - } - ep->gc.propertiesInUse++; -#endif - } - pp = ejsGetNextProperty(pp, EJS_ENUM_ALL); - } - - /* - * Traverse the base class - */ - baseClass = obj->objectState->baseClass; - if (baseClass) { - mprAssert(baseClass->type == EJS_TYPE_OBJECT); - mprAssert(baseClass->objectState); - if (baseClass->objectState) { - if (! baseClass->objectState->gcMarked) { - markObjByVar(ep, baseClass); - } - } - } -#if BLD_DEBUG - if (ep->gc.debugLevel >= 3) { - ep->gc.gcIndent--; - } -#endif -} - - -/******************************************************************************/ -/* - * Mark phase. Examine all variable frames and the return result. - */ - -static void mark(Ejs *ep) -{ - EjsVar *vp; - int i; - -#if BLD_DEBUG - if (ep->gc.debugLevel >= 3) { - mprLog(ep, 0, " "); - mprLog(ep, 0, "GC: Marked Blocks:"); - } -#endif - - if (ep->frames) { - for (i = 0; i < mprGetItemCount(ep->frames); i++) { - - vp = (EjsVar*) mprGetItem(ep->frames, i); - mprAssert(vp->type == EJS_TYPE_OBJECT); - - if (! vp->objectState->gcMarked) { - markObjByVar(ep, vp); - } - } - } - - vp = ep->result; - if (vp && vp->type == EJS_TYPE_OBJECT && ! vp->objectState->gcMarked) { - markObjByVar(ep, vp); - } - - vp = ep->currentObj; - if (vp && vp->type == EJS_TYPE_OBJECT && ! vp->objectState->gcMarked) { - markObjByVar(ep, vp); - } - - vp = ejsGetVarPtr(ep->currentProperty); - if (vp && vp->type == EJS_TYPE_OBJECT && ! vp->objectState->gcMarked) { - markObjByVar(ep, vp); - } - - /* - * OPT -- we could mark master as "mark permanent" somehow and - * then we would not need to walk the master objects. - */ - if (ep->slabAllocContext == ep->service->master) { - if (ep->service->master->global) { - markObjByVar(ep, ep->service->master->global); - } - } - -#if BLD_DEBUG - if (ep->gc.debugLevel >= 3) { - mprLog(ep, 0, " "); - } -#endif -} - - -/******************************************************************************/ -#if UNUSED - -static void resetMark(EjsVar *obj) -{ - EjsProperty *pp; - EjsVar *vp, *baseClass; - - obj->objectState->gcMarked = 0; - obj->objectState->visited = 1; - - pp = ejsGetFirstProperty(obj, EJS_ENUM_ALL); - while (pp) { - vp = ejsGetVarPtr(pp); - if (vp->type == EJS_TYPE_OBJECT && !vp->objectState->visited) { - resetMark(vp); - } - pp = ejsGetNextProperty(pp, EJS_ENUM_ALL); - } - - baseClass = obj->objectState->baseClass; - if (baseClass) { - mprAssert(baseClass->type == EJS_TYPE_OBJECT); - mprAssert(baseClass->objectState); - if (baseClass->objectState) { - if (! baseClass->objectState->visited) { - resetMark(baseClass); - } - } - } - obj->objectState->visited = 0; -} - -/******************************************************************************/ -/* - * Mark phase. Examine all variable frames and the return result. - */ - -static void resetAllMarks(Ejs *ep) -{ - EjsVar *vp; - int i; - - for (i = 0; i < mprGetItemCount(ep->frames); i++) { - vp = (EjsVar*) mprGetItem(ep->frames, i); - resetMark(vp); - } - - if (ep->result && ep->result->type == EJS_TYPE_OBJECT && - ! ep->result->objectState->gcMarked) { - resetMark(ep->result); - } -} - -#endif -/******************************************************************************/ -/* - * Sweep up the garbage - */ - -static void resetMarks(Ejs *ep, EjsSlab *slab) -{ - EjsVar *vp; - EjsObj *obj; - int gen, i; - - for (gen = EJS_GEN_NEW; gen < EJS_GEN_MAX; gen++) { - obj = (EjsObj*) slab->allocList[gen].next; - for (; obj; obj = (EjsObj*) obj->gc.next) { - obj->gcMarked = 0; - obj->visited = 0; - } - } - - if (ep->frames) { - for (i = 0; i < mprGetItemCount(ep->frames); i++) { - - vp = (EjsVar*) mprGetItem(ep->frames, i); - mprAssert(vp->type == EJS_TYPE_OBJECT); - - vp->objectState->gcMarked = 0; - vp->objectState->visited = 0; - } - } - - if (ep->result && ep->result->type == EJS_TYPE_OBJECT) { - ep->result->objectState->gcMarked = 0; - } -} - -/******************************************************************************/ -/* - * Mark all permanent and non-alive objects - */ - -static void markPerm(Ejs *ep, uint gen) -{ - EjsSlab *slab; - EjsObj *obj; - - slab = &ep->slabs[EJS_SLAB_OBJ]; - - for (obj = (EjsObj*) slab->allocList[gen].next; obj; ) { - - if (! obj->gcMarked) { - if (!obj->alive || obj->permanent) { - markObj(obj); - } - } - obj = (EjsObj*) obj->gc.next; - - } -} - -/******************************************************************************/ - -static void markObj(EjsObj *obj) -{ - EjsProperty *pp; - EjsPropLink *lp, *head; - EjsObj *op; - - mprAssert(obj); - - obj->gcMarked = 1; - - head = &obj->link; - for (lp = head->next; lp != head; lp = lp->next) { - - pp = ejsGetPropertyFromLink(lp); - - if (pp->var.type == EJS_TYPE_OBJECT) { - op = pp->var.objectState; - if (op != 0 && !op->gcMarked) { - markObj(op); - } - } - } -} - -/******************************************************************************/ -/* - * Sweep up the garbage. Return the number of objects freed. - */ - -static int sweep(Ejs *ep, uint gen) -{ - EjsSlab *slab; - EjsObj *obj, *next, *prev; - int count; - - slab = &ep->slabs[EJS_SLAB_OBJ]; - - /* - * Examine allocated objects in the specified generation (only). - * NOTE: we only sweep object allocated to this interpreter and so - * we do not sweep any permanent objects in the default interpreter. - */ - prev = 0; - count = 0; - for (obj = (EjsObj*) slab->allocList[gen].next; obj; obj = next) { - - next = (EjsObj*) obj->gc.next; - -#if BLD_DEBUG && (!BREW || BREW_SIMULATOR) - if ((uint) obj == breakAddr) { - mprBreakpoint(MPR_LOC, "Watched Block"); - } -#endif - - /* - * If object has not been marked inuse and is not a permanent - * object, then free it. - */ - if (! obj->gcMarked && obj->alive && !obj->permanent) { - -#if BLD_DEBUG - if (ep->gc.debugLevel >= 2) { - if (obj->objName) { - mprLog(ep, 0, "GC: destroy %-18s %10d, %8X", - obj->objName, (uint) obj, (uint) obj); - } else { - mprLog(ep, 0, "GC: destroy UNKNOWN %x", (uint) obj); - } - } -#endif - if (ejsDestroyObj(ep, obj) < 0) { - prev = obj; - obj->gcMarked = 0; - continue; - } - - if (prev) { - prev->gc.next = (EjsGCLink*) next; - } else { - slab->allocList[gen].next = (EjsGCLink*) next; - } - count++; - - } else { - prev = obj; - /* Reset for next time */ - obj->gcMarked = 0; - } - } - - if (gen == (EJS_GEN_OLD - 1)) { - slab->lastRecentBlock = prev; - } -#if BLD_FEATURE_ALLOC_STATS - slab->totalSweeps++; -#endif -#if BLD_DEBUG - if (ep->gc.debugLevel > 0) { - mprLog(ep, 0, "GC: Sweep freed %d objects", count); - } -#endif - return count; -} - -/******************************************************************************/ -/* - * Sweep all variables - */ - -void ejsSweepAll(Ejs *ep) -{ - EjsSlab *slab; - EjsObj *obj, *next, *prev; - int gen; - - slab = &ep->slabs[EJS_SLAB_OBJ]; - - for (gen = EJS_GEN_NEW; gen < EJS_GEN_MAX; gen++) { - prev = 0; - for (obj = (EjsObj*) slab->allocList[gen].next; obj; obj = next) { - next = (EjsObj*) obj->gc.next; - ejsDestroyObj(ep, obj); - } - break; - } -} - -/******************************************************************************/ - -bool ejsObjIsCollectable(EjsVar *vp) -{ - if (vp == 0 || !ejsVarIsObject(vp)) { - return 0; - } - return (vp->objectState->alive && !vp->objectState->permanent); -} - -/******************************************************************************/ -#if FUTURE - -static void ageGenerations(Ejs *ep) -{ - EjsSlab *slab; - EjsGCLink *oldList; - int gen; - - slab = &ep->slabs[EJS_SLAB_OBJ]; - - /* - * Age all blocks. First append all (old - 1) blocks onto the old - * alloc list - */ - oldList = &slab->allocList[EJS_GEN_OLD]; - - if (slab->lastRecentBlock) { - slab->lastRecentBlock->gc.next = oldList->next; - oldList->next = (EjsGCLink*) slab->lastRecentBlock; - } - - /* - * Now simply copy all allocation lists up one generation - */ - for (gen = EJS_GEN_OLD - 1; gen > 0; gen--) { - slab->allocList[gen] = slab->allocList[gen - 1]; - } - slab->allocList[0].next = 0; -} - -#endif -/******************************************************************************/ -/* - * Collect the garbage. This is a mark and sweep over all possible objects. - * If an object is not referenced, it and all contained properties will be - * freed. If a slabIndex is provided, the collection halts when a block is - * available for allocation on that slab. - * - * Return 0 if memory is now available after collecting garbage. Otherwise, - * return MPR_ERR_MEMORY. - */ - -int ejsCollectGarbage(Ejs *ep, int slabIndex) -{ - EjsGeneration gen; - - if (ep->flags & EJS_FLAGS_DONT_GC) { - return -1; - } - - /* - * Prevent destructors invoking the garbage collector - */ - if (ep->gc.collecting) { - return 0; - } - ep->gc.collecting = 1; - - resetMarks(ep, &ep->slabs[EJS_SLAB_OBJ]); - - /* - * Examine each generation of objects starting with the most recent - * generation. Stop scanning when we have a free block to use. - */ - for (gen = EJS_GEN_NEW; gen < EJS_GEN_MAX; gen++) { - - if (slabIndex >= 0 && ep->slabs[slabIndex].freeList.next) { - break; - } - - /* - * FUTURE OPT. Should mark objects in new generation and those - * with a dirty bit set in older generations. Don't need to mark - * entire heap. But how to keep list of dirty objects. - */ - mark(ep); - markPerm(ep, gen); - sweep(ep, gen); - - /* FUTURE - not using generations yet */ - break; - } - - /* - * FUTURE -- not using generations yet. - * - * ageGenerations(ep); - */ - - ep->gc.workDone = 0; - ep->gc.collecting = 0; - - return (gen < EJS_GEN_MAX) ? 0 : MPR_ERR_MEMORY; -} - - -/******************************************************************************/ -/* - * Should be called when the app has been idle for a little while and when it - * is likely to be idle a bit longer. Call ejsIsTimeForGC to see if this is - * true. Return the count of objects collected . - */ - -int ejsIncrementalCollectGarbage(Ejs *ep) -{ - int count; - - if (ep->gc.collecting) { - return 0; - } - - ep->gc.collecting = 1; - - resetMarks(ep, &ep->slabs[EJS_SLAB_OBJ]); - mark(ep); - - /* Not generational yet */ - count = sweep(ep, EJS_GEN_NEW); - - ep->gc.collecting = 0; - ep->gc.workDone = 0; - - return count; -} - -/******************************************************************************/ -#if BLD_DEBUG - -void ejsDumpObjects(Ejs *ep) -{ - int oldDebugLevel; - - mprLog(ep, 0, "Dump of objects in use\n"); - - oldDebugLevel = ep->gc.debugLevel; - - ep->gc.debugLevel = 3; - ep->gc.objectsInUse = 0; - ep->gc.propertiesInUse = 0; - ep->gc.collecting = 1; - - resetMarks(ep, &ep->slabs[EJS_SLAB_OBJ]); - mark(ep); - - ep->gc.collecting = 0; - ep->gc.debugLevel = oldDebugLevel; - - mprLog(ep, 0, "%d objects and %d properties in use", - ep->gc.objectsInUse, ep->gc.propertiesInUse); - mprLog(ep, 0, "%d object bytes, %d property bytes and %d total", - (int) (ep->gc.objectsInUse * sizeof(EjsObj)), - (int) (ep->gc.propertiesInUse * sizeof(EjsProperty)), - (int) ((ep->gc.objectsInUse * sizeof(EjsObj) + - ep->gc.propertiesInUse * sizeof(EjsProperty)))); -} - -#endif -/******************************************************************************/ -/* - * Return true if there is time to do a garbage collection and if we will - * benefit from it. - */ - -int ejsIsTimeForGC(Ejs *ep, int timeTillNextEvent) -{ - EjsGC *gc; - - if (timeTillNextEvent < EJS_MIN_TIME_FOR_GC) { - /* - * Not enough time to complete a collection - */ - return 0; - } - - gc = &ep->gc; - - /* - * Return if we haven't done enough work to warrant a collection - * Trigger a little short of the work quota to try to run GC before - * a demand allocation requires it. - */ - if (!gc->enable || !gc->enableIdleCollect || - (gc->workDone < (gc->workQuota - EJS_GC_MIN_WORK_QUOTA))) { - return 0; - } - -#if UNUSED - mprLog(ep, 0, "Time for GC. Work done %d, time till next event %d", - gc->workDone, timeTillNextEvent); -#endif - return 1; -} - -/******************************************************************************/ -/* - * Return the amount of memory in use by EJS - */ - -uint ejsGetUsedMemory(Ejs *ep) -{ -#if BLD_FEATURE_ALLOC_STATS - EjsSlab *slab; - int i, totalMemory, slabMemory; - - totalMemory = 0; - for (i = 0; i < EJS_SLAB_MAX; i++) { - slab = &ep->slabs[i]; - slabMemory = slab->allocCount * slab->size; - totalMemory += slabMemory; - } - return totalMemory; -#else - return 0; -#endif -} - -/******************************************************************************/ -/* - * Return the amount of memory allocated by EJS - */ - -uint ejsGetAllocatedMemory(Ejs *ep) -{ -#if BLD_FEATURE_ALLOC_STATS - EjsSlab *slab; - int i, totalMemory, slabMemory; - - totalMemory = 0; - for (i = 0; i < EJS_SLAB_MAX; i++) { - slab = &ep->slabs[i]; - slabMemory = (slab->allocCount + slab->freeCount) * slab->size; - totalMemory += slabMemory; - } - return totalMemory; -#else - return 0; -#endif -} - -/******************************************************************************/ -/* - * On a memory allocation failure, go into graceful degrade mode. Set all - * slab allocation chunk increments to 1 so we can create an exception block - * to throw. - */ - -static void ejsGracefulDegrade(Ejs *ep) -{ - EjsSlab *slab; - int i; - - mprLog(ep, 1, "WARNING: Memory almost depleted. In graceful degrade mode"); - for (i = 0; i < EJS_SLAB_MAX; i++) { - slab = &ep->slabs[i]; - slab->allocIncrement = 8; - } - ep->gc.degraded = 1; -} - -/******************************************************************************/ - -int ejsSetGCDebugLevel(Ejs *ep, int debugLevel) -{ - int old; - - old = ep->gc.debugLevel; - ep->gc.debugLevel = debugLevel; - return old; -} - -/******************************************************************************/ - -int ejsSetGCMaxMemory(Ejs *ep, uint maxMemory) -{ - int old; - - old = ep->gc.maxMemory; - ep->gc.maxMemory = maxMemory; - - return old; -} - -/******************************************************************************/ - -bool ejsBlockInUseInt(EjsVar *vp) -{ - if (vp) { -#if BLD_DEBUG - if (vp->gc.magic != EJS_MAGIC) { - return 0; - } - if (vp->type == EJS_TYPE_OBJECT && vp->objectState && - vp->objectState->gc.magic != EJS_MAGIC) { - return 0; - } -#endif - return 1; - } - return 1; -} - -/******************************************************************************/ -#else -void ejsGarbageDummy() {} - -#endif /* BLD_FEATURE_EJS */ - -/******************************************************************************/ -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * End: - * vim:tw=78 - * vim600: sw=4 ts=4 fdm=marker - * vim<600: sw=4 ts=4 - */ |