From 8e70525b66172e8a06c9dbd9380df78bfe31a40c Mon Sep 17 00:00:00 2001 From: Przemyslaw Czerpak Date: Fri, 5 Jun 2009 04:01:40 +0000 Subject: [PATCH] 2009-06-05 06:10 UTC+0200 Przemyslaw Czerpak (druzus/at/priv.onet.pl) * harbour/include/hbclass.ch + extended support for xbase++ compatible method declaration: [SYNC] METHOD [([])] ; [ , [([])] ] * harbour/include/hbthread.h * harbour/source/vm/thread.c * harbour/source/vm/classes.c + added alternative support for SYNC method which does not use sync mutexes. The old method is still present. I'll remove it when xbase++ users confirm that the new one exactly emulates xbase++ behavior. * harbour/source/vm/classes.c * harbour/source/rtl/tthreadx.prg * switch to alternative SYNC method implementation which seems to be xbase++ compatible - please test ! fixed missing thread interrupt in recent modification for ::setInterval --- harbour/ChangeLog | 20 ++++ harbour/include/hbclass.ch | 4 +- harbour/include/hbthread.h | 2 + harbour/source/rtl/tthreadx.prg | 29 +++--- harbour/source/vm/classes.c | 108 ++++++++++++++------ harbour/source/vm/thread.c | 171 +++++++++++++++++++++++++++++++- 6 files changed, 284 insertions(+), 50 deletions(-) diff --git a/harbour/ChangeLog b/harbour/ChangeLog index 994f864243..2c5e7ac5c5 100644 --- a/harbour/ChangeLog +++ b/harbour/ChangeLog @@ -17,6 +17,26 @@ past entries belonging to these authors: Viktor Szakats. */ +2009-06-05 06:10 UTC+0200 Przemyslaw Czerpak (druzus/at/priv.onet.pl) + * harbour/include/hbclass.ch + + extended support for xbase++ compatible method declaration: + [SYNC] METHOD [([])] ; + [ , [([])] ] + + * harbour/include/hbthread.h + * harbour/source/vm/thread.c + * harbour/source/vm/classes.c + + added alternative support for SYNC method which does not use + sync mutexes. The old method is still present. I'll remove it + when xbase++ users confirm that the new one exactly emulates + xbase++ behavior. + + * harbour/source/vm/classes.c + * harbour/source/rtl/tthreadx.prg + * switch to alternative SYNC method implementation which seems to be + xbase++ compatible - please test + ! fixed missing thread interrupt in recent modification for ::setInterval + 2009-06-04 23:00 UTC+0200 Viktor Szakats (harbour.01 syenar.hu) * utils/hbmk2/hbmk2.prg + Added support for hbcs= lins in .hbc files. This means diff --git a/harbour/include/hbclass.ch b/harbour/include/hbclass.ch index f78c116934..1b4c5d0ec2 100644 --- a/harbour/include/hbclass.ch +++ b/harbour/include/hbclass.ch @@ -571,8 +571,10 @@ DECLARE HBClass ; #xcommand SYNC METHOD [] => METHOD [] SYNC #xcommand SYNC CLASS METHOD [] => CLASSMETHOD [] SYNC - #xcommand METHOD , [, ] => ; + #xcommand METHOD [([])], [([])] [, [([])]] => ; METHOD [ ; METHOD ] [ ; METHOD ] + #xcommand SYNC METHOD [([])], [([])] [, [([])]] => ; + SYNC METHOD [ ; SYNC METHOD ] [ ; SYNC METHOD ] #xcommand METHOD :[([])] => ; METHOD ( ) CLASS diff --git a/harbour/include/hbthread.h b/harbour/include/hbthread.h index 17650d3981..c62b93ba6d 100644 --- a/harbour/include/hbthread.h +++ b/harbour/include/hbthread.h @@ -340,6 +340,8 @@ extern PHB_ITEM hb_threadMutexTimedSubscribe( PHB_ITEM pItem, ULONG ulMilliSec, #if defined( HB_MT_VM ) && defined( _HB_API_INTERNAL_ ) extern void hb_threadMutexUnlockAll( void ); +extern void hb_threadMutexSyncSignal( PHB_ITEM pItemMtx ); +extern BOOL hb_threadMutexSyncWait( PHB_ITEM pItemMtx, ULONG ulMilliSec, PHB_ITEM pItemSync ); #if defined( HB_NO_TLS ) # undef HB_USE_TLS diff --git a/harbour/source/rtl/tthreadx.prg b/harbour/source/rtl/tthreadx.prg index f9308bae94..92b5c37fa6 100644 --- a/harbour/source/rtl/tthreadx.prg +++ b/harbour/source/rtl/tthreadx.prg @@ -86,15 +86,10 @@ METHOD new( ... ) CLASS TSIGNAL RETURN Self METHOD wait( nTimeOut ) CLASS TSIGNAL - /* TOCHECK: I do not know if strict Xbase++ compatibility needs - * hb_mutexSubscribe() or hb_mutexSubscribeNow() - * Please change it if necessary - */ - RETURN hb_mutexSubscribe( ::mutex, ; - iif( ISNUMBER( nTimeOut ), nTimeOut / 100, ) ) + RETURN __ClsSyncWait( ::mutex, nTimeOut ) METHOD signal() CLASS TSIGNAL - hb_mutexNotify( ::mutex ) + __ClsSyncSignal( ::mutex ) RETURN Self @@ -105,22 +100,22 @@ METHOD signal() CLASS TSIGNAL CREATE CLASS TThread FUNCTION Thread EXPORTED: - VAR cargo AS USUAL SYNC + VAR cargo AS USUAL VAR active AS LOGICAL READONLY INIT .f. VAR deltaTime AS NUMERIC READONLY INIT 0 VAR interval AS USUAL READONLY INIT NIL VAR priority AS NUMERIC READONLY INIT 0 VAR startCount AS NUMERIC READONLY INIT 0 VAR startTime AS USUAL READONLY INIT NIL - VAR atEnd AS USUAL INIT NIL SYNC - VAR atStart AS USUAL INIT NIL SYNC - VAR result AS USUAL INIT NIL SYNC + VAR atEnd AS USUAL INIT NIL + VAR atStart AS USUAL INIT NIL + VAR result AS USUAL INIT NIL PROTECTED: VAR maxStackSize AS USUAL INIT 50000 HIDDEN: - VAR pThreadID AS USUAL INIT NIL SYNC + VAR pThreadID AS USUAL INIT NIL EXPORTED: METHOD new() @@ -131,11 +126,11 @@ PROTECTED: METHOD execute() EXPORTED: - METHOD quit( xResult, nRestart ) SYNC + METHOD quit( xResult, nRestart ) METHOD setInterval( nHSeconds ) METHOD setPriority( nPriority ) METHOD setStartTime( nSeconds ) - METHOD start() SYNC + METHOD start() METHOD synchronize( nTimeOut ) METHOD threadSelf() METHOD threadID() @@ -179,7 +174,7 @@ METHOD quit( xResult, nRestart ) CLASS TTHREAD RETURN NIL METHOD setInterval( nHSeconds ) CLASS TTHREAD - IF ISNUMBER( nHSeconds ) + IF nHSeconds == NIL .OR. ISNUMBER( nHSeconds ) ::interval := nHSeconds ENDIF RETURN .F. @@ -212,11 +207,12 @@ METHOD start( xAction, ... ) CLASS TTHREAD ::startTime := Seconds() ThreadObject( Self ) ::result := ::execute( ... ) + ::startTime := NIL IF ISNUMBER( ::interval ) hb_idleSleep( ::interval / 100 ) LOOP ENDIF - ::startTime := NIL + EXIT ENDDO RETURN NIL }, ... ) @@ -239,6 +235,7 @@ METHOD start( xAction, ... ) CLASS TTHREAD hb_idleSleep( ::interval / 100 ) LOOP ENDIF + EXIT ENDDO RETURN NIL }, ... ) diff --git a/harbour/source/vm/classes.c b/harbour/source/vm/classes.c index a4e7d4e716..4d0333f511 100644 --- a/harbour/source/vm/classes.c +++ b/harbour/source/vm/classes.c @@ -926,7 +926,7 @@ static void hb_clsCopyClass( PCLASS pClsDst, PCLASS pClsSrc ) pClsDst->uiMutexOffset = pClsSrc->uiMutexOffset; pClsDst->ulOpFlags = pClsSrc->ulOpFlags; if( pClsSrc->pMutex ) - pClsDst->pMutex = hb_threadMutexCreate( TRUE ); + pClsDst->pMutex = hb_threadMutexCreate( FALSE ); if( pClsSrc->uiInitDatas ) { @@ -2991,7 +2991,7 @@ static BOOL hb_clsAddMsg( USHORT uiClass, const char * szMessage, if( uiScope & HB_OO_CLSTP_CLASS ) { if( !pClass->pMutex ) - pClass->pMutex = hb_threadMutexCreate( TRUE ); + pClass->pMutex = hb_threadMutexCreate( FALSE ); pNewMeth->pFuncSym = &s___msgSyncClass; } else @@ -3353,7 +3353,7 @@ static USHORT hb_clsNew( const char * szClassName, USHORT uiDatas, if( pNewCls->uiMutexOffset ) pNewCls->uiMutexOffset = pNewCls->uiDatas + 1; if( fClsMutex && !pNewCls->pMutex ) - pNewCls->pMutex = hb_threadMutexCreate( TRUE ); + pNewCls->pMutex = hb_threadMutexCreate( FALSE ); return s_uiClasses; } @@ -3479,7 +3479,7 @@ static PHB_ITEM hb_clsInst( USHORT uiClass ) if( pClass->uiMutexOffset ) { - PHB_ITEM pMutex = hb_threadMutexCreate( TRUE ); + PHB_ITEM pMutex = hb_threadMutexCreate( FALSE ); hb_arraySet( pSelf, pClass->uiMutexOffset, pMutex ); hb_itemRelease( pMutex ); } @@ -4090,6 +4090,60 @@ HB_FUNC( __SENDER ) } } +HB_FUNC( __CLSSYNCSIGNAL ) +{ +#if defined( HB_MT_VM ) + hb_threadMutexSyncSignal( hb_param( 1, HB_IT_ANY ) ); +#endif /* HB_MT_VM */ +} + +HB_FUNC( __CLSSYNCWAIT ) +{ +#if defined( HB_MT_VM ) + HB_STACK_TLS_PRELOAD + PHB_ITEM pMutex = NULL; + ULONG ulMilliSec = HB_THREAD_INFINITE_WAIT; + LONG lOffset = hb_stackBaseProcOffset( 2 ); + + if( lOffset > 0 ) + { + PHB_ITEM pBase = hb_stackItem( lOffset ); + PHB_STACK_STATE pStack = pBase->item.asSymbol.stackstate; + USHORT uiClass = pStack->uiClass; + + if( uiClass && uiClass <= s_uiClasses ) + { + PCLASS pClass = s_pClasses[ uiClass ]; + PMETHOD pMethod = pClass->pMethods + pStack->uiMethod; + + if( pMethod->pFuncSym == &s___msgSync ) + { + PHB_ITEM pSelf = hb_stackItem( lOffset + 1 ); + + /* Is it inline method? */ + if( HB_IS_BLOCK( pSelf ) && pBase->item.asSymbol.value == &hb_symEval ) + pSelf = hb_stackItem( pBase->item.asSymbol.stackstate->lBaseItem + 1 ); + + uiClass = hb_objGetClass( pSelf ); + if( uiClass && uiClass <= s_uiClasses ) + pMutex = hb_arrayGetItemPtr( pSelf, s_pClasses[ uiClass ]->uiMutexOffset ); + } + else if( pMethod->pFuncSym == &s___msgSyncClass ) + pMutex = pClass->pMutex; + } + } + + if( ISNUM( 2 ) ) + { + double dTimeOut = hb_parnd( 2 ); + if( dTimeOut > 0 ) + ulMilliSec = ( ULONG ) ( dTimeOut * 10 ); + } + + hb_retl( hb_threadMutexSyncWait( hb_param( 1, HB_IT_ANY ), ulMilliSec, pMutex ) ); +#endif /* HB_MT_VM */ +} + /* * ClassH( ) -> * @@ -4136,32 +4190,37 @@ static HARBOUR hb___msgClassName( void ) static int hb_methodType( PMETHOD pMethod ) { - if ( pMethod->pFuncSym == &s___msgSetClsData || - pMethod->pFuncSym == &s___msgGetClsData || - pMethod->pFuncSym == &s___msgSetShrData || - pMethod->pFuncSym == &s___msgGetShrData ) + PHB_SYMB pFuncSym = pMethod->pFuncSym; + + if( pFuncSym == &s___msgSync || pFuncSym == &s___msgSyncClass ) + pFuncSym = pMethod->pRealSym; + + if ( pFuncSym == &s___msgSetClsData || + pFuncSym == &s___msgGetClsData || + pFuncSym == &s___msgSetShrData || + pFuncSym == &s___msgGetShrData ) return HB_OO_MSG_CLASSDATA; - else if( pMethod->pFuncSym == &s___msgSetData || - pMethod->pFuncSym == &s___msgGetData ) + else if( pFuncSym == &s___msgSetData || + pFuncSym == &s___msgGetData ) return HB_OO_MSG_DATA; - else if( pMethod->pFuncSym == &s___msgEvalInline ) + else if( pFuncSym == &s___msgEvalInline ) return HB_OO_MSG_INLINE; - else if( pMethod->pFuncSym == &s___msgVirtual ) + else if( pFuncSym == &s___msgVirtual ) return HB_OO_MSG_VIRTUAL; - else if( pMethod->pFuncSym == &s___msgSuper ) + else if( pFuncSym == &s___msgSuper ) return HB_OO_MSG_SUPER; - else if( pMethod->pFuncSym == &s___msgRealClass ) + else if( pFuncSym == &s___msgRealClass ) return HB_OO_MSG_REALCLASS; - else if( pMethod->pFuncSym == &s___msgDelegate ) + else if( pFuncSym == &s___msgDelegate ) return HB_OO_MSG_DELEGATE; - else if( pMethod->pFuncSym == &s___msgPerform ) + else if( pFuncSym == &s___msgPerform ) return HB_OO_MSG_PERFORM; else if( pMethod->pMessage == s___msgOnError.pDynSym ) @@ -4204,22 +4263,9 @@ static HARBOUR hb___msgClassSel( void ) { if( ( nParam == HB_MSGLISTALL ) || ( nParam == HB_MSGLISTCLASS && - ( - ( pMethod->pFuncSym == &s___msgSetClsData ) || - ( pMethod->pFuncSym == &s___msgGetClsData ) || - ( pMethod->pFuncSym == &s___msgSetShrData ) || - ( pMethod->pFuncSym == &s___msgGetShrData ) - ) - ) || + hb_methodType( pMethod ) == HB_OO_MSG_CLASSDATA ) || ( nParam == HB_MSGLISTPURE && - !( - ( pMethod->pFuncSym == &s___msgSetClsData ) || - ( pMethod->pFuncSym == &s___msgGetClsData ) || - ( pMethod->pFuncSym == &s___msgSetShrData ) || - ( pMethod->pFuncSym == &s___msgGetShrData ) - ) - ) - ) + hb_methodType( pMethod ) != HB_OO_MSG_CLASSDATA ) ) { if( nScope == 0 || ( pMethod->uiScope & nScope ) != 0 ) { diff --git a/harbour/source/vm/thread.c b/harbour/source/vm/thread.c index 3061e7c3fe..7ef0092222 100644 --- a/harbour/source/vm/thread.c +++ b/harbour/source/vm/thread.c @@ -1549,6 +1549,173 @@ PHB_ITEM hb_threadMutexCreate( BOOL fSync ) return pItem; } +#if defined( HB_MT_VM ) +void hb_threadMutexSyncSignal( PHB_ITEM pItemMtx ) +{ + PHB_MUTEX pMutex = hb_mutexPtr( pItemMtx ); + + if( pMutex ) + { + HB_CRITICAL_LOCK( pMutex->mutex ); + + if( pMutex->waiters ) + { + int iCount = pMutex->waiters; + ULONG ulLen; + + if( pMutex->events ) + { + ulLen = hb_arrayLen( pMutex->events ); + iCount -= ulLen; + if( iCount > 0 ) + hb_arraySize( pMutex->events, ulLen + iCount ); + } + else + { + ulLen = 0; + pMutex->events = hb_itemArrayNew( iCount ); + } + if( iCount == 1 ) + HB_COND_SIGNAL( pMutex->cond_w ); + else if( iCount > 0 ) + HB_COND_SIGNALN( pMutex->cond_w, iCount ); + } + else if( !pMutex->events ) + pMutex->events = hb_itemArrayNew( 1 ); + + HB_CRITICAL_UNLOCK( pMutex->mutex ); + } +} + +BOOL hb_threadMutexSyncWait( PHB_ITEM pItemMtx, ULONG ulMilliSec, + PHB_ITEM pItemSync ) +{ + PHB_MUTEX pMutex = hb_mutexPtr( pItemMtx ), pSyncMutex = NULL; + BOOL fResult = FALSE; + + if( pMutex ) + { + if( pItemSync ) + { + pSyncMutex = hb_mutexPtr( pItemSync ); + if( !pSyncMutex ) + pMutex = NULL; + } + } + + if( pMutex ) + { + int lock_count = 0; + + hb_vmUnlock(); + + HB_CRITICAL_LOCK( pMutex->mutex ); + + if( ulMilliSec && !( pMutex->events && hb_arrayLen( pMutex->events ) > 0 ) ) + { + /* release own locks from sync mutex */ + if( pSyncMutex && HB_THREAD_EQUAL( pSyncMutex->owner, HB_THREAD_SELF() ) ) + { + HB_CRITICAL_LOCK( pSyncMutex->mutex ); + lock_count = pSyncMutex->lock_count; + pSyncMutex->lock_count = 0; + pSyncMutex->owner = ( HB_THREAD_ID ) 0; + if( pSyncMutex->lockers ) + HB_COND_SIGNAL( pSyncMutex->cond_l ); + HB_CRITICAL_UNLOCK( pSyncMutex->mutex ); + } + + if( ulMilliSec == HB_THREAD_INFINITE_WAIT ) + { + while( !pMutex->events || hb_arrayLen( pMutex->events ) == 0 ) + { + pMutex->waiters++; +# if defined( HB_PTHREAD_API ) + pthread_cond_wait( &pMutex->cond_w, &pMutex->mutex ); +# elif defined( HB_COND_HARBOUR_SUPPORT ) + _hb_thread_cond_wait( &pMutex->cond_w, &pMutex->mutex, HB_THREAD_INFINITE_WAIT ); +# else + HB_CRITICAL_UNLOCK( pMutex->mutex ); + ( void ) HB_COND_WAIT( pMutex->cond_w ); + HB_CRITICAL_LOCK( pMutex->mutex ); +# endif + pMutex->waiters--; + } + } + else + { + pMutex->waiters++; +# if defined( HB_PTHREAD_API ) + { + struct timespec ts; + + hb_threadTimeInit( &ts, ulMilliSec ); + while( !pMutex->events || hb_arrayLen( pMutex->events ) == 0 ) + { + if( pthread_cond_timedwait( &pMutex->cond_w, &pMutex->mutex, &ts ) != 0 ) + break; + } + } +# else + { + /* TODO: on some platforms HB_COND_SIGNAL() may wake up more then + * one thread so we should use while loop to check if wait + * condition is true. + */ +# if defined( HB_COND_HARBOUR_SUPPORT ) + _hb_thread_cond_wait( &pMutex->cond_w, &pMutex->mutex, ulMilliSec ); +# else + HB_CRITICAL_UNLOCK( pMutex->mutex ); + ( void ) HB_COND_TIMEDWAIT( pMutex->cond_w, ulMilliSec ); + HB_CRITICAL_LOCK( pMutex->mutex ); +# endif + } +# endif + pMutex->waiters--; + } + } + + if( pMutex->events && hb_arrayLen( pMutex->events ) > 0 ) + { + hb_arraySize( pMutex->events, hb_arrayLen( pMutex->events ) - 1 ); + fResult = TRUE; + } + + HB_CRITICAL_UNLOCK( pMutex->mutex ); + + /* restore the own locks on sync mutex if necessary */ + if( lock_count ) + { + HB_CRITICAL_LOCK( pSyncMutex->mutex ); + if( pSyncMutex->owner ) + { + pSyncMutex->lockers++; + while( pSyncMutex->lock_count != 0 ) + { +# if defined( HB_PTHREAD_API ) + pthread_cond_wait( &pSyncMutex->cond_l, &pSyncMutex->mutex ); +# elif defined( HB_COND_HARBOUR_SUPPORT ) + _hb_thread_cond_wait( &pSyncMutex->cond_l, &pSyncMutex->mutex, HB_THREAD_INFINITE_WAIT ); +# else + HB_CRITICAL_UNLOCK( pSyncMutex->mutex ); + ( void ) HB_COND_WAIT( pSyncMutex->cond_l ); + HB_CRITICAL_LOCK( pSyncMutex->mutex ); +# endif + } + pSyncMutex->lockers--; + } + pSyncMutex->lock_count = lock_count; + pSyncMutex->owner = HB_THREAD_SELF(); + HB_CRITICAL_UNLOCK( pSyncMutex->mutex ); + } + + hb_vmLock(); + } + + return fResult; +} +#endif /* HB_MT_VM */ + BOOL hb_threadMutexUnlock( PHB_ITEM pItem ) { PHB_MUTEX pMutex = hb_mutexPtr( pItem ); @@ -1838,7 +2005,7 @@ PHB_ITEM hb_threadMutexSubscribe( PHB_ITEM pItem, BOOL fClear ) if( fClear && pMutex->events ) hb_arraySize( pMutex->events, 0 ); - /* release own locak from this mutex */ + /* release own lock from this mutex */ if( HB_THREAD_EQUAL( pMutex->owner, HB_THREAD_SELF() ) ) { lock_count = pMutex->lock_count; @@ -1949,7 +2116,7 @@ PHB_ITEM hb_threadMutexTimedSubscribe( PHB_ITEM pItem, ULONG ulMilliSec, BOOL fC if( ulMilliSec && !( pMutex->events && hb_arrayLen( pMutex->events ) > 0 ) ) { - /* release own locak from this mutex */ + /* release own lock from this mutex */ if( HB_THREAD_EQUAL( pMutex->owner, HB_THREAD_SELF() ) ) { lock_count = pMutex->lock_count;