diff --git a/harbour/ChangeLog b/harbour/ChangeLog index 76637962e1..56c3dae77c 100644 --- a/harbour/ChangeLog +++ b/harbour/ChangeLog @@ -1,3 +1,34 @@ +2000-07-10 11:10 UTC+0100 Ryszard Glab + + *include/hbcomp.h + * added a forward declaration to comiple with Watcom C/C++ + + *source/compiler/harbour.c + * added missing declaration of hb_compReleaseStrings() + + *source/rtl/errorapi.c + * error objects created during error launching called internally + are locked to prevent deallocation by the GC + + *source/rtl/idle.c + * changed to call hb_gcCollectAll() + + *include/hbapi.h + *source/vm/arrays.c + *source/vm/classes.c + *source/vm/codebloc.c + *source/vm/garbage.c + *source/vm/hvm.c + *source/vm/memvars.c + * changed the implementation of the GC to use a classical mark-sweep + algorithm - this means that all garbage memory blocks are released + during a single hb_gcCollectAll() + * the garbage collector maintains now the separate list of + locked items to prevend premature deallocation of items + referenced inside of locked items + * during sweep phase the items are checked only once to prevent + recursive loops + 2000-07-09 01:02 UTC+0800 Ron Pinkas * include/hbcomp.h + Added: external BOOL hb_comp_bSimpLex diff --git a/harbour/include/hbapi.h b/harbour/include/hbapi.h index 4c383e9757..2ff6c64d9f 100644 --- a/harbour/include/hbapi.h +++ b/harbour/include/hbapi.h @@ -527,11 +527,13 @@ extern void hb_gcLockItem( HB_ITEM_PTR pItem ); /* do not release a memory blo extern void hb_gcUnlockItem( HB_ITEM_PTR pItem ); /* allow to release the item */ extern void hb_gcCollect( void ); /* checks if a single memory block can be released */ extern void hb_gcCollectAll( void ); /* checks if all memory blocks can be released */ -extern BOOL hb_gcItemRef( HB_ITEM_PTR pItem, void *pAlloc ); /* checks if passed item refers passed memory block pointer */ -extern BOOL hb_vmIsLocalRef( void *pAlloc ); /* hvm.c - checks all local variables if they are refering a memory block */ -extern BOOL hb_vmIsStaticRef( void *pAlloc ); /* hvm.c - checks all static variables if they are refering a memory block */ -extern BOOL hb_memvarsIsMemvarRef( void *pAlloc ); /* memvars.c - checks all memvar variables if they are refering a memory block */ -extern BOOL hb_clsIsClassRef( void *pAlloc ); /* classes.c - checks all classes if they are refering a memory block */ +extern void hb_gcItemRef( HB_ITEM_PTR pItem ); /* checks if passed item refers passed memory block pointer */ +extern void hb_vmIsLocalRef( void ); /* hvm.c - mark all local variables as used */ +extern void hb_vmIsStaticRef( void ); /* hvm.c - mark all static variables as used */ +extern void hb_memvarsIsMemvarRef( void ); /* memvars.c - mark all memvar variables as used */ +extern void hb_clsIsClassRef( void ); /* classes.c - mark all class internals as used */ +extern HB_GARBAGE_FUNC( hb_codeblockDeleteGarbage ); /* clear a codeblock before releasing by the GC */ +extern HB_GARBAGE_FUNC( hb_arrayReleaseGarbage ); /* clear an array before releasing by the GC */ /* idle states */ extern void hb_idleState( void ); /* services a single idle state */ diff --git a/harbour/include/hbcomp.h b/harbour/include/hbcomp.h index 77455cec07..4a37977e50 100644 --- a/harbour/include/hbcomp.h +++ b/harbour/include/hbcomp.h @@ -84,6 +84,8 @@ typedef struct int iFiles; /* number of files currently opened */ } FILES; +struct _COMCLASS; /* forward declaration */ + /* Declared Function/Method support structure */ typedef struct _COMDECLARED { diff --git a/harbour/source/compiler/harbour.c b/harbour/source/compiler/harbour.c index ce6498bc35..4562b2e300 100644 --- a/harbour/source/compiler/harbour.c +++ b/harbour/source/compiler/harbour.c @@ -182,6 +182,9 @@ extern char * hb_comp_szAnnounce; extern void yyrestart( FILE * ); +/* Simplex requirement */ +extern void hb_compReleaseStrings( void ); + /* ************************************************************************* */ int main( int argc, char * argv[] ) diff --git a/harbour/source/rtl/errorapi.c b/harbour/source/rtl/errorapi.c index cd4421261f..06f15273ff 100644 --- a/harbour/source/rtl/errorapi.c +++ b/harbour/source/rtl/errorapi.c @@ -197,6 +197,11 @@ USHORT hb_errLaunch( PHB_ITEM pError ) if( s_iLaunchCount == HB_ERROR_LAUNCH_MAX ) hb_errInternal( IE_ERRTOOMANY, NULL, NULL, NULL ); + /* Lock an item to prevent deallocation by the GC - the error object + * can be not assigned to any harbour level variable + */ + hb_gcLockItem( pError ); + /* Launch the error handler: "lResult := EVAL( ErrorBlock(), oError )" */ s_iLaunchCount++; @@ -214,6 +219,7 @@ USHORT hb_errLaunch( PHB_ITEM pError ) else pResult = hb_itemDo( &s_errorBlock, 1, pError ); + hb_gcUnlockItem( pError ); s_iLaunchCount--; /* Check results */ @@ -300,6 +306,11 @@ PHB_ITEM hb_errLaunchSubst( PHB_ITEM pError ) if( s_iLaunchCount == HB_ERROR_LAUNCH_MAX ) hb_errInternal( IE_ERRTOOMANY, NULL, NULL, NULL ); + /* Lock an item to prevent deallocation by the GC - the error object + * can be not assigned to any harbour level variable + */ + hb_gcLockItem( pError ); + /* Launch the error handler: "xResult := EVAL( ErrorBlock(), oError )" */ s_iLaunchCount++; @@ -317,6 +328,7 @@ PHB_ITEM hb_errLaunchSubst( PHB_ITEM pError ) else pResult = hb_itemDo( &s_errorBlock, 1, pError ); + hb_gcUnlockItem( pError ); s_iLaunchCount--; /* Check results */ diff --git a/harbour/source/rtl/idle.c b/harbour/source/rtl/idle.c index df6f396ec6..7e30d24240 100644 --- a/harbour/source/rtl/idle.c +++ b/harbour/source/rtl/idle.c @@ -107,7 +107,7 @@ void hb_idleState( void ) { s_bIamIdle = TRUE; hb_releaseCPU(); - hb_gcCollect(); + hb_gcCollectAll(); if( s_pIdleTasks ) { diff --git a/harbour/source/vm/arrays.c b/harbour/source/vm/arrays.c index 4d8d172741..9e9ba42686 100644 --- a/harbour/source/vm/arrays.c +++ b/harbour/source/vm/arrays.c @@ -52,8 +52,6 @@ #include "hbapilng.h" #include "hbvm.h" -static HB_GARBAGE_FUNC( hb_arrayReleaseGarbage ); - BOOL hb_arrayNew( PHB_ITEM pItem, ULONG ulLen ) /* creates a new array */ { PHB_BASEARRAY pBaseArray = ( PHB_BASEARRAY ) hb_gcAlloc( sizeof( HB_BASEARRAY ), hb_arrayReleaseGarbage ); @@ -738,7 +736,7 @@ PHB_ITEM hb_arrayClone( PHB_ITEM pSrcArray ) } /* This releases array when called from the garbage collector */ -static HB_GARBAGE_FUNC( hb_arrayReleaseGarbage ) +HB_GARBAGE_FUNC( hb_arrayReleaseGarbage ) { PHB_BASEARRAY pBaseArray = ( PHB_BASEARRAY ) Cargo; diff --git a/harbour/source/vm/classes.c b/harbour/source/vm/classes.c index 4693125b06..1dbc0c1b20 100644 --- a/harbour/source/vm/classes.c +++ b/harbour/source/vm/classes.c @@ -320,11 +320,10 @@ void hb_clsReleaseAll( void ) s_pClasses = NULL; } -/* Check if passed memory block pointer is referenced by some class - internal data. - This is called from the garbage collector. -*/ -BOOL hb_clsIsClassRef( void *pBlock ) +/* Mark all internal data as used so it will not be released by the + * garbage collector + */ +void hb_clsIsClassRef( void ) { USHORT uiClass = s_uiClasses; PCLASS pClass = s_pClasses; @@ -332,29 +331,24 @@ BOOL hb_clsIsClassRef( void *pBlock ) USHORT uiLimit; PMETHOD pMeth; - HB_TRACE(HB_TR_DEBUG, ("hb_clsIsClassRef(%p)", pBlock)); + HB_TRACE(HB_TR_DEBUG, ("hb_clsIsClassRef()")); while( uiClass-- ) { if( pClass->pInlines ) - if( hb_gcItemRef( pClass->pInlines, pBlock ) ) - return TRUE; + hb_gcItemRef( pClass->pInlines ); if( pClass->pClassDatas ) - if( hb_gcItemRef( pClass->pClassDatas, pBlock ) ) - return TRUE; - + hb_gcItemRef( pClass->pClassDatas ); + uiLimit = ( USHORT ) ( pClass->uiHashKey * BUCKET ); pMeth = pClass->pMethods; for( uiAt = 0; uiAt < uiLimit; uiAt++, pMeth++ ) if( pMeth->pInitValue ) - if( hb_gcItemRef( pMeth->pInitValue, pBlock ) ) - return TRUE; - + hb_gcItemRef( pMeth->pInitValue ); + ++pClass; } - - return FALSE; /* passed block is not referenced in any class */ } void hb_clsScope( PHB_ITEM pObject, PMETHOD pMethod ) diff --git a/harbour/source/vm/codebloc.c b/harbour/source/vm/codebloc.c index 9f7ef8e3e4..d1f8cd7c62 100644 --- a/harbour/source/vm/codebloc.c +++ b/harbour/source/vm/codebloc.c @@ -39,8 +39,6 @@ #include "hbapiitm.h" #include "hbvm.h" -static HB_GARBAGE_FUNC( hb_codeblockDeleteGarbage ); - /* Creates the codeblock structure * * pBuffer -> the buffer with pcodes (without HB_P_PUSHBLOCK) @@ -246,7 +244,7 @@ void hb_codeblockDelete( HB_ITEM_PTR pItem ) /* Release all allocated memory when called from the garbage collector */ -static HB_GARBAGE_FUNC( hb_codeblockDeleteGarbage ) +HB_GARBAGE_FUNC( hb_codeblockDeleteGarbage ) { HB_CODEBLOCK_PTR pCBlock = ( HB_CODEBLOCK_PTR ) Cargo; diff --git a/harbour/source/vm/garbage.c b/harbour/source/vm/garbage.c index b7350014f5..fc88cc5a06 100644 --- a/harbour/source/vm/garbage.c +++ b/harbour/source/vm/garbage.c @@ -42,31 +42,68 @@ #include "error.ch" /* holder of memory block information */ +/* NOTE: USHORT is used intentionally to fill up the structure to + * full 16 bytes (on 16/32 bit environment) + */ typedef struct HB_GARBAGE_ { struct HB_GARBAGE_ *pNext; /* next memory block */ struct HB_GARBAGE_ *pPrev; /* previous memory block */ HB_GARBAGE_FUNC_PTR pFunc; /* cleanup function called before memory releasing */ - ULONG status; /* block status */ + USHORT locked; /* locking counter */ + USHORT used; /* used/unused block */ +// ULONG counter; } HB_GARBAGE, *HB_GARBAGE_PTR; /* status of memory block */ #define HB_GC_UNLOCKED 0 #define HB_GC_LOCKED 1 /* do not collect a memory block */ -#define HB_GC_NOTCHECKED 2 /* this item was not checked yet */ -#define HB_GC_CHECKING 4 /* this item is checked currently */ +#define HB_GC_USED_FLAG 2 /* the bit for used/unused flag */ /* pointer to memory block that will be checked in next step */ static HB_GARBAGE_PTR s_pCurrBlock = NULL; /* memory blocks are stored in linked list with a loop */ +/* pointer to locked memory blocks */ +static HB_GARBAGE_PTR s_pLockedBlock = NULL; + /* marks if block releasing is requested during garbage collecting */ static BOOL s_bCollecting = FALSE; +/* flag for used/unused blocks - the meaning of the HB_GC_USED_FLAG bit + * is reversed on every collecting attempt + */ +static USHORT s_uUsedFlag = HB_GC_USED_FLAG; +//static ULONG s_ulCounter = 0; /* we may use a cache later */ #define HB_GARBAGE_NEW( ulSize ) ( HB_GARBAGE_PTR )hb_xgrab( ulSize ) #define HB_GARBAGE_FREE( pAlloc ) hb_xfree( (void *)(pAlloc) ) +static void hb_gcLink( HB_GARBAGE_PTR *pList, HB_GARBAGE_PTR pAlloc ) +{ + if( *pList ) + { + /* add new block at the logical end of list */ + pAlloc->pNext = *pList; + pAlloc->pPrev = (*pList)->pPrev; + pAlloc->pPrev->pNext = pAlloc; + (*pList)->pPrev = pAlloc; + } + else + { + *pList = pAlloc->pNext = pAlloc->pPrev = pAlloc; + } +} + +static void hb_gcUnlink( HB_GARBAGE_PTR *pList, HB_GARBAGE_PTR pAlloc ) +{ + pAlloc->pPrev->pNext = pAlloc->pNext; + pAlloc->pNext->pPrev = pAlloc->pPrev; + if( *pList == pAlloc ) + *pList = pAlloc->pNext; + if( ( pAlloc->pNext == pAlloc->pPrev ) && ( *pList == pAlloc ) ) + *pList = NULL; /* this was the last block */ +} /* allocates a memory block */ void * hb_gcAlloc( ULONG ulSize, HB_GARBAGE_FUNC_PTR pCleanupFunc ) @@ -76,22 +113,11 @@ void * hb_gcAlloc( ULONG ulSize, HB_GARBAGE_FUNC_PTR pCleanupFunc ) pAlloc = HB_GARBAGE_NEW( ulSize + sizeof( HB_GARBAGE ) ); if( pAlloc ) { - if( s_pCurrBlock ) - { - /* add new block at the logical end of list */ - pAlloc->pNext = s_pCurrBlock; - pAlloc->pPrev = s_pCurrBlock->pPrev; - pAlloc->pPrev->pNext = pAlloc; - s_pCurrBlock->pPrev = pAlloc; - } - else - { - s_pCurrBlock = pAlloc; - s_pCurrBlock->pNext = s_pCurrBlock->pPrev = pAlloc; - } - pAlloc->pFunc = pCleanupFunc; - pAlloc->status = HB_GC_UNLOCKED; - + hb_gcLink( &s_pCurrBlock, pAlloc ); + pAlloc->pFunc = pCleanupFunc; + pAlloc->locked = 0; + pAlloc->used = s_uUsedFlag; +//pAlloc->counter = ++s_ulCounter; return (void *)( pAlloc + 1 ); /* hide the internal data */ } else @@ -109,12 +135,10 @@ void hb_gcFree( void *pBlock ) if( !( s_bCollecting && pAlloc == s_pCurrBlock ) ) { /* Don't release the block that is a subject of collecting */ - pAlloc->pPrev->pNext = pAlloc->pNext; - pAlloc->pNext->pPrev = pAlloc->pPrev; - if( s_pCurrBlock == pAlloc ) - s_pCurrBlock = pAlloc->pNext; - if( pAlloc->pPrev == pAlloc->pPrev && pAlloc == s_pCurrBlock ) - s_pCurrBlock = NULL; /* this was the last block */ + if( pAlloc->locked ) + hb_gcUnlink( &s_pLockedBlock, pAlloc ); + else + hb_gcUnlink( &s_pCurrBlock, pAlloc ); HB_GARBAGE_FREE( pAlloc ); } } @@ -134,7 +158,12 @@ void *hb_gcLock( void *pBlock ) HB_GARBAGE_PTR pAlloc = ( HB_GARBAGE_PTR ) pBlock; --pAlloc; - pAlloc->status |= HB_GC_LOCKED; + if( !pAlloc->locked ) + { + hb_gcUnlink( &s_pCurrBlock, pAlloc ); + hb_gcLink( &s_pLockedBlock, pAlloc ); + } + ++pAlloc->locked; } return pBlock; } @@ -149,7 +178,15 @@ void *hb_gcUnlock( void *pBlock ) HB_GARBAGE_PTR pAlloc = ( HB_GARBAGE_PTR ) pBlock; --pAlloc; - pAlloc->status &= ~( ( ULONG ) HB_GC_LOCKED ); + if( pAlloc->locked ) + { + if( --pAlloc->locked == 0 ) + { + hb_gcUnlink( &s_pLockedBlock, pAlloc ); + hb_gcLink( &s_pCurrBlock, pAlloc ); + pAlloc->used = s_uUsedFlag; + } + } } return pBlock; } @@ -177,124 +214,145 @@ void hb_gcUnlockItem( HB_ITEM_PTR pItem ) hb_gcUnlock( pItem->item.asBlock.value ); } -/* Check a single memory block if it can be released - * The block will be released if it is not locked and there is no - * references for this block inside some harbour variable (local, - * memvar or static) -*/ -void hb_gcCollect( void ) +static void hb_gcReleaseItem( void ) { - if( s_pCurrBlock && !s_bCollecting ) - { - void *pBlock = ( void * )( s_pCurrBlock + 1 ); /* change for real pointer */ - HB_GARBAGE_PTR pNext = s_pCurrBlock->pNext; + HB_GARBAGE_PTR pDelete; - s_pCurrBlock->status &= ~( (ULONG)HB_GC_NOTCHECKED ); - if( !( s_pCurrBlock->status & HB_GC_LOCKED ) ) + /* call a cleanup function */ + if( s_pCurrBlock->pFunc ) + ( s_pCurrBlock->pFunc )( ( void *)( s_pCurrBlock + 1 ) ); + + pDelete = s_pCurrBlock; + hb_gcUnlink( &s_pCurrBlock, s_pCurrBlock ); + HB_GARBAGE_FREE( pDelete ); +} + +/* Mark a passed item as used so it will be not released by the GC +*/ +void hb_gcItemRef( HB_ITEM_PTR pItem ) +{ + if( HB_IS_BYREF( pItem ) ) + pItem = hb_itemUnRef( pItem ); + + if( HB_IS_ARRAY( pItem ) ) + { + HB_GARBAGE_PTR pAlloc = ( HB_GARBAGE_PTR ) pItem->item.asArray.value; + --pAlloc; + + /* Check this array only if it was not checked yet */ + if( pAlloc->used == s_uUsedFlag ) { - if( !hb_vmIsLocalRef( pBlock ) ) + ULONG ulSize = pItem->item.asArray.value->ulLen; + /* mark this block as used so it will be no re-checked from + * other references + */ + pAlloc->used ^= HB_GC_USED_FLAG; + + /* mark also all array elements */ + pItem = pItem->item.asArray.value->pItems; + while( ulSize ) { - if( !hb_memvarsIsMemvarRef( pBlock ) ) - { - if( !hb_vmIsStaticRef( pBlock ) ) - { - if( !hb_clsIsClassRef( pBlock ) ) - { - /* It is possible that s_pCurrBlock will be requested - * to release from a cleanup function - to prevent it - * we have to use some flag. - */ - s_bCollecting = TRUE; - if( s_pCurrBlock->pFunc ) - ( s_pCurrBlock->pFunc )( ( void *)( s_pCurrBlock + 1 ) ); - s_pCurrBlock->pPrev->pNext = s_pCurrBlock->pNext; - s_pCurrBlock->pNext->pPrev = s_pCurrBlock->pPrev; - pNext = s_pCurrBlock->pNext; - if( s_pCurrBlock == pNext && pNext->pPrev == pNext->pNext ) - pNext = NULL; /* this was the last block */ - HB_GARBAGE_FREE( s_pCurrBlock ); - s_bCollecting = FALSE; - } - } - } + hb_gcItemRef( pItem++ ); + --ulSize; } } - s_pCurrBlock = pNext; } + else if( HB_IS_BLOCK( pItem ) ) + { + HB_GARBAGE_PTR pAlloc = ( HB_GARBAGE_PTR ) pItem->item.asBlock.value; + --pAlloc; + if( pAlloc->used == s_uUsedFlag ) + pAlloc->used ^= HB_GC_USED_FLAG; /* mark this codeblock as used */ + } + /* all other data types don't need the GC */ +} + + +void hb_gcCollect( void ) +{ + /* TODO: decrease the amount of time spend collecting */ + hb_gcCollectAll(); } /* Check all memory block if they can be released */ void hb_gcCollectAll( void ) { - if( s_pCurrBlock ) + if( s_pCurrBlock && !s_bCollecting ) { - HB_GARBAGE_PTR pBlock = s_pCurrBlock; - - do + HB_GARBAGE_PTR pAlloc; + + s_bCollecting = TRUE; + + /* Step 1 - mark */ + /* All blocks are already marked because we are flipping + * the used/unused flag + */ + + /* Step 2 - sweep */ + /* check all known places for blocks they are referring */ + hb_vmIsLocalRef(); + hb_vmIsStaticRef(); + hb_memvarsIsMemvarRef(); + hb_clsIsClassRef(); + + /* check list of locked block for blocks referenced from + * locked block + */ + if( s_pLockedBlock ) { - pBlock->status |= HB_GC_NOTCHECKED; - pBlock = pBlock->pNext; - } while (pBlock != s_pCurrBlock); - - do - { - hb_gcCollect(); - } while( s_pCurrBlock && (s_pCurrBlock->status & HB_GC_NOTCHECKED) ); - } -} - -/* Check if passed item contains a reference to passed - * memory pointer - * Returns TRUE if there is a reference (the pointer cannot be released) - * or returns FALSE if the item doesn't refer the pointer. - * Arrays are scanned recursively. -*/ -BOOL hb_gcItemRef( HB_ITEM_PTR pItem, void *pBlock ) -{ - if( HB_IS_BYREF( pItem ) ) - { - if( HB_IS_MEMVAR( pItem ) ) - pItem = hb_itemUnRef( pItem ); /* detached variable */ - else - return FALSE; /* all items should be passed directly */ - } - - if( HB_IS_ARRAY( pItem ) ) - { - /* NOTE: this checks for objects too */ - if( pItem->item.asArray.value == ( HB_BASEARRAY_PTR )pBlock ) - return TRUE; - else - { - HB_GARBAGE_PTR pAlloc = ( HB_GARBAGE_PTR ) pItem->item.asArray.value; - ULONG ulSize = pItem->item.asArray.value->ulLen; - - --pAlloc; - if( !( pAlloc->status & HB_GC_CHECKING ) ) - { - pAlloc->status |= HB_GC_CHECKING; - pItem = pItem->item.asArray.value->pItems; - while( ulSize-- ) + pAlloc = s_pLockedBlock; + do + { /* it is not very elegant method but it works well */ + if( pAlloc->pFunc == hb_arrayReleaseGarbage ) { - if( hb_gcItemRef( pItem, pBlock ) ) + HB_BASEARRAY_PTR pArray = ( HB_BASEARRAY_PTR ) ( pAlloc + 1 ); + ULONG ulSize = pArray->ulLen; + HB_ITEM_PTR pItem; + + /* mark as used all elements in locked array */ + pItem = pArray->pItems; + while( ulSize ) { - pAlloc->status &= ~( (ULONG) ( HB_GC_CHECKING ) ); - return TRUE; + hb_gcItemRef( pItem++ ); + --ulSize; } - else - ++pItem; - } - pAlloc->status &= ~( (ULONG) ( HB_GC_CHECKING ) ); - } - } - } - else if( HB_IS_BLOCK( pItem ) ) - { - return ( pItem->item.asBlock.value == ( HB_CODEBLOCK_PTR )pBlock ); - } + } /* it is not very elegant method but it works well */ + else if( pAlloc->pFunc == hb_codeblockDeleteGarbage ) + { + HB_CODEBLOCK_PTR pCBlock = ( HB_CODEBLOCK_PTR ) ( pAlloc + 1 ); + USHORT ui = 1; - return FALSE; + /* mark as used all detached variables in locked codeblock */ + while( ui <= pCBlock->uiLocals ) + { + hb_gcItemRef( &pCBlock->pLocals[ ui ] ); + ++ui; + } + } + pAlloc = pAlloc->pNext; + } while ( s_pLockedBlock != pAlloc ); + } + + /* Step 3 - finalize */ + /* Release all blocks that are still marked as unused */ + pAlloc = s_pCurrBlock; + do + { + if( s_pCurrBlock->used == s_uUsedFlag ) + hb_gcReleaseItem( ); + else + s_pCurrBlock = s_pCurrBlock->pNext; + } while ( s_pCurrBlock && (pAlloc != s_pCurrBlock) ); + + s_bCollecting = FALSE; + + /* Step 4 - flip flag */ + /* Reverse used/unused flag so we don't have to mark all blocks + * during next collecting + */ + s_uUsedFlag ^= HB_GC_USED_FLAG; + } } /* service a single garbage collector step diff --git a/harbour/source/vm/hvm.c b/harbour/source/vm/hvm.c index 2acf61d28e..6bf0bb3dfd 100644 --- a/harbour/source/vm/hvm.c +++ b/harbour/source/vm/hvm.c @@ -350,16 +350,17 @@ void hb_vmQuit( void ) hb_dynsymRelease(); /* releases the dynamic symbol table */ hb_conRelease(); /* releases Console */ hb_setRelease(); /* releases Sets */ - + /* release all known garbage */ hb_gcCollectAll(); - + /* release all remaining items */ while( hb_stack.pPos > hb_stack.pItems ) hb_stackPop(); hb_itemClear( &hb_stack.Return ); hb_arrayRelease( &s_aStatics ); hb_memvarsRelease(); + hb_stackFree(); /* hb_dynsymLog(); */ hb_xexit(); @@ -4356,32 +4357,34 @@ WINBASEAPI LONG WINAPI UnhandledExceptionFilter( struct _EXCEPTION_POINTERS * Ex /* The garbage collector interface */ /* ------------------------------------------------------------------------ */ -/* check if passed pointer is referenced on the eval stack (local variables) - Returns TRUE if is referenced otherwise returns FALSE; -*/ -BOOL hb_vmIsLocalRef( void *pBlock ) +/* Mark all locals as used so they will not be released by the + * garbage collector + */ +void hb_vmIsLocalRef( void ) { + HB_TRACE(HB_TR_DEBUG, ("hb_vmIsLocalRef()")); + if( hb_stack.pPos > hb_stack.pItems ) { /* the eval stack is not cleared yet */ HB_ITEM_PTR pItem = hb_stack.pPos - 1; while( pItem != hb_stack.pItems ) { - if( hb_gcItemRef( pItem, pBlock ) ) - return TRUE; - else - --pItem; + if( pItem->type & (HB_IT_BYREF | HB_IT_ARRAY | HB_IT_BLOCK) ) + hb_gcItemRef( pItem ); + --pItem; } } - return FALSE; } -/* check if passed pointer is referenced in static variables - Returns TRUE if is referenced otherwise returns FALSE; -*/ -BOOL hb_vmIsStaticRef( void *pBlock ) +/* Mark all statics as used so they will not be released by the + * garbage collector + */ +void hb_vmIsStaticRef( void ) { + HB_TRACE(HB_TR_DEBUG, ("hb_vmIsStaticRef()")); + /* statics are stored as an item of array type */ - return hb_gcItemRef( &s_aStatics, pBlock ); + hb_gcItemRef( &s_aStatics ); } diff --git a/harbour/source/vm/memvars.c b/harbour/source/vm/memvars.c index 92f3e376d6..88df31011f 100644 --- a/harbour/source/vm/memvars.c +++ b/harbour/source/vm/memvars.c @@ -306,7 +306,7 @@ void hb_memvarValueDecRef( HB_HANDLE hValue ) if( pValue->counter > 0 ) { - /* Notice that Counter can be equal to 0. + /* Notice that Counter can be equal to 0. * This can happen if for example PUBLIC variable holds a codeblock * with detached variable. When hb_memvarsRelease() is called then * detached variable can be released before the codeblock. So if @@ -1246,7 +1246,7 @@ HB_FUNC( __MVSAVE ) pDynVar = s_privateStack[ ulBase ]; /* NOTE: Harbour name lengths are not limited, but the .MEM file - structure is not flexible enough to allow for it. + structure is not flexible enough to allow for it. [vszakats] */ if( pDynVar->hMemvar ) { @@ -1479,12 +1479,12 @@ HB_FUNC( __MVRESTORE ) /* ----------------------------------------------------------------------- */ -/* check if passed pointer is referenced in memvar variable - Returns TRUE if it is referenced otherwise returns FALSE; -*/ -BOOL hb_memvarsIsMemvarRef( void *pBlock ) +/* Mark all memvars as used so they will not be released by the + * garbage collector + */ +void hb_memvarsIsMemvarRef( void ) { - HB_TRACE(HB_TR_DEBUG, ("hb_memvarsIsMemvarRef(%p)", pBlock)); + HB_TRACE(HB_TR_DEBUG, ("hb_memvarsIsMemvarRef()")); if( s_globalTable ) { @@ -1492,16 +1492,14 @@ BOOL hb_memvarsIsMemvarRef( void *pBlock ) while( ulCnt ) { + /* do not check detached variables - for these variables only + * references from the eval stack are meaningfull for the GC + */ if( s_globalTable[ ulCnt ].counter && s_globalTable[ ulCnt ].hPrevMemvar != ( HB_HANDLE )-1 ) { - /* do not check detached variables - for these variables only - * references from the eval stack are meaningfull for the GC - */ - if( hb_gcItemRef( &s_globalTable[ ulCnt ].item, pBlock ) ) - return TRUE; /* this item is referenced - stop processing */ + hb_gcItemRef( &s_globalTable[ ulCnt ].item ); } --ulCnt; } } - return FALSE; }