Files
harbour-core/harbour/source/vm/thread.c
Viktor Szakats 454b3b49e0 2008-09-23 14:38 UTC+0200 Viktor Szakats (harbour.01 syenar hu)
* contrib/hbole/ole2.c
  * contrib/hbgf/hbgfwin/winapi.c
  * contrib/hbgf/hbgfos2/os2pm.c
  * contrib/hbwin/win_ole.c
  * contrib/hbwhat/wincorec.c
  * source/rtl/do.c
  * source/rdd/workarea.c
    % hb_vmPushSymbol( hb_dynsymSymbol( p ) ) -> hb_vmPushDynSym( p )

  * contrib/hbwhat/wincorec.c
    ! WHT__GETDLGPROC() fixed.
    ! hb_vmPushSymbol( hb_itemGetSymbol( p ) ) -> hb_vmPushDynSym( p )

  * contrib/hbgf/hbgfwin/winapi.c
  * contrib/hbwhat/wincorec.c
  * contrib/hbwhat/whtmsg.c
  * contrib/hbwhat/whtbmp.c
  * contrib/hbwhat/whtcomm.c
  * contrib/hbwhat/whthead.c
  * contrib/hbwhat/whthead.c
  * contrib/hbwhat/whtkbrd.c
  * contrib/hbwhat/whtlv.c
  * contrib/hbwhat/whttext.c
  * contrib/hbwhat/whtwnd.c
  * contrib/hbwhat/whtdlg.c
  * contrib/hbwhat/whttab.c
    ! Fixed few LPARAM/WPARAM passings to be 64-bit compatible.
      (hb_parnl()/hb_parni() -> hb_parnint())

  * source/vm/thread.c
  * source/rtl/do.c
  * source/rdd/usrrdd/usrrdd.c
  * contrib/hbwhat/whtlv.c
  * contrib/hbwhat/whtfont.c
    % hb_vmPushSymbol( hb_itemGetSymbol( p ) ) -> hb_vmPush()
    ; QUESTION/TOFIX: In whtlv.c and whtfont.c this may be wrong, 
                      and hb_vmPushDynSym() could be needed instead.
                      Can someone take a look pls?
2008-09-23 12:48:23 +00:00

1350 lines
33 KiB
C

