From f184dc34f71d883c259b5dcb9550ea9182a9e9f4 Mon Sep 17 00:00:00 2001 From: Przemyslaw Czerpak Date: Thu, 16 Apr 2009 15:48:13 +0000 Subject: [PATCH] 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 --- harbour/ChangeLog | 28 ++++++++++++++++++++++ harbour/source/vm/garbage.c | 48 +++++++++++++++++++++++++++++++++++-- 2 files changed, 74 insertions(+), 2 deletions(-) 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 );