diff --git a/harbour/ChangeLog b/harbour/ChangeLog index 9717081c97..cc73be1b4b 100644 --- a/harbour/ChangeLog +++ b/harbour/ChangeLog @@ -8,6 +8,34 @@ 2009-12-31 13:59 UTC+0100 Foo Bar (foo.bar foobar.org) */ +2009-04-16 17:55 UTC+0200 Przemyslaw Czerpak (druzus/at/priv.onet.pl) + * harbour/source/vm/garbage.c + + added optional support for automatic GC activation. + It's not enabled by default yet but by compilation with macro + HB_GC_AUTO, f.e. after setting envvar: + set HB_USER_CFLAGS=-DHB_GC_AUTO + Automatic GC activation may be necessary for code which creates + complex variables with cyclic references and does not execute + any other code which may activate idle state GC call. It also + makes unnecessary to explicitly call hb_gcAll() from user code, + f.e. as some type of background task. If some of you were using + code which needed such explicit GC activation then please remove + it and make some tests with Harbour compiled with HB_GC_AUTO macro. + + This simple .prg code illustrates the problem. When linked with HVM + without automatic GC it quite fast allocates all available memory + and finished with out of memory error or is interrupted by OS. + When automatic GC is enabled it can work unlimited time period + with some fixed maximum memory usage. + + proc main() + local a + while .t. + a:={nil} + a[1]:=a + enddo + return + 2009-04-16 00:37 UTC-0800 Pritpal Bedi (pritpal@vouchcac.com) * harbour/contrib/gtqtc/gtqtc.cpp * harbour/contrib/gtqtc/gtqtc.h diff --git a/harbour/source/vm/garbage.c b/harbour/source/vm/garbage.c index 70ca465f10..0c39b5b64e 100644 --- a/harbour/source/vm/garbage.c +++ b/harbour/source/vm/garbage.c @@ -134,6 +134,18 @@ typedef struct HB_GARBAGE_ /* flags stored in 'flags' slot */ #define HB_GC_USERSWEEP 8 /* memory block with user defined sweep function */ +#ifdef HB_GC_AUTO +/* number of allocated memory blocks */ +static ULONG s_ulBlocks = 0; +/* number of allocated memory blocks after last GC activation */ +static ULONG s_ulBlocksMarked = 0; +# define HB_GC_AUTO_INC ++s_ulBlocks; +# define HB_GC_AUTO_DEC --s_ulBlocks; +#else +# define HB_GC_AUTO_INC +# define HB_GC_AUTO_DEC +#endif + /* 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 */ @@ -206,6 +218,15 @@ void * hb_gcAlloc( ULONG ulSize, HB_GARBAGE_FUNC_PTR pCleanupFunc ) pAlloc->flags = 0; HB_GC_LOCK +#ifdef HB_GC_AUTO + if( s_ulBlocks > s_ulBlocksMarked + 100000 ) + { + HB_GC_UNLOCK + hb_gcCollectAll( TRUE ); + HB_GC_LOCK + } + HB_GC_AUTO_INC +#endif hb_gcLink( &s_pCurrBlock, pAlloc ); HB_GC_UNLOCK @@ -226,7 +247,13 @@ void hb_gcFree( void *pBlock ) if( !( pAlloc->used & HB_GC_DELETE ) ) { HB_GC_LOCK - hb_gcUnlink( pAlloc->locked ? &s_pLockedBlock : &s_pCurrBlock, pAlloc ); + if( pAlloc->locked ) + hb_gcUnlink( &s_pLockedBlock, pAlloc ); + else + { + hb_gcUnlink( &s_pCurrBlock, pAlloc ); + HB_GC_AUTO_DEC + } HB_GC_UNLOCK if( pAlloc->flags & HB_GC_USERSWEEP ) @@ -278,7 +305,13 @@ void hb_gcRefFree( void * pBlock ) * if cleanup function activate GC */ HB_GC_LOCK - hb_gcUnlink( pAlloc->locked ? &s_pLockedBlock : &s_pCurrBlock, pAlloc ); + if( pAlloc->locked ) + hb_gcUnlink( &s_pLockedBlock, pAlloc ); + else + { + hb_gcUnlink( &s_pCurrBlock, pAlloc ); + HB_GC_AUTO_DEC + } HB_GC_UNLOCK pAlloc->used |= HB_GC_DELETE; @@ -323,6 +356,7 @@ void hb_gcRefCheck( void * pBlock ) HB_GC_LOCK hb_gcLink( &s_pCurrBlock, pAlloc ); + HB_GC_AUTO_INC HB_GC_UNLOCK if( hb_vmRequestQuery() == 0 ) @@ -400,6 +434,7 @@ void * hb_gcLock( void * pBlock ) { hb_gcUnlink( &s_pCurrBlock, pAlloc ); hb_gcLink( &s_pLockedBlock, pAlloc ); + HB_GC_AUTO_DEC } ++pAlloc->locked; HB_GC_UNLOCK @@ -426,6 +461,7 @@ void * hb_gcUnlock( void * pBlock ) hb_gcUnlink( &s_pLockedBlock, pAlloc ); hb_gcLink( &s_pCurrBlock, pAlloc ); + HB_GC_AUTO_INC } } HB_GC_UNLOCK @@ -713,6 +749,7 @@ void hb_gcCollectAll( BOOL fForce ) pDelete->used |= HB_GC_DELETE | HB_GC_DELETELST; hb_gcUnlink( &s_pCurrBlock, pDelete ); hb_gcLink( &s_pDeletedBlock, pDelete ); + HB_GC_AUTO_DEC } else { @@ -730,6 +767,11 @@ void hb_gcCollectAll( BOOL fForce ) */ s_uUsedFlag ^= HB_GC_USED_FLAG; +#ifdef HB_GC_AUTO + /* store number of marked blocks for automatic GC activation */ + s_ulBlocksMarked = s_ulBlocks; +#endif + hb_vmResumeThreads(); /* do we have any deleted blocks? */ @@ -757,6 +799,7 @@ void hb_gcCollectAll( BOOL fForce ) pDelete->locked = 0; HB_GC_LOCK hb_gcLink( &s_pCurrBlock, pDelete ); + HB_GC_AUTO_INC HB_GC_UNLOCK if( hb_vmRequestQuery() == 0 ) hb_errRT_BASE( EG_DESTRUCTOR, 1301, NULL, "Reference to freed block", 0 ); @@ -803,6 +846,7 @@ void hb_gcReleaseAll( void ) HB_TRACE( HB_TR_INFO, ( "Release %p", s_pCurrBlock ) ); pDelete = s_pCurrBlock; hb_gcUnlink( &s_pCurrBlock, pDelete ); + HB_GC_AUTO_DEC HB_GARBAGE_FREE( pDelete ); } while ( s_pCurrBlock );