/*
* $Id$
*/
/*
* Harbour Project source code:
* MT mode functions
*
* Copyright 2008 Przemyslaw Czerpak <druzus / at / priv.onet.pl>
* www - http://www.harbour-project.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307 USA (or visit the web site http://www.gnu.org/).
*
* As a special exception, the Harbour Project gives permission for
* additional uses of the text contained in its release of Harbour.
*
* The exception is that, if you link the Harbour libraries with other
* files to produce an executable, this does not by itself cause the
* resulting executable to be covered by the GNU General Public License.
* Your use of that executable is in no way restricted on account of
* linking the Harbour library code into it.
*
* This exception does not however invalidate any other reasons why
* the executable file might be covered by the GNU General Public License.
*
* This exception applies only to the code released by the Harbour
* Project under the name Harbour. If you copy code from other
* Harbour Project or Free Software Foundation releases into a copy of
* Harbour, as the General Public License permits, the exception does
* not apply to the code that you add in this way. To avoid misleading
* anyone as to the status of such modified files, you must delete
* this exception notice from them.
*
* If you write modifications of your own for Harbour, it is your choice
* whether to permit this exception to apply to your modifications.
* If you do not wish that, delete this exception notice.
*
*/
#define HB_OS_WIN_32_USED
#define INCL_DOSSEMAPHORES
#define INCL_DOSPROCESS
#define _HB_THREAD_INTERNAL_
#include "hbvmopt.h"
#include "hbthread.h"
#include "hbapiitm.h"
#include "hbapierr.h"
#include "hbapicdp.h"
#include "hbapilng.h"
#include "hbvm.h"
#include "hbstack.h"
#include "hbmemvar.ch"
#include "hbthread.ch"
#if defined( HB_PTHREAD_API )
# include <time.h>
# include <sys/time.h>
#endif
static volatile BOOL s_fThreadInit = FALSE;
#if !defined( HB_MT_VM )
/* nothing */
#else
# if defined( HB_PTHREAD_API )
struct timespec ts;
static void hb_threadTimeInit( struct timespec * ts, ULONG ulMilliSec )
{
# if _POSIX_C_SOURCE >= 199309L
clock_gettime( CLOCK_REALTIME, ts );
# else
struct timeval tv;
gettimeofday( &tv, NULL );
ts->tv_sec = tv.tv_sec;
ts->tv_nsec = tv.tv_usec * 1000l;
# endif
ts->tv_nsec += ( ulMilliSec % 1000 ) * 1000000l;
ts->tv_sec += ulMilliSec / 1000 + ts->tv_nsec / 1000000000l;
ts->tv_nsec %= 1000000000l;
}
# endif
# if defined( HB_CRITICAL_INIT )
static HB_RAWCRITICAL_T s_critical_init;
static void hb_threadCriticalInit( HB_CRITICAL_T * critical )
{
if( !s_fThreadInit )
hb_threadInit();
HB_CRITICAL_LOCK( s_critical_init );
if( !critical->fInit )
{
HB_CRITICAL_INIT( critical->critical );
critical->fInit = TRUE;
}
HB_CRITICAL_UNLOCK( s_critical_init );
}
# else
static HB_CRITICAL_NEW( s_critical_init );
# endif
# if defined( HB_COND_INIT )
static void hb_threadCondInit( HB_COND_T * cond )
{
if( !s_fThreadInit )
hb_threadInit();
HB_CRITICAL_LOCK( s_critical_init );
if( !cond->fInit )
{
HB_COND_INIT( cond->cond );
# if defined( HB_CRITICAL_INIT )
HB_CRITICAL_INIT( cond->critical );
# endif
cond->waiters = 0;
cond->fInit = TRUE;
}
HB_CRITICAL_UNLOCK( s_critical_init );
}
# endif
#endif /* HB_MT_VM */
void hb_threadInit( void )
{
if( !s_fThreadInit )
{
#if !defined( HB_MT_VM )
/* nothing to do */
#elif defined( HB_CRITICAL_INIT )
HB_CRITICAL_INIT( s_critical_init );
#endif
s_fThreadInit = TRUE;
}
}
void hb_threadExit( void )
{
if( s_fThreadInit )
{
s_fThreadInit = FALSE;
#if !defined( HB_MT_VM )
/* nothing to do */
#elif defined( HB_CRITICAL_DESTROY )
HB_CRITICAL_DESTROY( s_critical_init );
#endif
}
}
void hb_threadEnterCriticalSection( HB_CRITICAL_T * critical )
{
#if !defined( HB_MT_VM )
HB_SYMBOL_UNUSED( critical );
#elif defined( HB_CRITICAL_INIT )
if( !critical->fInit )
hb_threadCriticalInit( critical );
HB_CRITICAL_LOCK( critical->critical );
#else
HB_CRITICAL_LOCK( *critical );
#endif
}
void hb_threadLeaveCriticalSection( HB_CRITICAL_T * critical )
{
#if !defined( HB_MT_VM )
HB_SYMBOL_UNUSED( critical );
#elif defined( HB_CRITICAL_INIT )
HB_CRITICAL_UNLOCK( critical->critical );
#else
HB_CRITICAL_UNLOCK( *critical );
#endif
}
BOOL hb_threadCondSignal( HB_COND_T * cond )
{
#if !defined( HB_MT_VM )
HB_SYMBOL_UNUSED( cond );
return FALSE;
#elif defined( HB_PTHREAD_API )
# if defined( HB_COND_INIT )
if( !cond->fInit )
hb_threadCondInit( cond );
# endif
return pthread_cond_signal( HB_COND_GET( cond ) ) == 0;
#else
if( !cond->fInit )
hb_threadCondInit( cond );
HB_CRITICAL_LOCK( cond->critical );
if( cond->waiters )
{
HB_COND_SIGNAL( cond->cond );
cond->waiters--;
}
HB_CRITICAL_UNLOCK( cond->critical );
return TRUE;
#endif
}
BOOL hb_threadCondBroadcast( HB_COND_T * cond )
{
#if !defined( HB_MT_VM )
HB_SYMBOL_UNUSED( cond );
return FALSE;
#elif defined( HB_PTHREAD_API )
# if defined( HB_COND_INIT )
if( !cond->fInit )
hb_threadCondInit( cond );
# endif
return pthread_cond_broadcast( HB_COND_GET( cond ) ) == 0;
#else
if( !cond->fInit )
hb_threadCondInit( cond );
HB_CRITICAL_LOCK( cond->critical );
if( cond->waiters )
{
HB_COND_SIGNALN( cond->cond, cond->waiters );
cond->waiters = 0;
}
HB_CRITICAL_UNLOCK( cond->critical );
return TRUE;
#endif
}
BOOL hb_threadCondWait( HB_COND_T * cond, HB_CRITICAL_T * mutex )
{
#if !defined( HB_MT_VM )
HB_SYMBOL_UNUSED( cond );
HB_SYMBOL_UNUSED( mutex );
return FALSE;
#elif defined( HB_PTHREAD_API )
# if defined( HB_COND_INIT )
if( !cond->fInit )
hb_threadCondInit( cond );
# endif
return pthread_cond_wait( HB_COND_GET( cond ), HB_CRITICAL_GET( mutex ) ) == 0;
#else
BOOL fResult;
if( !cond->fInit )
hb_threadCondInit( cond );
/* mutex should be already locked so it's not necessary
* to make initialization test here
*/
HB_CRITICAL_LOCK( cond->critical );
cond->waiters++;
HB_CRITICAL_UNLOCK( cond->critical );
HB_CRITICAL_UNLOCK( mutex->critical );
fResult = HB_COND_WAIT( cond->cond );
HB_CRITICAL_LOCK( mutex->critical );
return fResult;
#endif
}
BOOL hb_threadCondTimedWait( HB_COND_T * cond, HB_CRITICAL_T * mutex, ULONG ulMilliSec )
{
#if !defined( HB_MT_VM )
HB_SYMBOL_UNUSED( cond );
HB_SYMBOL_UNUSED( mutex );
HB_SYMBOL_UNUSED( ulMilliSec );
return FALSE;
#elif defined( HB_PTHREAD_API )
struct timespec ts;
# if defined( HB_COND_INIT )
if( !cond->fInit )
hb_threadCondInit( cond );
# endif
hb_threadTimeInit( &ts, ulMilliSec );
return pthread_cond_timedwait( HB_COND_GET( cond ), HB_CRITICAL_GET( mutex ), &ts ) == 0;
#else
BOOL fResult;
if( !cond->fInit )
hb_threadCondInit( cond );
/* mutex should be already locked so it's not necessary
* to make initialization test here
*/
HB_CRITICAL_LOCK( cond->critical );
cond->waiters++;
HB_CRITICAL_UNLOCK( cond->critical );
HB_CRITICAL_UNLOCK( mutex->critical );
fResult = HB_COND_TIMEDWAIT( cond->cond, ulMilliSec );
HB_CRITICAL_LOCK( mutex->critical );
return fResult;
#endif
}
HB_THREAD_T hb_threadCreate( PHB_THREAD_STARTFUNC start_func, void * Cargo )
{
HB_THREAD_T th_id;
#if !defined( HB_MT_VM )
HB_SYMBOL_UNUSED( start_func );
HB_SYMBOL_UNUSED( Cargo );
th_id = 0;
#elif defined( HB_PTHREAD_API )
if( pthread_create( &th_id, NULL, start_func, Cargo ) != 0 )
th_id = 0;
#elif defined( HB_OS_WIN_32 )
th_id = ( HANDLE ) _beginthreadex( NULL, 0, start_func, Cargo, 0, NULL );
#elif defined( HB_OS_OS2 )
th_id = _beginthread( ( void * ) start_func, NULL, 128 * 1024, Cargo );
#else
{ int TODO_MT; }
th_id = 0;
#endif
return th_id;
}
BOOL hb_threadJoin( HB_THREAD_T th_id )
{
#if !defined( HB_MT_VM )
HB_SYMBOL_UNUSED( th_id );
return FALSE;
#elif defined( HB_PTHREAD_API )
return pthread_join( th_id, NULL ) == 0;
#elif defined( HB_OS_WIN_32 )
if( WaitForSingleObject( th_id, INFINITE ) != WAIT_FAILED )
{
CloseHandle( th_id );
return TRUE;
}
return FALSE;
#elif defined( HB_OS_OS2 )
APIRET rc = DosWaitThread( &th_id, DCWW_WAIT );
/* TOFIX: ERROR_INVALID_THREADID is a hack for failing DosWaitThread()
* when thread terminates before DosWaitThread() call.
* OS2 users please check and fix this code if possible.
*/
return rc == NO_ERROR || rc == ERROR_INVALID_THREADID;
#else
{ int TODO_MT; }
return FALSE;
#endif
}
BOOL hb_threadDetach( HB_THREAD_T th_id )
{
#if !defined( HB_MT_VM )
HB_SYMBOL_UNUSED( th_id );
return FALSE;
#elif defined( HB_PTHREAD_API )
return pthread_detach( th_id ) == 0;
#elif defined( HB_OS_WIN_32 )
return CloseHandle( th_id ) != 0;
#elif defined( HB_OS_OS2 )
APIRET rc = DosWaitThread( &th_id, DCWW_NOWAIT );
return rc == NO_ERROR || rc == ERROR_INVALID_THREADID;
#else
{ int TODO_MT; }
return FALSE;
#endif
}
/*
* .PRG level functions
*/
/* I. THREADS */
static HB_GARBAGE_FUNC( hb_threadDestructor )
{
PHB_THREADSTATE pThread = ( PHB_THREADSTATE ) Cargo;
if( pThread->pParams )
{
hb_itemRelease( pThread->pParams );
pThread->pParams = NULL;
}
if( pThread->pMemvars )
{
hb_itemRelease( pThread->pMemvars );
pThread->pMemvars = NULL;
}
if( pThread->pResult )
{
hb_itemRelease( pThread->pResult );
pThread->pResult = NULL;
}
if( pThread->th_id != 0 )
{
hb_threadDetach( pThread->th_id );
pThread->th_id = 0;
}
}
static HB_THREAD_STARTFUNC( hb_threadStartVM )
{
#if defined( HB_MT_VM )
PHB_ITEM pThItm = ( PHB_ITEM ) Cargo;
ULONG ulPCount, ulParam;
PHB_THREADSTATE pThread;
pThread = ( PHB_THREADSTATE ) hb_itemGetPtrGC( pThItm, hb_threadDestructor );
pThread->pThItm = pThItm;
hb_vmThreadInit( ( void * ) pThread );
ulPCount = hb_arrayLen( pThread->pParams );
if( ulPCount > 0 )
{
PHB_ITEM pStart = hb_arrayGetItemPtr( pThread->pParams, 1 );
if( HB_IS_BLOCK( pStart ) )
{
hb_vmPushSymbol( &hb_symEval );
hb_vmPush( pStart );
}
else if( HB_IS_SYMBOL( pStart ) )
{
hb_vmPush( pStart );
hb_vmPushNil();
}
else if( HB_IS_STRING( pStart ) )
{
hb_vmPushDynSym( hb_dynsymGet( hb_itemGetCPtr( pStart ) ) );
hb_vmPushNil();
}
else
ulPCount = 0;
}
if( ulPCount > 0 )
{
for( ulParam = 2; ulParam <= ulPCount; ++ulParam )
hb_vmPush( hb_arrayGetItemPtr( pThread->pParams, ulParam ) );
hb_itemRelease( pThread->pParams );
pThread->pParams = NULL;
hb_vmDo( ( USHORT ) ( ulPCount - 1 ) );
}
else
{
hb_itemRelease( pThread->pParams );
pThread->pParams = NULL;
if( pThread->pMemvars )
{
hb_itemRelease( pThread->pMemvars );
pThread->pMemvars = NULL;
}
hb_errRT_BASE_SubstR( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, 0 );
}
/* hb_vmThreadQuit() unlocks and release HVM stack and may release
* also pThItm item so we should not access any HVM items or
* pThread structure after this function.
*/
hb_vmThreadQuit();
HB_THREAD_END
#else
hb_itemRelease( ( PHB_ITEM ) Cargo );
HB_THREAD_RAWEND
#endif
}
static PHB_THREADSTATE hb_thParam( int iParam )
{
PHB_THREADSTATE pThread = ( PHB_THREADSTATE ) hb_parptrGC( hb_threadDestructor, iParam );
if( pThread )
return pThread;
hb_errRT_BASE_SubstR( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
return NULL;
}
HB_FUNC( HB_THREADSTART )
{
ULONG ulAttr = 0, ulStart = 1;
const char * szFuncName = NULL;
PHB_SYMB pSymbol = NULL;
PHB_ITEM pStart;
pStart = hb_param( ulStart, HB_IT_ANY );
while( pStart && HB_IS_NUMERIC( pStart ) )
{
ulAttr |= ( ULONG ) hb_itemGetNL( pStart );
pStart = hb_param( ++ulStart, HB_IT_ANY );
}
if( pStart )
{
if( HB_IS_STRING( pStart ) )
{
PHB_DYNS pDynSym;
szFuncName = hb_itemGetCPtr( pStart );
pDynSym = hb_dynsymFindName( szFuncName );
if( pDynSym )
pSymbol = pDynSym->pSymbol;
if( !pSymbol || !pSymbol->value.pFunPtr )
pStart = NULL;
}
else if( HB_IS_SYMBOL( pStart ) )
{
pSymbol = hb_itemGetSymbol( pStart );
if( !pSymbol->value.pFunPtr )
{
szFuncName = pSymbol->szName;
pStart = NULL;
}
}
else if( !HB_IS_BLOCK( pStart ) )
pStart = NULL;
}
if( pStart )
{
PHB_ITEM pReturn;
PHB_THREADSTATE pThread;
ULONG ulPCount, ulParam;
pReturn = hb_itemNew( NULL );
pThread = ( PHB_THREADSTATE )
hb_gcAlloc( sizeof( HB_THREADSTATE ), hb_threadDestructor );
memset( pThread, 0, sizeof( HB_THREADSTATE ) );
hb_itemPutPtrGC( pReturn, pThread );
pThread->pszCDP = hb_cdpID();
pThread->pszLang = hb_langID();
pThread->pszDefRDD = hb_stackRDD()->szDefaultRDD;
pThread->pSet = hb_setClone( hb_stackSetStruct() );
pThread->pParams = hb_arrayBaseParams();
ulPCount = hb_arrayLen( pThread->pParams );
/* remove thread attributes */
if( ulStart > 1 )
{
for( ulParam = 1; ulParam < ulStart; ++ulParam )
hb_arrayDel( pThread->pParams, 1 );
ulPCount -= ulStart - 1;
hb_arraySize( pThread->pParams, ulPCount );
}
if( HB_IS_STRING( pStart ) && pSymbol )
hb_itemPutSymbol( hb_arrayGetItemPtr( pThread->pParams, 1 ), pSymbol );
/* detach LOCAL variables passed by reference */
for( ulParam = 1; ulParam <= ulPCount; ++ulParam )
{
PHB_ITEM pParam = hb_arrayGetItemPtr( pThread->pParams, ulParam );
if( HB_IS_BYREF( pParam ) )
{
if( ulParam == 1 )
hb_itemCopy( pParam, hb_itemUnRef( pParam ) );
else
hb_memvarDetachLocal( pParam );
}
}
if( ( ulAttr & HB_THREAD_INHERIT_MEMVARS ) != 0 )
{
int iScope = 0;
if( ( ulAttr & HB_THREAD_INHERIT_PUBLIC ) != 0 )
iScope |= HB_MV_PUBLIC;
if( ( ulAttr & HB_THREAD_INHERIT_PRIVATE ) != 0 )
iScope |= HB_MV_PRIVATE;
pThread->pMemvars = hb_memvarSaveInArray( iScope,
( ulAttr & HB_THREAD_MEMVARS_COPY ) != 0 );
}
/* make copy of thread pointer item before we pass it to new thread
* to avoid race condition
*/
hb_itemReturn( pReturn );
pThread->th_id = hb_threadCreate( hb_threadStartVM, ( void * ) pReturn );
if( pThread->th_id == 0 )
{
if( pThread->pMemvars )
hb_itemRelease( pThread->pMemvars );
hb_setRelease( pThread->pSet );
hb_xfree( pThread->pSet );
hb_itemRelease( pReturn );
hb_ret();
}
}
else
{
if( szFuncName )
hb_errRT_BASE_SubstR( EG_NOFUNC, 1001, NULL, szFuncName, 0 );
else
hb_errRT_BASE_SubstR( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
}
}
HB_FUNC( HB_THREADSELF )
{
#if defined( HB_MT_VM )
PHB_THREADSTATE pThread = ( PHB_THREADSTATE ) hb_vmThreadState();
if( pThread )
hb_itemReturn( pThread->pThItm );
#endif
}
HB_FUNC( HB_THREADJOIN )
{
PHB_THREADSTATE pThread = hb_thParam( 1 );
if( pThread )
{
BOOL fResult = FALSE;
if( pThread->th_id )
{
hb_vmUnlock();
fResult = hb_threadJoin( pThread->th_id );
if( fResult )
pThread->th_id = 0;
hb_vmLock();
}
if( fResult )
{
if( pThread->pResult )
hb_itemParamStore( 2, pThread->pResult );
}
hb_retl( fResult );
}
}
HB_FUNC( HB_THREADDETACH )
{
PHB_THREADSTATE pThread = hb_thParam( 1 );
if( pThread )
{
BOOL fResult = FALSE;
if( pThread->th_id && hb_threadDetach( pThread->th_id ) )
{
pThread->th_id = 0;
fResult = TRUE;
}
hb_retl( fResult );
}
}
HB_FUNC( HB_THREADQUITREQUEST )
{
PHB_THREADSTATE pThread = hb_thParam( 1 );
if( pThread )
{
BOOL fResult = FALSE;
#if defined( HB_MT_VM )
if( pThread->fActive )
{
hb_vmThreadQuitRequest( ( void * ) pThread );
fResult = TRUE;
}
#endif
hb_retl( fResult );
}
}
HB_FUNC( HB_THREADWAITFORALL )
{
#if defined( HB_MT_VM )
hb_vmWaitForThreads();
#endif
}
HB_FUNC( HB_THREADTERMINATEALL )
{
#if defined( HB_MT_VM )
hb_vmTerminateThreads();
#endif
}
/* II. MUTEXES */
typedef struct _HB_MUTEX
{
int lock_count;
int lockers;
int waiters;
PHB_ITEM events;
HB_THREAD_ID owner;
HB_RAWCRITICAL_T mutex;
HB_RAWCOND_T cond;
BOOL fSync;
struct _HB_MUTEX * pNext;
struct _HB_MUTEX * pPrev;
}
HB_MUTEX, * PHB_MUTEX;
typedef struct _HB_MTXLST
{
int lock_count;
PHB_MUTEX pMutex;
struct _HB_MTXLST * pNext;
}
HB_MTXLST, * PHB_MTXLST;
static PHB_MUTEX s_pSyncList = NULL;
static PHB_MUTEX s_pMutexList = NULL;
static void hb_mutexLink( PHB_MUTEX *pList, PHB_MUTEX pItem )
{
if( *pList )
{
pItem->pNext = *pList;
pItem->pPrev = (*pList)->pPrev;
pItem->pPrev->pNext = pItem;
(*pList)->pPrev = pItem;
}
else
{
*pList = pItem->pNext = pItem->pPrev = pItem;
}
}
static void hb_mutexUnlink( PHB_MUTEX *pList, PHB_MUTEX pItem )
{
pItem->pPrev->pNext = pItem->pNext;
pItem->pNext->pPrev = pItem->pPrev;
if( *pList == pItem )
{
*pList = pItem->pNext;
if( *pList == pItem )
*pList = NULL; /* this was the last block */
}
}
#if defined( HB_MT_VM )
static void hb_mutexUnlockList( PHB_MUTEX * pList, PHB_MTXLST * pStore )
{
HB_CRITICAL_LOCK( s_critical_init );
if( *pList )
{
PHB_MUTEX pMutex = *pList;
do
{
if( HB_THREAD_EQUAL( pMutex->owner, HB_THREAD_SELF() ) )
{
HB_CRITICAL_LOCK( pMutex->mutex );
if( HB_THREAD_EQUAL( pMutex->owner, HB_THREAD_SELF() ) )
{
if( pStore )
{
*pStore = ( PHB_MTXLST ) hb_xgrab( sizeof( HB_MTXLST ) );
(*pStore)->lock_count = pMutex->lock_count;
(*pStore)->pMutex = pMutex;
pStore = &(*pStore)->pNext;
*pStore = NULL;
}
pMutex->lock_count = 0;
pMutex->owner = ( HB_THREAD_ID ) 0;
HB_COND_SIGNALN( pMutex->cond, pMutex->lockers );
}
HB_CRITICAL_UNLOCK( pMutex->mutex );
}
pMutex = pMutex->pNext;
}
while( pMutex != *pList );
}
HB_CRITICAL_UNLOCK( s_critical_init );
}
static void hb_mutexLockList( PHB_MTXLST pList )
{
while( pList )
{
PHB_MUTEX pMutex = pList->pMutex;
HB_CRITICAL_LOCK( pMutex->mutex );
pMutex->lockers++;
while( pMutex->lock_count != 0 )
{
#if defined( HB_PTHREAD_API )
pthread_cond_wait( &pMutex->cond, &pMutex->mutex );
#else
HB_CRITICAL_UNLOCK( pMutex->mutex );
( void ) HB_COND_WAIT( pMutex->cond );
HB_CRITICAL_LOCK( pMutex->mutex );
#endif
}
pMutex->lockers--;
pMutex->lock_count = pList->lock_count;
pMutex->owner = HB_THREAD_SELF();
HB_CRITICAL_UNLOCK( pMutex->mutex );
{
PHB_MTXLST pFree = pList;
pList = pList->pNext;
hb_xfree( pFree );
}
}
}
void hb_threadMutexUnlockAll( void )
{
hb_mutexUnlockList( &s_pMutexList, NULL );
hb_mutexUnlockList( &s_pSyncList, NULL );
}
#endif
static HB_GARBAGE_FUNC( hb_mutexDestructor )
{
PHB_MUTEX pMutex = ( PHB_MUTEX ) Cargo;
#if defined( HB_MT_VM )
HB_CRITICAL_LOCK( s_critical_init );
hb_mutexUnlink( pMutex->fSync ? &s_pSyncList : &s_pMutexList, pMutex );
HB_CRITICAL_UNLOCK( s_critical_init );
#else
hb_mutexUnlink( pMutex->fSync ? &s_pSyncList : &s_pMutexList, pMutex );
#endif
if( pMutex->events )
hb_itemRelease( pMutex->events );
#if !defined( HB_MT_VM )
/* nothing */
#elif defined( HB_PTHREAD_API )
pthread_mutex_destroy( &pMutex->mutex );
pthread_cond_destroy( &pMutex->cond );
#else
HB_CRITICAL_DESTROY( pMutex->mutex );
HB_COND_DESTROY( pMutex->cond );
#endif
}
static PHB_MUTEX hb_mutexPtr( PHB_ITEM pItem )
{
return ( PHB_MUTEX ) hb_itemGetPtrGC( pItem, hb_mutexDestructor );
}
static PHB_ITEM hb_mutexParam( int iParam )
{
PHB_ITEM pItem = hb_param( iParam, HB_IT_POINTER );
if( hb_itemGetPtrGC( pItem, hb_mutexDestructor ) )
return pItem;
hb_errRT_BASE_SubstR( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
return NULL;
}
PHB_ITEM hb_threadMutexCreate( BOOL fSync )
{
PHB_MUTEX pMutex;
PHB_ITEM pItem;
pItem = hb_itemNew( NULL );
pMutex = ( PHB_MUTEX ) hb_gcAlloc( sizeof( HB_MUTEX ), hb_mutexDestructor );
memset( pMutex, 0, sizeof( HB_MUTEX ) );
pItem = hb_itemPutPtrGC( pItem, pMutex );
#if !defined( HB_MT_VM )
/* nothing */
#elif defined( HB_PTHREAD_API )
pthread_mutex_init( &pMutex->mutex, NULL );
pthread_cond_init( &pMutex->cond, NULL );
#else
HB_CRITICAL_INIT( pMutex->mutex );
HB_COND_INIT( pMutex->cond );
#endif
pMutex->fSync = fSync;
#if defined( HB_MT_VM )
HB_CRITICAL_LOCK( s_critical_init );
hb_mutexLink( fSync ? &s_pSyncList : &s_pMutexList, pMutex );
HB_CRITICAL_UNLOCK( s_critical_init );
#else
hb_mutexLink( fSync ? &s_pSyncList : &s_pMutexList, pMutex );
#endif
return pItem;
}
BOOL hb_threadMutexLock( PHB_ITEM pItem )
{
PHB_MUTEX pMutex = hb_mutexPtr( pItem );
BOOL fResult = FALSE;
if( pMutex )
{
if( HB_THREAD_EQUAL( pMutex->owner, HB_THREAD_SELF() ) )
pMutex->lock_count++;
else
{
hb_vmUnlock();
#if !defined( HB_MT_VM )
pMutex->lock_count = 1;
pMutex->owner = HB_THREAD_SELF();
fResult = TRUE;
#else
HB_CRITICAL_LOCK( pMutex->mutex );
pMutex->lockers++;
while( pMutex->lock_count != 0 )
{
#if defined( HB_PTHREAD_API )
pthread_cond_wait( &pMutex->cond, &pMutex->mutex );
#else
HB_CRITICAL_UNLOCK( pMutex->mutex );
( void ) HB_COND_WAIT( pMutex->cond );
HB_CRITICAL_LOCK( pMutex->mutex );
#endif
}
pMutex->lockers--;
pMutex->lock_count = 1;
pMutex->owner = HB_THREAD_SELF();
HB_CRITICAL_UNLOCK( pMutex->mutex );
fResult = TRUE;
#endif
hb_vmLock();
}
}
return fResult;
}
BOOL hb_threadMutexTimedLock( PHB_ITEM pItem, ULONG ulMilliSec )
{
PHB_MUTEX pMutex = hb_mutexPtr( pItem );
BOOL fResult = FALSE;
if( pMutex )
{
if( HB_THREAD_EQUAL( pMutex->owner, HB_THREAD_SELF() ) )
pMutex->lock_count++;
else
{
hb_vmUnlock();
#if !defined( HB_MT_VM )
HB_SYMBOL_UNUSED( ulMilliSec );
pMutex->lock_count = 1;
pMutex->owner = HB_THREAD_SELF();
fResult = TRUE;
#elif defined( HB_PTHREAD_API )
pthread_mutex_lock( &pMutex->mutex );
if( ulMilliSec && pMutex->lock_count != 0 )
{
struct timespec ts;
hb_threadTimeInit( &ts, ulMilliSec );
/* pthread_cond_signal() wakes up at least one thread
* but it's not guaranteed it's exactly one thread so
* we should use while look here.
*/
pMutex->lockers++;
while( pMutex->lock_count == 0 )
{
if( pthread_cond_timedwait( &pMutex->cond, &pMutex->mutex, &ts ) != 0 )
break;
}
pMutex->lockers--;
}
if( pMutex->lock_count == 0 )
{
pMutex->lock_count = 1;
pMutex->owner = HB_THREAD_SELF();
fResult = TRUE;
}
pthread_mutex_unlock( &pMutex->mutex );
#else
HB_CRITICAL_LOCK( pMutex->mutex );
if( ulMilliSec && pMutex->lock_count != 0 )
{
pMutex->lockers++;
HB_CRITICAL_UNLOCK( pMutex->mutex );
( void ) HB_COND_TIMEDWAIT( pMutex->cond, ulMilliSec );
HB_CRITICAL_LOCK( pMutex->mutex );
pMutex->lockers--;
}
if( pMutex->lock_count == 0 )
{
pMutex->lock_count = 1;
pMutex->owner = HB_THREAD_SELF();
fResult = TRUE;
}
HB_CRITICAL_UNLOCK( pMutex->mutex );
#endif
hb_vmLock();
}
}
return fResult;
}
BOOL hb_threadMutexUnlock( PHB_ITEM pItem )
{
PHB_MUTEX pMutex = hb_mutexPtr( pItem );
BOOL fResult = FALSE;
if( pMutex )
{
HB_CRITICAL_LOCK( pMutex->mutex );
if( HB_THREAD_EQUAL( pMutex->owner, HB_THREAD_SELF() ) )
{
if( --pMutex->lock_count == 0 )
{
pMutex->owner = ( HB_THREAD_ID ) 0;
HB_COND_SIGNALN( pMutex->cond, pMutex->lockers );
}
fResult = TRUE;
}
HB_CRITICAL_UNLOCK( pMutex->mutex );
}
return fResult;
}
void hb_threadMutexNotify( PHB_ITEM pItem, PHB_ITEM pNotifier, BOOL fWaiting )
{
PHB_MUTEX pMutex = hb_mutexPtr( pItem );
if( pMutex )
{
HB_CRITICAL_LOCK( pMutex->mutex );
if( !fWaiting )
{
if( !pMutex->events )
{
pMutex->events = hb_itemArrayNew( 1 );
if( pNotifier && !HB_IS_NIL( pNotifier ) )
hb_arraySet( pMutex->events, 1, pNotifier );
}
else if( pNotifier )
hb_arrayAdd( pMutex->events, pNotifier );
else
hb_arraySize( pMutex->events, hb_arrayLen( pMutex->events ) + 1 );
HB_COND_SIGNAL( pMutex->cond );
}
else if( pMutex->waiters )
{
int iCount = pMutex->waiters;
ULONG ulLen;
if( pMutex->events )
{
ulLen = hb_arrayLen( pMutex->events );
hb_arraySize( pMutex->events, ulLen + pMutex->waiters );
}
else
{
ulLen = pMutex->waiters;
pMutex->events = hb_itemArrayNew( ulLen );
}
if( pNotifier && !HB_IS_NIL( pNotifier ) )
{
do
hb_arraySet( pMutex->events, ++ulLen, pNotifier );
while( --iCount );
}
HB_COND_SIGNALN( pMutex->cond, pMutex->waiters );
}
HB_CRITICAL_UNLOCK( pMutex->mutex );
}
}
PHB_ITEM hb_threadMutexSubscribe( PHB_ITEM pItem, BOOL fClear )
{
PHB_MUTEX pMutex = hb_mutexPtr( pItem );
PHB_ITEM pResult = NULL;
if( pMutex )
{
#if !defined( HB_MT_VM )
if( pMutex->events && hb_arrayLen( pMutex->events ) > 0 )
{
if( fClear && pMutex->events )
hb_arraySize( pMutex->events, 0 );
else
{
pResult = hb_itemNew( NULL );
hb_arrayGet( pMutex->events, 1, pResult );
hb_arrayDel( pMutex->events, 1 );
hb_arraySize( pMutex->events, hb_arrayLen( pMutex->events ) - 1 );
}
}
#else
PHB_MTXLST pSyncList = NULL;
hb_mutexUnlockList( &s_pSyncList, &pSyncList );
hb_vmUnlock();
HB_CRITICAL_LOCK( pMutex->mutex );
if( fClear && pMutex->events )
hb_arraySize( pMutex->events, 0 );
pMutex->waiters++;
while( !pMutex->events || hb_arrayLen( pMutex->events ) == 0 )
{
# if defined( HB_PTHREAD_API )
pthread_cond_wait( &pMutex->cond, &pMutex->mutex );
# else
HB_CRITICAL_UNLOCK( pMutex->mutex );
( void ) HB_COND_WAIT( pMutex->cond );
HB_CRITICAL_LOCK( pMutex->mutex );
# endif
}
pMutex->waiters--;
if( pMutex->events && hb_arrayLen( pMutex->events ) > 0 )
{
pResult = hb_itemNew( NULL );
hb_arrayGet( pMutex->events, 1, pResult );
hb_arrayDel( pMutex->events, 1 );
hb_arraySize( pMutex->events, hb_arrayLen( pMutex->events ) - 1 );
}
HB_CRITICAL_UNLOCK( pMutex->mutex );
hb_vmLock();
hb_mutexLockList( pSyncList );
#endif
}
return pResult;
}
PHB_ITEM hb_threadMutexTimedSubscribe( PHB_ITEM pItem, ULONG ulMilliSec, BOOL fClear )
{
PHB_MUTEX pMutex = hb_mutexPtr( pItem );
PHB_ITEM pResult = NULL;
if( pMutex )
{
#if !defined( HB_MT_VM )
HB_SYMBOL_UNUSED( ulMilliSec );
if( pMutex->events && hb_arrayLen( pMutex->events ) > 0 )
{
if( fClear && pMutex->events )
hb_arraySize( pMutex->events, 0 );
else
{
pResult = hb_itemNew( NULL );
hb_arrayGet( pMutex->events, 1, pResult );
hb_arrayDel( pMutex->events, 1 );
hb_arraySize( pMutex->events, hb_arrayLen( pMutex->events ) - 1 );
}
}
#else
PHB_MTXLST pSyncList = NULL;
hb_mutexUnlockList( &s_pSyncList, &pSyncList );
hb_vmUnlock();
HB_CRITICAL_LOCK( pMutex->mutex );
if( fClear && pMutex->events )
hb_arraySize( pMutex->events, 0 );
if( ulMilliSec && !( pMutex->events && hb_arrayLen( pMutex->events ) > 0 ) )
{
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, &pMutex->mutex, &ts ) != 0 )
break;
}
}
# else
HB_CRITICAL_UNLOCK( pMutex->mutex );
( void ) HB_COND_TIMEDWAIT( pMutex->cond, ulMilliSec );
HB_CRITICAL_LOCK( pMutex->mutex );
# endif
pMutex->waiters--;
}
if( pMutex->events && hb_arrayLen( pMutex->events ) > 0 )
{
pResult = hb_itemNew( NULL );
hb_arrayGet( pMutex->events, 1, pResult );
hb_arrayDel( pMutex->events, 1 );
hb_arraySize( pMutex->events, hb_arrayLen( pMutex->events ) - 1 );
}
HB_CRITICAL_UNLOCK( pMutex->mutex );
hb_vmLock();
hb_mutexLockList( pSyncList );
#endif
}
return pResult;
}
HB_FUNC( HB_MUTEXCREATE )
{
hb_itemReturnRelease( hb_threadMutexCreate( FALSE ) );
}
HB_FUNC( HB_MUTEXLOCK )
{
PHB_ITEM pItem = hb_mutexParam( 1 );
if( pItem )
{
if( ISNUM( 2 ) )
{
ULONG ulMilliSec = 0;
double dTimeOut = hb_parnd( 1 );
if( dTimeOut > 0 )
ulMilliSec = ( ULONG ) ( dTimeOut * 1000 );
hb_retl( hb_threadMutexTimedLock( pItem, ulMilliSec ) );
}
else
hb_retl( hb_threadMutexLock( pItem ) );
}
}
HB_FUNC( HB_MUTEXUNLOCK )
{
PHB_ITEM pItem = hb_mutexParam( 1 );
if( pItem )
hb_retl( hb_threadMutexUnlock( pItem ) );
}
HB_FUNC( HB_MUTEXNOTIFY )
{
PHB_ITEM pItem = hb_mutexParam( 1 );
if( pItem )
hb_threadMutexNotify( pItem, hb_param( 2, HB_IT_ANY ), FALSE );
}
HB_FUNC( HB_MUTEXNOTIFYALL )
{
PHB_ITEM pItem = hb_mutexParam( 1 );
if( pItem )
hb_threadMutexNotify( pItem, hb_param( 2, HB_IT_ANY ), TRUE );
}
HB_FUNC( HB_MUTEXSUBSCRIBE )
{
PHB_ITEM pItem = hb_mutexParam( 1 );
if( pItem )
{
PHB_ITEM pResult;
if( ISNUM( 2 ) )
{
ULONG ulMilliSec = 0;
double dTimeOut = hb_parnd( 1 );
if( dTimeOut > 0 )
ulMilliSec = ( ULONG ) ( dTimeOut * 1000 );
pResult = hb_threadMutexTimedSubscribe( pItem, ulMilliSec, FALSE );
}
else
pResult = hb_threadMutexSubscribe( pItem, FALSE );
hb_itemParamStoreForward( 3, pResult);
hb_itemRelease( pResult );
hb_retl( pResult != NULL );
}
}
HB_FUNC( HB_MUTEXSUBSCRIBENOW )
{
PHB_ITEM pItem = hb_mutexParam( 1 );
if( pItem )
{
PHB_ITEM pResult;
if( ISNUM( 2 ) )
{
ULONG ulMilliSec = 0;
double dTimeOut = hb_parnd( 1 );
if( dTimeOut > 0 )
ulMilliSec = ( ULONG ) ( dTimeOut * 1000 );
pResult = hb_threadMutexTimedSubscribe( pItem, ulMilliSec, TRUE );
}
else
pResult = hb_threadMutexSubscribe( pItem, TRUE );
hb_itemParamStoreForward( 3, pResult);
hb_itemRelease( pResult );
hb_retl( pResult != NULL );
}
}
HB_FUNC( HB_MTVM )
{
#if defined( HB_MT_VM )
hb_retl( TRUE );
#else
hb_retl( FALSE );
#endif
}