From 844cf0e16bf337375232d8968282fca897349f3b Mon Sep 17 00:00:00 2001 From: Przemyslaw Czerpak Date: Wed, 18 Feb 2009 18:55:32 +0000 Subject: [PATCH] 2009-02-18 20:00 UTC+0100 Przemyslaw Czerpak (druzus/at/priv.onet.pl) * harbour/include/hbthread.h * harbour/source/vm/thread.c ! added support for conditional variables working like in PTHREADS in OS2 builds - it fixes the problem with possible dead lock or starvation effect which can also cause dead lock in some cases. OS2 users please test current code. --- harbour/ChangeLog | 8 ++ harbour/include/hbthread.h | 38 +++++--- harbour/source/vm/thread.c | 194 ++++++++++++++++++++++++++++++++----- 3 files changed, 207 insertions(+), 33 deletions(-) diff --git a/harbour/ChangeLog b/harbour/ChangeLog index d66646c482..2083c8636d 100644 --- a/harbour/ChangeLog +++ b/harbour/ChangeLog @@ -8,6 +8,14 @@ 2009-12-31 13:59 UTC+0100 Foo Bar (foo.bar foobar.org) */ +2009-02-18 20:00 UTC+0100 Przemyslaw Czerpak (druzus/at/priv.onet.pl) + * harbour/include/hbthread.h + * harbour/source/vm/thread.c + ! added support for conditional variables working like in PTHREADS + in OS2 builds - it fixes the problem with possible dead lock or + starvation effect which can also cause dead lock in some cases. + OS2 users please test current code. + 2009-02-18 18:59 UTC+0100 Viktor Szakats (harbour.01 syenar hu) * utils/hbmk/hbmk2.prg % Optimizations in FindInPath(). diff --git a/harbour/include/hbthread.h b/harbour/include/hbthread.h index 0ee49da84d..df56fd60c2 100644 --- a/harbour/include/hbthread.h +++ b/harbour/include/hbthread.h @@ -169,10 +169,24 @@ HB_EXTERN_BEGIN typedef TID HB_THREAD_ID; typedef TID HB_THREAD_HANDLE; typedef HMTX HB_RAWCRITICAL_T; - typedef HEV HB_RAWCOND_T; extern ULONG _hb_gettid( void ); + typedef struct _HB_WAIT_LIST + { + struct _HB_WAIT_LIST * prev; + struct _HB_WAIT_LIST * next; + HEV cond; + BOOL signaled; + } HB_WAIT_LIST, * PHB_WAIT_LIST; + + typedef struct + { + PHB_WAIT_LIST waiters; + } HB_COND_T, HB_RAWCOND_T; + +# define HB_COND_NEW( name ) HB_COND_T name = { NULL } + # define HB_THREAD_STARTFUNC( func ) void func( void * Cargo ) # define HB_THREAD_END _endthread(); return; # define HB_THREAD_RAWEND return; @@ -189,16 +203,15 @@ HB_EXTERN_BEGIN # define HB_CRITICAL_DESTROY(v) DosCloseMutexSem( v ) # define HB_CRITICAL_LOCK(v) DosRequestMutexSem( (v), SEM_INDEFINITE_WAIT ) # define HB_CRITICAL_UNLOCK(v) DosReleaseMutexSem( v ) -# define HB_COND_INIT(v) DosCreateEventSem( NULL, &(v), 0L, FALSE ); -# define HB_COND_DESTROY(v) DosCloseEventSem( v ) -# define HB_COND_SIGNAL(v) DosPostEventSem( v ) -# define HB_COND_SIGNALN(v,n) do { int i = (n); while( --i >= 0 ) DosPostEventSem( v ); } while(0) -# define HB_COND_WAIT(v) _hb_cond_timed_wait( (v), SEM_INDEFINITE_WAIT ) -# define HB_COND_TIMEDWAIT(v,n) _hb_cond_timed_wait( (v), (n) ) +# define HB_COND_SIGNAL(v) _hb_thread_cond_signal( &(v) ) +# define HB_COND_SIGNALN(v,n) _hb_thread_cond_broadcast( &(v) ) +# define HB_COND_WAIT(v) _hb_thread_cond_wait( (v), SEM_INDEFINITE_WAIT ) +# define HB_COND_TIMEDWAIT(v,n) _hb_thread_cond_wait( (v), (n) ) # undef HB_COND_OS_SUPPORT +# undef HB_COND_NEED_INIT +# define HB_COND_HARBOUR_SUPPORT # define HB_CRITICAL_NEED_INIT -# define HB_COND_NEED_INIT # define HB_THREAD_INFINITE_WAIT SEM_INDEFINITE_WAIT @@ -243,15 +256,15 @@ HB_EXTERN_BEGIN # if defined( HB_COND_OS_SUPPORT ) typedef struct { - BOOL fInit; + BOOL fInit; HB_RAWCOND_T cond; } HB_COND_T; # define HB_COND_NEW( name ) HB_COND_T name = { FALSE, HB_COND_INITVAL } # else typedef struct { - BOOL fInit; - int waiters; + BOOL fInit; + int waiters; HB_RAWCOND_T cond; HB_RAWCRITICAL_T critical; } HB_COND_T; @@ -289,6 +302,9 @@ typedef struct _HB_THREADSTATE HB_THREAD_HANDLE th_h; struct _HB_THREADSTATE * pPrev; struct _HB_THREADSTATE * pNext; +#if defined( HB_COND_HARBOUR_SUPPORT ) + HB_WAIT_LIST pWaitList; +#endif } HB_THREADSTATE, * PHB_THREADSTATE; extern void hb_threadInit( void ); diff --git a/harbour/source/vm/thread.c b/harbour/source/vm/thread.c index 9cb26e32df..eb2b76f97a 100644 --- a/harbour/source/vm/thread.c +++ b/harbour/source/vm/thread.c @@ -184,19 +184,111 @@ void hb_threadExit( void ) } } -#if defined( HB_OS_OS2 ) -BOOL _hb_cond_timed_wait( HEV v, ULONG n ) +#if defined( HB_OS_OS2 ) && defined( HB_MT_VM ) +static PHB_WAIT_LIST _hb_thread_wait_list( void ) { - ULONG ulPostCount = 0; - APIRET rc; + PHB_THREADSTATE pThread = ( PHB_THREADSTATE ) hb_vmThreadState(); - rc = DosWaitEventSem( v, n ); - DosResetEventSem( v, &ulPostCount ); - - return rc == NO_ERROR; + if( pThread ) + return &pThread->pWaitList; + else + return NULL; } +static void _hb_thread_wait_add( HB_COND_T * cond, PHB_WAIT_LIST pWaiting ) +{ + ULONG ulPostCount = 0; + if( cond->waiters == NULL ) + { + cond->waiters = pWaiting->next = pWaiting->prev = pWaiting; + } + else + { + pWaiting->next = cond->waiters; + pWaiting->prev = cond->waiters->prev; + cond->waiters->prev = pWaiting->prev->next = pWaiting; + } + pWaiting->signaled = FALSE; + DosResetEventSem( pWaiting->cond, &ulPostCount ); +} + +static void _hb_thread_wait_del( HB_COND_T * cond, PHB_WAIT_LIST pWaiting ) +{ + if( pWaiting->next == pWaiting->prev ) + cond->waiters = NULL; + else + { + pWaiting->next->prev = pWaiting->prev; + pWaiting->prev->next = pWaiting->next; + if( pWaiting == cond->waiters ) + cond->waiters = pWaiting->next; + } +} + +static BOOL _hb_thread_cond_signal( HB_COND_T * cond ) +{ + if( cond->waiters ) + { + PHB_WAIT_LIST pWaiting = cond->waiters; + do + { + if( !pWaiting->signaled ) + { + DosPostEventSem( pWaiting->cond ); + pWaiting->signaled = TRUE; + /* signal only single thread */ + break; + } + pWaiting = pWaiting->prev; + } + while( pWaiting != cond->waiters ); + } + + return TRUE; +} + +static BOOL _hb_thread_cond_broadcast( HB_COND_T * cond ) +{ + if( cond->waiters ) + { + PHB_WAIT_LIST pWaiting = cond->waiters; + do + { + if( !pWaiting->signaled ) + { + DosPostEventSem( pWaiting->cond ); + pWaiting->signaled = TRUE; + } + pWaiting = pWaiting->prev; + } + while( pWaiting != cond->waiters ); + } + + return TRUE; +} + +static BOOL _hb_thread_cond_wait( HB_COND_T * cond, HB_RAWCRITICAL_T * critical, ULONG ulMillisec ) +{ + PHB_WAIT_LIST pWaiting = _hb_thread_wait_list(); + BOOL fResult = FALSE; + + if( pWaiting ) + { + _hb_thread_wait_add( cond, pWaiting ); + + DosReleaseMutexSem( *critical ); + fResult = DosWaitEventSem( pWaiting->cond, ulMillisec ) == NO_ERROR; + DosRequestMutexSem( *critical, SEM_INDEFINITE_WAIT ); + + _hb_thread_wait_del( cond, pWaiting ); + } + + return fResult; +} +#endif + +#if defined( HB_OS_OS2 ) && !defined( __GNUC__ ) ULONG _hb_gettid( void ) { ULONG tid = 0; @@ -329,6 +421,10 @@ BOOL hb_threadCondSignal( HB_COND_T * cond ) # endif return pthread_cond_signal( HB_COND_GET( cond ) ) == 0; +#elif defined( HB_COND_HARBOUR_SUPPORT ) + + return _hb_thread_cond_signal( cond ); + #else if( !cond->fInit ) @@ -362,6 +458,10 @@ BOOL hb_threadCondBroadcast( HB_COND_T * cond ) # endif return pthread_cond_broadcast( HB_COND_GET( cond ) ) == 0; +#elif defined( HB_COND_HARBOUR_SUPPORT ) + + return _hb_thread_cond_broadcast( cond ); + #else if( !cond->fInit ) @@ -396,6 +496,10 @@ BOOL hb_threadCondWait( HB_COND_T * cond, HB_CRITICAL_T * mutex ) # endif return pthread_cond_wait( HB_COND_GET( cond ), HB_CRITICAL_GET( mutex ) ) == 0; +#elif defined( HB_COND_HARBOUR_SUPPORT ) + + return _hb_thread_cond_wait( cond, &mutex->critical, HB_THREAD_INFINITE_WAIT ); + #else BOOL fResult; @@ -449,6 +553,10 @@ BOOL hb_threadCondTimedWait( HB_COND_T * cond, HB_CRITICAL_T * mutex, ULONG ulMi hb_threadTimeInit( &ts, ulMilliSec ); return pthread_cond_timedwait( HB_COND_GET( cond ), HB_CRITICAL_GET( mutex ), &ts ) == 0; +#elif defined( HB_COND_HARBOUR_SUPPORT ) + + return _hb_thread_cond_wait( cond, &mutex->critical, ulMilliSec ); + #else BOOL fResult; @@ -603,6 +711,13 @@ static HB_GARBAGE_FUNC( hb_threadDestructor ) hb_gtRelease( pThread->hGT ); pThread->hGT = NULL; } +#if defined( HB_OS_OS2 ) + if( pThread->pWaitList.cond ) + { + DosCloseEventSem( pThread->pWaitList.cond ); + pThread->pWaitList.cond = ( HEV ) 0; + } +#endif } static HB_THREAD_STARTFUNC( hb_threadStartVM ) @@ -700,6 +815,10 @@ PHB_THREADSTATE hb_threadStateNew( void ) pThread->pThItm = pThItm; pThread->hGT = hb_gtAlloc( NULL ); +#if defined( HB_OS_OS2 ) + DosCreateEventSem( NULL, &pThread->pWaitList.cond, 0L, FALSE ); +#endif + return pThread; } @@ -926,6 +1045,12 @@ static int hb_threadWait( PHB_THREADSTATE * pThreads, int iThreads, fExit = pthread_cond_wait( &s_thread_cond, &s_thread_mtx ) != 0; hb_vmLock(); #else +# if defined( HB_COND_HARBOUR_SUPPORT ) + hb_vmUnlock(); + fResult = !_hb_thread_cond_wait( &s_thread_cond, &s_thread_mtx, ulMilliSec ); + hb_vmLock(); +# else + HB_CRITICAL_UNLOCK( s_thread_mtx ); hb_vmUnlock(); fResult = HB_COND_TIMEDWAIT( s_thread_cond, ulMilliSec ); @@ -933,6 +1058,7 @@ static int hb_threadWait( PHB_THREADSTATE * pThreads, int iThreads, HB_CRITICAL_LOCK( s_thread_mtx ); if( !fResult ) s_waiting_for_threads--; +# endif if( timer ) { HB_ULONG curr = hb_dateMilliSeconds(); @@ -1257,12 +1383,14 @@ static void hb_mutexListLock( PHB_MTXLST pList ) pMutex->lockers++; #if defined( HB_PTHREAD_API ) pthread_cond_wait( &pMutex->cond_l, &pMutex->mutex ); +#elif defined( HB_COND_HARBOUR_SUPPORT ) + _hb_thread_cond_wait( &pMutex->cond_l, &pMutex->mutex, HB_THREAD_INFINITE_WAIT ); #else HB_CRITICAL_UNLOCK( pMutex->mutex ); ( void ) HB_COND_WAIT( pMutex->cond_l ); HB_CRITICAL_LOCK( pMutex->mutex ); - pMutex->lockers--; #endif + pMutex->lockers--; } pMutex->lock_count = pList->lock_count; pMutex->owner = HB_THREAD_SELF(); @@ -1302,8 +1430,10 @@ static HB_GARBAGE_FUNC( hb_mutexDestructor ) /* nothing */ #else HB_CRITICAL_DESTROY( pMutex->mutex ); +# if !defined( HB_COND_HARBOUR_SUPPORT ) HB_COND_DESTROY( pMutex->cond_l ); HB_COND_DESTROY( pMutex->cond_w ); +# endif #endif } @@ -1337,8 +1467,10 @@ PHB_ITEM hb_threadMutexCreate( BOOL fSync ) /* nothing */ #else HB_CRITICAL_INIT( pMutex->mutex ); +# if !defined( HB_COND_HARBOUR_SUPPORT ) HB_COND_INIT( pMutex->cond_l ); HB_COND_INIT( pMutex->cond_w ); +# endif #endif pMutex->fSync = fSync; @@ -1410,13 +1542,15 @@ BOOL hb_threadMutexLock( PHB_ITEM pItem ) while( pMutex->lock_count != 0 ) { pMutex->lockers++; -#if defined( HB_PTHREAD_API ) +# if defined( HB_PTHREAD_API ) pthread_cond_wait( &pMutex->cond_l, &pMutex->mutex ); -#else +# elif defined( HB_COND_HARBOUR_SUPPORT ) + _hb_thread_cond_wait( &pMutex->cond_l, &pMutex->mutex, HB_THREAD_INFINITE_WAIT ); +# else HB_CRITICAL_UNLOCK( pMutex->mutex ); ( void ) HB_COND_WAIT( pMutex->cond_l ); HB_CRITICAL_LOCK( pMutex->mutex ); -#endif +# endif pMutex->lockers--; } pMutex->lock_count = 1; @@ -1455,7 +1589,7 @@ BOOL hb_threadMutexTimedLock( PHB_ITEM pItem, ULONG ulMilliSec ) HB_CRITICAL_LOCK( pMutex->mutex ); if( ulMilliSec && pMutex->lock_count != 0 ) { -#if defined( HB_PTHREAD_API ) +# if defined( HB_PTHREAD_API ) struct timespec ts; hb_threadTimeInit( &ts, ulMilliSec ); @@ -1472,17 +1606,23 @@ BOOL hb_threadMutexTimedLock( PHB_ITEM pItem, ULONG ulMilliSec ) } while( pMutex->lock_count != 0 ); pMutex->lockers--; -#else +# 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 ) + pMutex->lockers++; + _hb_thread_cond_wait( &pMutex->cond_l, &pMutex->mutex, ulMilliSec ); + pMutex->lockers--; +# else pMutex->lockers++; HB_CRITICAL_UNLOCK( pMutex->mutex ); ( void ) HB_COND_TIMEDWAIT( pMutex->cond_l, ulMilliSec ); HB_CRITICAL_LOCK( pMutex->mutex ); pMutex->lockers--; -#endif +# endif +# endif } if( pMutex->lock_count == 0 ) { @@ -1657,12 +1797,14 @@ PHB_ITEM hb_threadMutexSubscribe( PHB_ITEM pItem, BOOL fClear ) 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 ); - pMutex->waiters--; # endif + pMutex->waiters--; } if( pMutex->events && hb_arrayLen( pMutex->events ) > 0 ) @@ -1681,13 +1823,15 @@ PHB_ITEM hb_threadMutexSubscribe( PHB_ITEM pItem, BOOL fClear ) pMutex->lockers++; while( pMutex->lock_count != 0 ) { -#if defined( HB_PTHREAD_API ) +# if defined( HB_PTHREAD_API ) pthread_cond_wait( &pMutex->cond_l, &pMutex->mutex ); -#else +# elif defined( HB_COND_HARBOUR_SUPPORT ) + _hb_thread_cond_wait( &pMutex->cond_l, &pMutex->mutex, HB_THREAD_INFINITE_WAIT ); +# else HB_CRITICAL_UNLOCK( pMutex->mutex ); ( void ) HB_COND_WAIT( pMutex->cond_l ); HB_CRITICAL_LOCK( pMutex->mutex ); -#endif +# endif } pMutex->lockers--; } @@ -1773,9 +1917,13 @@ PHB_ITEM hb_threadMutexTimedSubscribe( PHB_ITEM pItem, ULONG ulMilliSec, BOOL fC * 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--; @@ -1797,13 +1945,15 @@ PHB_ITEM hb_threadMutexTimedSubscribe( PHB_ITEM pItem, ULONG ulMilliSec, BOOL fC pMutex->lockers++; while( pMutex->lock_count != 0 ) { -#if defined( HB_PTHREAD_API ) +# if defined( HB_PTHREAD_API ) pthread_cond_wait( &pMutex->cond_l, &pMutex->mutex ); -#else +# elif defined( HB_COND_HARBOUR_SUPPORT ) + _hb_thread_cond_wait( &pMutex->cond_l, &pMutex->mutex, HB_THREAD_INFINITE_WAIT ); +# else HB_CRITICAL_UNLOCK( pMutex->mutex ); ( void ) HB_COND_WAIT( pMutex->cond_l ); HB_CRITICAL_LOCK( pMutex->mutex ); -#endif +# endif } pMutex->lockers--; }