* harbour/include/hbthread.h
* harbour/source/vm/thread.c
+ added hb_atomic_set(), hb_atomic_get(), hb_atomic_inc() and
hb_atomic_dec() functions which operates on HB_COUNTER or smaller
type if it's necessary for some platforms which can be access/assign
increment/decrement in MT safe atom operations.
hb_atomic_dec() returns true if counter is 0 after decrementation
* harbour/include/hbatomic.h
! fixed compilation in Linux and OpenWatcom
* harbour/include/hbapiitm.h
* harbour/source/rtl/itemseri.c
+ make hb_itemSerialize() and hb_itemDeserialize() public functions
! fixed serialization items with internal item references
* harbour/source/vm/hvm.c
* release memvars after closing RDDs
* harbour/source/debug/dbgentry.c
! fixed buffer overflow reported by Rodrigo
* harbour/source/vm/macro.c
* harbour/source/compiler/hbmain.c
* formatting
* harbour/include/hbexprb.c
! fixed wrongly recognized functions with HB_I18N_ prefix as
HB_I18N_GETTEXT()
* harbour/include/hbapi.h
* harbour/include/hbstack.h
* harbour/include/hbthread.h
* harbour/source/vm/estack.c
* harbour/source/vm/thread.c
* harbour/source/vm/hvm.c
+ added support for I18N in HVM.
Each thread can have it's own i18n set.
When new thread is created then it inherits i18n set from parent
thread and both uses the same set (please remember about it if you
will want to make some direct modifications on active i18n set
internals).
When thread change active i18n set then it effects only this thread
and new threads which will be create later. It does not change i18n
in other existing threads.
+ added functions to set/get pointer to active i18n set in HVM
void * hb_vmI18N( void )
void hb_vmSetI18N( void * )
* harbour/include/hbapi.h
* harbour/source/rtl/hbi18n.c
+ added i18n module. Now only for internal Harbour usage without support
for optional switching to alternative implementations.
I'll add such functionality later when I will work on native gettext
support.
The following public .prg functions has been added:
HB_I18N_GETTEXT[_STRICT]( <cMsgID> [, <cContext> ] )
-> <cTranslatedMsgID> | <cMsgID>
HB_I18N_NGETTEXT[_STRICT]( <nValue>, <cMsgID> | <acMsgID> ;
[, <cContext> ] )
-> <cTranslatedMsgID> | <cMsgID> | <acMsgID>[ <nIndex> ]
This is minimal support necessary for .prg code which has to exists
in each i18n module working with Harbour.
The following functions had been added as public C API:
PHB_ITEM hb_i18n_gettext( PHB_ITEM pMsgID, PHB_ITEM pContext )
PHB_ITEM hb_i18n_ngettext( PHB_ITEM pNum,
PHB_ITEM pMsgID, PHB_ITEM pContext )
The following functions had been added as private HVM C API:
void hb_i18n_init( void )
void hb_i18n_exit( void )
void hb_i18n_release( void * cargo )
void * hb_i18n_alloc( void * cargo )
They have to be supported by alternative i18n modules
The following functions has been added to manage Harbour i18n
translations sets:
HB_I18N_CREATE()
-> <pI18N>
Creates new empty I18N translation set
HB_I18N_CODEPAGE( [<pI18N>,] [<cNewCP>], [<lBase>], [<lTranslate>] )
-> <cOldCP>
Gets or sets Harbour codepage used by translation set
<pI18N> - I18N translation set,
if it's not given then currently active I18N set is used
<cNewCP> - new CP ID. Must be linked with application
<lBase> - when it's .T. then get/set base massages CP instead of
translated massages CP
<lTranslate> - if it's .T. then translate base (<lBase>==.T.) or
final messages in I18N set from previous CP to
given one. Base messages translation in synced
with context ID translation.
HB_I18N_PLURALFORM( [<pI18N>,] [<cNewForm>|<bNewForm>], [<lBase>] )
-> <cOldForm>|<bOldForm>
Gets or sets plural form used for final or base messages
<pI18N> - I18N translation set,
if it's not given then currently active I18N set is used
<cNewForm> - language ID of plural form, f.e.: "EN", "PL", "LT".
Now only three above are supported. Please add rules
for other languages to source/rtl/hbi18n.c.
<bNewForm> - codeblock used to calculate plural form indexes.
can be used instead of character representation but
it's not storred in serialized I18N set
<lBase> - when it's .T. then get/set base massages plural form
instead of translated massages one.
HB_I18N_DESCRIPTION( [<pI18N>,] [<cNewDescription>] )
-> <cOldDescription>
Gets or sets translation set description. After serialization
up to 32 bytes is stored in header which can be easy used to
determinate type of translation file.
<pI18N> - I18N translation set,
if it's not given then currently active I18N set is used
<cNewDescription> - new description
HB_I18N_ADDTEXT( <pI18N>, <cMsgID>, <cTrans> | <acTrans> [, <cContext> ] )
-> NIL
Adds new message with translation to i18n translation set
<pI18N> - I18N translation set
<cMsgID> - original message
<cTrans> - translated message
<acTrans> - array with translated messages used for plural forms
<cContext> - message context
HB_I18N_SET( [ <pI18N> | NIL ] )
-> <lActive>
Sets given I18N translation set as default one used by
HB_I18N_[N]GETTEXT[_STRICT]() functions or remove translation
set for calling thread when passed parameter is NIL
<pI18N> - I18N translation set
Returns logical value which is .T. when i18n set is active
HB_I18N_SAVETABLE( [<pI18N>] )
-> <cTable>
Returns I18N translation as string item which can be stored
in file or database
<pI18N> - I18N translation set, if it's not given then currently
active I18N set is used
HB_I18N_RESTORETABLE( <cTable> )
-> <pI18N> | NIL
Restores I18N translation set from strin item.
<cTable> - I18N translation set in string representation
On success it returns new <pI18N> set otherwise NIL if <cTable>
is not valid item created by HB_I18N_SAVETABLE() or it's corrupted.
HB_I18N_HEADERSIZE()
-> <nHeaderSize>
Returns size of header used by i18n serialized version
HB_I18N_CHEK( <cTable> | <cHeader> [, @<cDescription> ] )
-> <lValid>
<cTable> - i18n translation set serialized by HB_I18N_SAVETABLE
<cHeader> - header of i18n translation set
( LEFT( <cTable>, HB_I18N_HEADERSIZE() )
<cDescription> - optional parameter passed by reference where
will be sored i18n translation set description
extracted from valid header
Returns logical value indicating if given table or header is
valid serialized by HB_I18N_SAVETABLE() data. It does not
decode the table though it validates size and control sums.
These functions are optional and some future alternative implementations
may not support all of them and/or may provide some other functions.
+ added unofficial .prg function __I18N_HASHTABLE() which allows to
access hash table used by i18n translation set or create new translation
set with given hash table. It's helper functions for developers which
will work on Harbour i18n tools and should not be used by Harbour users.
Unlike original gettext Harbour allows to use language with many
plural forms as base one. In such case programmer should activate
at application startup default i18n translation set with base plural
form valid for base application language, f.e. by:
pI18N := hb_i18n_create()
hb_i18n_pluralForm( pI18N, <cLangID> | <bForm>, .t. )
hb_i18n_set( pI18N )
.prg code example:
#xtranslate _( <x,...> ) => hb_i18n_gettext_strict( <x> )
#xtranslate _N( <x,...> ) => hb_i18n_ngettext_strict( <x> )
proc main()
local pI18N, i
pI18N := hb_i18n_create()
hb_i18n_pluralForm( pI18N, "PL", .t. )
hb_i18n_set( pI18N )
for i := 0 to 30
? i, _N( i, {"grosz", "grosze", "groszy"} )
if i > 0 .and. i % 10 == 0
wait
endif
next
return
In .pot files created during compilation by Harbour with -j option
for above code we have the following entries for message with plural
forms:
msgid "grosz"
msgid_plural "grosze"
msgid_plural2 "groszy"
msgstr[0] ""
The msgid_plural2 (and others if language has more plural forms)
is Harbour extension which is not gettext compatible.
The above implementation is base version but should be fully functional.
Now we will need functions to safe/read i18n files and tools to mange
.pot files: merge them, edit translations, create final binary i18n
translation sets. Because we are using gettext compatible .pot files
then for some of such jobs we can use original gettext tools but we
need at least function which will create translation set from one or
more .pot files.
We should also agree some default localization(s) for files containing
translated data, their name convention and environment variable(s)
to set default language. It's not strictly necessary and each user
can have his own implementation but it would help in adding new
translations by final users to any Harbour application which will
respect them. We can use LANG envvar to extract preferred language
and use the same path as executed application looking for files
<appname>-<lang>.hil files though it may create some problems for
OSes which support only 8.3 file names so we can also define that
HB_I18N envvar has higher priority and points to expected translation
file.
* harbour/include/hbextern.ch
- removed old __i18n_*() functions
+ added current i18n functions
1909 lines
48 KiB
C
1909 lines
48 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 "hbatomic.h"
|
|
#include "hbapiitm.h"
|
|
#include "hbapierr.h"
|
|
#include "hbapicdp.h"
|
|
#include "hbapilng.h"
|
|
#include "hbvm.h"
|
|
#include "hbstack.h"
|
|
#include "hbdate.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;
|
|
static PHB_ITEM s_pOnceMutex = NULL;
|
|
|
|
#if !defined( HB_MT_VM )
|
|
/* nothing */
|
|
#else
|
|
|
|
static int s_waiting_for_threads = 0;
|
|
|
|
# if defined( HB_PTHREAD_API )
|
|
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_NEED_INIT )
|
|
static HB_RAWCRITICAL_T s_init_mtx;
|
|
static HB_RAWCRITICAL_T s_once_mtx;
|
|
static HB_RAWCRITICAL_T s_thread_mtx;
|
|
static HB_RAWCRITICAL_T s_mutexlst_mtx;
|
|
static void hb_threadCriticalInit( HB_CRITICAL_T * critical )
|
|
{
|
|
if( !s_fThreadInit )
|
|
hb_threadInit();
|
|
|
|
HB_CRITICAL_LOCK( s_init_mtx );
|
|
if( !critical->fInit )
|
|
{
|
|
HB_CRITICAL_INIT( critical->critical );
|
|
critical->fInit = TRUE;
|
|
}
|
|
HB_CRITICAL_UNLOCK( s_init_mtx );
|
|
}
|
|
# else
|
|
# if defined( HB_COND_NEED_INIT )
|
|
static HB_CRITICAL_NEW( s_init_mtx );
|
|
# endif
|
|
static HB_CRITICAL_NEW( s_once_mtx );
|
|
static HB_CRITICAL_NEW( s_thread_mtx );
|
|
static HB_CRITICAL_NEW( s_mutexlst_mtx );
|
|
# endif
|
|
|
|
# if defined( HB_COND_NEED_INIT )
|
|
static HB_RAWCOND_T s_thread_cond;
|
|
static void hb_threadCondInit( HB_COND_T * cond )
|
|
{
|
|
if( !s_fThreadInit )
|
|
hb_threadInit();
|
|
|
|
HB_CRITICAL_LOCK( s_init_mtx );
|
|
if( !cond->fInit )
|
|
{
|
|
HB_COND_INIT( cond->cond );
|
|
# if !defined( HB_COND_OS_SUPPORT )
|
|
HB_CRITICAL_INIT( cond->critical );
|
|
cond->waiters = 0;
|
|
# endif
|
|
cond->fInit = TRUE;
|
|
}
|
|
HB_CRITICAL_UNLOCK( s_init_mtx );
|
|
}
|
|
# else
|
|
static HB_COND_NEW( s_thread_cond );
|
|
# endif
|
|
|
|
#endif /* HB_MT_VM */
|
|
|
|
void hb_threadInit( void )
|
|
{
|
|
if( !s_fThreadInit )
|
|
{
|
|
#if !defined( HB_MT_VM )
|
|
/* nothing to do */
|
|
#else
|
|
# if defined( HB_CRITICAL_NEED_INIT )
|
|
HB_CRITICAL_INIT( s_init_mtx );
|
|
HB_CRITICAL_INIT( s_once_mtx );
|
|
HB_CRITICAL_INIT( s_thread_mtx );
|
|
HB_CRITICAL_INIT( s_mutexlst_mtx );
|
|
# endif
|
|
# if defined( HB_COND_NEED_INIT )
|
|
HB_COND_INIT( s_thread_cond );
|
|
# endif
|
|
#endif
|
|
s_fThreadInit = TRUE;
|
|
}
|
|
}
|
|
|
|
void hb_threadExit( void )
|
|
{
|
|
if( s_pOnceMutex )
|
|
{
|
|
hb_itemRelease( s_pOnceMutex );
|
|
s_pOnceMutex = NULL;
|
|
}
|
|
}
|
|
|
|
#if defined( HB_OS_OS2 )
|
|
ULONG _hb_gettid( void )
|
|
{
|
|
ULONG tid = 0;
|
|
PTIB ptib = NULL;
|
|
|
|
if( DosGetInfoBlocks( &ptib, NULL ) == NO_ERROR )
|
|
tid = ptib->tib_ptib2->tib2_ultid;
|
|
|
|
return tid;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* atomic increment/decrement operations
|
|
*/
|
|
#if !defined( HB_MT_VM )
|
|
void hb_atomic_set( volatile HB_COUNTER * pCounter, HB_COUNTER value )
|
|
{
|
|
*pCounter = value;
|
|
}
|
|
|
|
HB_COUNTER hb_atomic_get( volatile HB_COUNTER * pCounter )
|
|
{
|
|
return *pCounter;
|
|
}
|
|
|
|
void hb_atomic_inc( volatile HB_COUNTER * pCounter )
|
|
{
|
|
++( *pCounter );
|
|
}
|
|
|
|
BOOL hb_atomic_dec( volatile HB_COUNTER * pCounter )
|
|
{
|
|
return --( *pCounter ) == 0;
|
|
}
|
|
#elif defined( HB_ATOM_INC ) && defined( HB_ATOM_DEC ) && \
|
|
defined( HB_ATOM_GET ) && defined( HB_ATOM_SET )
|
|
void hb_atomic_set( volatile HB_COUNTER * pCounter, HB_COUNTER value )
|
|
{
|
|
HB_ATOM_SET( pCounter, value );
|
|
}
|
|
|
|
HB_COUNTER hb_atomic_get( volatile HB_COUNTER * pCounter )
|
|
{
|
|
return HB_ATOM_GET( pCounter );
|
|
}
|
|
|
|
void hb_atomic_inc( volatile HB_COUNTER * pCounter )
|
|
{
|
|
HB_ATOM_INC( pCounter );
|
|
}
|
|
|
|
BOOL hb_atomic_dec( volatile HB_COUNTER * pCounter )
|
|
{
|
|
return HB_ATOM_DEC( pCounter ) == 0;
|
|
}
|
|
#else
|
|
static HB_CRITICAL_NEW( s_atomicMtx );
|
|
void hb_atomic_set( volatile HB_COUNTER * pCounter, HB_COUNTER value )
|
|
{
|
|
/* NOTE: on some platforms it may be necessary to protect this
|
|
* by cirtical section, f.e. when HB_COUNTER cannot be accessed
|
|
* using single memory access by CPU.
|
|
*/
|
|
*pCounter = value;
|
|
}
|
|
|
|
HB_COUNTER hb_atomic_get( volatile HB_COUNTER * pCounter )
|
|
{
|
|
/* NOTE: on some platforms it may be necessary to protect this
|
|
* by cirtical section, f.e. when HB_COUNTER cannot be accessed
|
|
* using single memory access by CPU.
|
|
*/
|
|
return *pCounter;
|
|
}
|
|
|
|
void hb_atomic_inc( volatile HB_COUNTER * pCounter )
|
|
{
|
|
hb_threadEnterCriticalSection( &s_atomicMtx );
|
|
++( *pCounter );
|
|
hb_threadLeaveCriticalSection( &s_atomicMtx );
|
|
}
|
|
|
|
BOOL hb_atomic_dec( volatile HB_COUNTER * pCounter )
|
|
{
|
|
BOOL fResult;
|
|
hb_threadEnterCriticalSection( &s_atomicMtx );
|
|
fResult = --( *pCounter ) == 0;
|
|
hb_threadLeaveCriticalSection( &s_atomicMtx );
|
|
return fResult;
|
|
}
|
|
#endif
|
|
|
|
void hb_threadEnterCriticalSection( HB_CRITICAL_T * critical )
|
|
{
|
|
#if !defined( HB_MT_VM )
|
|
HB_SYMBOL_UNUSED( critical );
|
|
#elif defined( HB_CRITICAL_NEED_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_NEED_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_NEED_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_NEED_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_NEED_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 );
|
|
/* There is race condition here and user code should always check if
|
|
* if the wait condition is valid after leaving hb_threadCondWait()
|
|
* even if it returns TRUE
|
|
*/
|
|
if( !fResult )
|
|
cond->waiters--;
|
|
|
|
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_NEED_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 );
|
|
/* There is race condition here and user code should always check if
|
|
* if the wait condition is valid after leaving hb_threadCondTimedWait()
|
|
* even if it returns TRUE
|
|
*/
|
|
if( !fResult )
|
|
cond->waiters--;
|
|
|
|
return fResult;
|
|
|
|
#endif
|
|
}
|
|
|
|
HB_THREAD_HANDLE hb_threadCreate( HB_THREAD_ID * th_id, PHB_THREAD_STARTFUNC start_func, void * Cargo )
|
|
{
|
|
HB_THREAD_HANDLE th_h;
|
|
|
|
#if !defined( HB_MT_VM )
|
|
HB_SYMBOL_UNUSED( start_func );
|
|
HB_SYMBOL_UNUSED( Cargo );
|
|
*th_id = ( HB_THREAD_ID ) 0;
|
|
th_h = ( HB_THREAD_HANDLE ) 0;
|
|
#elif defined( HB_PTHREAD_API )
|
|
if( pthread_create( th_id, NULL, start_func, Cargo ) != 0 )
|
|
*th_id = ( HB_THREAD_ID ) 0;
|
|
th_h = *th_id;
|
|
#elif defined( HB_OS_WIN_32 )
|
|
th_h = ( HANDLE ) _beginthreadex( NULL, 0, start_func, Cargo, 0, th_id );
|
|
if( !th_h )
|
|
*th_id = ( HB_THREAD_ID ) 0;
|
|
#elif defined( HB_OS_OS2 )
|
|
*th_id = _beginthread( start_func, NULL, 128 * 1024, Cargo );
|
|
th_h = *th_id;
|
|
#else
|
|
{ int TODO_MT; }
|
|
*th_id = ( HB_THREAD_ID ) 0;
|
|
th_h = ( HB_THREAD_HANDLE ) 0;
|
|
#endif
|
|
|
|
return th_h;
|
|
}
|
|
|
|
BOOL hb_threadJoin( HB_THREAD_HANDLE th_h )
|
|
{
|
|
#if !defined( HB_MT_VM )
|
|
HB_SYMBOL_UNUSED( th_h );
|
|
return FALSE;
|
|
#elif defined( HB_PTHREAD_API )
|
|
return pthread_join( th_h, NULL ) == 0;
|
|
#elif defined( HB_OS_WIN_32 )
|
|
if( WaitForSingleObject( th_h, INFINITE ) != WAIT_FAILED )
|
|
{
|
|
CloseHandle( th_h );
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
#elif defined( HB_OS_OS2 )
|
|
APIRET rc = DosWaitThread( &th_h, 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_HANDLE th_h )
|
|
{
|
|
#if !defined( HB_MT_VM )
|
|
HB_SYMBOL_UNUSED( th_h );
|
|
return FALSE;
|
|
#elif defined( HB_PTHREAD_API )
|
|
return pthread_detach( th_h ) == 0;
|
|
#elif defined( HB_OS_WIN_32 )
|
|
return CloseHandle( th_h ) != 0;
|
|
#elif defined( HB_OS_OS2 )
|
|
APIRET rc = DosWaitThread( &th_h, 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->pI18N )
|
|
{
|
|
hb_i18n_release( pThread->pI18N );
|
|
pThread->pI18N = NULL;
|
|
}
|
|
if( pThread->pSet )
|
|
{
|
|
hb_setRelease( pThread->pSet );
|
|
hb_xfree( pThread->pSet );
|
|
pThread->pSet = NULL;
|
|
}
|
|
if( pThread->th_h != 0 )
|
|
{
|
|
hb_threadDetach( pThread->th_h );
|
|
pThread->th_h = 0;
|
|
}
|
|
if( pThread->hGT )
|
|
{
|
|
hb_gtRelease( pThread->hGT );
|
|
pThread->hGT = NULL;
|
|
}
|
|
}
|
|
|
|
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 );
|
|
|
|
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_CRITICAL_LOCK( s_thread_mtx );
|
|
if( s_waiting_for_threads )
|
|
{
|
|
HB_COND_SIGNALN( s_thread_cond, s_waiting_for_threads );
|
|
s_waiting_for_threads = 0;
|
|
}
|
|
HB_CRITICAL_UNLOCK( s_thread_mtx );
|
|
|
|
HB_THREAD_END
|
|
#else
|
|
hb_itemRelease( ( PHB_ITEM ) Cargo );
|
|
HB_THREAD_RAWEND
|
|
#endif
|
|
}
|
|
|
|
PHB_THREADSTATE hb_threadStateNew( void )
|
|
{
|
|
PHB_ITEM pThItm;
|
|
PHB_THREADSTATE pThread;
|
|
|
|
pThItm = hb_itemNew( NULL );
|
|
pThread = ( PHB_THREADSTATE )
|
|
hb_gcAlloc( sizeof( HB_THREADSTATE ), hb_threadDestructor );
|
|
memset( pThread, 0, sizeof( HB_THREADSTATE ) );
|
|
hb_itemPutPtrGC( pThItm, pThread );
|
|
|
|
pThread->pszCDP = HB_MACRO2STRING( HB_CODEPAGE_DEFAULT );
|
|
pThread->pszLang = HB_MACRO2STRING( HB_LANG_DEFAULT );
|
|
pThread->pThItm = pThItm;
|
|
pThread->hGT = hb_gtAlloc( NULL );
|
|
|
|
return pThread;
|
|
}
|
|
|
|
static PHB_THREADSTATE hb_thParam( int iParam, int iPos )
|
|
{
|
|
PHB_THREADSTATE pThread = ( PHB_THREADSTATE )
|
|
hb_parptrGC( hb_threadDestructor, iParam, iPos );
|
|
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;
|
|
|
|
pThread = hb_threadStateNew();
|
|
pReturn = pThread->pThItm;
|
|
|
|
pThread->pszCDP = hb_cdpID();
|
|
pThread->pszLang = hb_langID();
|
|
pThread->pI18N = hb_i18n_alloc( hb_vmI18N() );
|
|
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 );
|
|
|
|
#if defined( HB_MT_VM )
|
|
if( hb_vmThreadRegister( ( void * ) pThread ) )
|
|
#endif
|
|
pThread->th_h = hb_threadCreate( &pThread->th_id, hb_threadStartVM, ( void * ) pReturn );
|
|
|
|
if( !pThread->th_h )
|
|
{
|
|
#if defined( HB_MT_VM )
|
|
hb_vmThreadRelease( pThread );
|
|
#else
|
|
hb_itemRelease( pReturn );
|
|
#endif
|
|
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();
|
|
/* It's possible that pThread will be NULL and this function will
|
|
* return NIL. It may happen only in one case when this function is
|
|
* executed by one of destructors of items stored in thread pointer
|
|
* item (in practice it can be only thread return value) and parent
|
|
* thread destroyed this thread pointer item. [druzus]
|
|
*/
|
|
if( pThread )
|
|
hb_itemReturn( pThread->pThItm );
|
|
#endif
|
|
}
|
|
|
|
HB_FUNC( HB_THREADID )
|
|
{
|
|
#if defined( HB_MT_VM )
|
|
PHB_THREADSTATE pThread;
|
|
|
|
if( hb_pcount() > 0 )
|
|
{
|
|
pThread = hb_thParam( 1, 0 );
|
|
if( pThread )
|
|
hb_retnint( pThread->th_no );
|
|
}
|
|
else
|
|
{
|
|
pThread = ( PHB_THREADSTATE ) hb_vmThreadState();
|
|
if( pThread )
|
|
hb_retnint( pThread->th_no );
|
|
else
|
|
hb_retnint( 0 );
|
|
}
|
|
#else
|
|
hb_retnint( 0 );
|
|
#endif
|
|
}
|
|
|
|
#if defined( HB_MT_VM )
|
|
static int hb_threadWait( PHB_THREADSTATE * pThreads, int iThreads,
|
|
BOOL fAll, ULONG ulMilliSec )
|
|
{
|
|
int i, iFinished, iResult = 0;
|
|
BOOL fExit = ulMilliSec == 0;
|
|
|
|
#if defined( HB_PTHREAD_API )
|
|
struct timespec ts;
|
|
|
|
if( ulMilliSec != HB_THREAD_INFINITE_WAIT )
|
|
hb_threadTimeInit( &ts, ulMilliSec );
|
|
else
|
|
ts.tv_sec = ts.tv_nsec = 0;
|
|
#else
|
|
HB_ULONG timer;
|
|
BOOL fResult;
|
|
|
|
if( ulMilliSec != HB_THREAD_INFINITE_WAIT )
|
|
timer = hb_dateMilliSeconds() + ulMilliSec;
|
|
else
|
|
timer = 0;
|
|
#endif
|
|
|
|
HB_CRITICAL_LOCK( s_thread_mtx );
|
|
for( ;; )
|
|
{
|
|
for( i = iFinished = 0; i < iThreads; ++i )
|
|
{
|
|
if( pThreads[ i ]->fFinished )
|
|
{
|
|
iFinished++;
|
|
if( !fAll )
|
|
{
|
|
iResult = i + 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if( iFinished >= ( fAll ? iThreads : 1 ) )
|
|
break;
|
|
|
|
if( fExit )
|
|
break;
|
|
|
|
s_waiting_for_threads++;
|
|
#if defined( HB_PTHREAD_API )
|
|
if( ulMilliSec != HB_THREAD_INFINITE_WAIT )
|
|
fExit = pthread_cond_timedwait( &s_thread_cond, &s_thread_mtx, &ts ) != 0;
|
|
else
|
|
fExit = pthread_cond_wait( &s_thread_cond, &s_thread_mtx ) != 0;
|
|
#else
|
|
HB_CRITICAL_UNLOCK( s_thread_mtx );
|
|
hb_vmUnlock();
|
|
fResult = HB_COND_TIMEDWAIT( s_thread_cond, ulMilliSec );
|
|
hb_vmLock();
|
|
HB_CRITICAL_LOCK( s_thread_mtx );
|
|
if( !fResult )
|
|
s_waiting_for_threads--;
|
|
if( timer )
|
|
{
|
|
HB_ULONG curr = hb_dateMilliSeconds();
|
|
if( timer <= curr )
|
|
fExit = TRUE;
|
|
else
|
|
ulMilliSec = timer - curr;
|
|
}
|
|
#endif
|
|
|
|
if( hb_vmRequestQuery() != 0 )
|
|
break;
|
|
}
|
|
HB_CRITICAL_UNLOCK( s_thread_mtx );
|
|
|
|
return fAll ? iFinished : iResult;
|
|
}
|
|
#endif
|
|
|
|
HB_FUNC( HB_THREADJOIN )
|
|
{
|
|
PHB_THREADSTATE pThread = hb_thParam( 1, 0 );
|
|
|
|
if( pThread )
|
|
{
|
|
BOOL fResult = FALSE;
|
|
|
|
if( pThread->th_h )
|
|
{
|
|
hb_vmUnlock();
|
|
fResult = hb_threadJoin( pThread->th_h );
|
|
if( fResult )
|
|
pThread->th_h = 0;
|
|
hb_vmLock();
|
|
}
|
|
if( fResult )
|
|
{
|
|
if( pThread->pResult )
|
|
{
|
|
hb_itemParamStoreForward( 2, pThread->pResult );
|
|
hb_itemRelease( pThread->pResult );
|
|
pThread->pResult = NULL;
|
|
}
|
|
}
|
|
hb_retl( fResult );
|
|
}
|
|
}
|
|
|
|
HB_FUNC( HB_THREADDETACH )
|
|
{
|
|
PHB_THREADSTATE pThread = hb_thParam( 1, 0 );
|
|
|
|
if( pThread )
|
|
{
|
|
BOOL fResult = FALSE;
|
|
|
|
if( pThread->th_h && hb_threadDetach( pThread->th_h ) )
|
|
{
|
|
pThread->th_h = 0;
|
|
fResult = TRUE;
|
|
}
|
|
hb_retl( fResult );
|
|
}
|
|
}
|
|
|
|
HB_FUNC( HB_THREADQUITREQUEST )
|
|
{
|
|
PHB_THREADSTATE pThread = hb_thParam( 1, 0 );
|
|
|
|
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_THREADWAIT )
|
|
{
|
|
#if defined( HB_MT_VM )
|
|
# define HB_THREAD_WAIT_ALLOC 16
|
|
BOOL fAll = FALSE;
|
|
ULONG ulMilliSec = HB_THREAD_INFINITE_WAIT;
|
|
PHB_THREADSTATE * pThreads, pAlloc[ HB_THREAD_WAIT_ALLOC ];
|
|
int iThreads = -1;
|
|
|
|
pThreads = pAlloc;
|
|
if( ISARRAY( 1 ) )
|
|
{
|
|
PHB_ITEM pArray = hb_param( 1, HB_IT_ARRAY );
|
|
int iLen = ( int ) hb_arrayLen( pArray ), i;
|
|
|
|
for( i = iThreads = 0; i < iLen; ++i )
|
|
{
|
|
PHB_THREADSTATE pThread = hb_thParam( 1, i + 1 );
|
|
if( !pThread )
|
|
{
|
|
iThreads = -1;
|
|
break;
|
|
}
|
|
if( pThreads == pAlloc && iThreads >= HB_THREAD_WAIT_ALLOC )
|
|
{
|
|
pThreads = ( PHB_THREADSTATE * )
|
|
hb_xgrab( sizeof( PHB_THREADSTATE ) * iLen );
|
|
memcpy( pThreads, pAlloc, sizeof( pAlloc ) );
|
|
}
|
|
pThreads[ iThreads++ ] = pThread;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pThreads[ 0 ] = hb_thParam( 1, 0 );
|
|
if( pThreads[ 0 ] )
|
|
iThreads = 1;
|
|
}
|
|
|
|
if( iThreads > 0 )
|
|
{
|
|
if( ISNUM( 2 ) )
|
|
{
|
|
double dTimeOut = hb_parnd( 2 );
|
|
ulMilliSec = dTimeOut > 0 ? ( ULONG ) ( dTimeOut * 1000 ) : 0;
|
|
}
|
|
|
|
if( ISLOG( 3 ) )
|
|
fAll = hb_parl( 3 );
|
|
|
|
hb_retni( hb_threadWait( pThreads, iThreads, fAll, ulMilliSec ) );
|
|
}
|
|
else if( iThreads == 0 )
|
|
hb_retni( 0 );
|
|
|
|
if( pThreads != pAlloc )
|
|
hb_xfree( pThreads );
|
|
#endif
|
|
}
|
|
|
|
|
|
HB_FUNC( HB_THREADWAITFORALL )
|
|
{
|
|
#if defined( HB_MT_VM )
|
|
hb_vmWaitForThreads();
|
|
#endif
|
|
}
|
|
|
|
HB_FUNC( HB_THREADTERMINATEALL )
|
|
{
|
|
#if defined( HB_MT_VM )
|
|
hb_vmTerminateThreads();
|
|
#endif
|
|
}
|
|
|
|
/* hb_threadOnce( @<onceControl> [, <bAction> ] ) -> <lFirstCall>
|
|
* Execute <bAction> only once. <onceControl> is variable which holds
|
|
* the execution status and have to be initialized to NIL. In most of
|
|
* cases it will be simple staticvariable in user code.
|
|
* When <bAction> is executed by a thread all other threads which call
|
|
* hb_threadOnce() are stopped even if they use different <onceControl>.
|
|
* Because hb_threadOnce() uses single recursive mutex then deadlock caused
|
|
* by cross call to hb_threadOnce() from different threads is not possible.
|
|
* If thread calls hb_threadOnce() with the same <onceControl> variable
|
|
* recursively from <bAction> then hb_threadOnce() returns immediately
|
|
* returning FALSE without executing <bAction>.
|
|
* This function returns logical value indicating if it was 1-st call to
|
|
* hb_threadOnce() for given <onceControl> variable
|
|
*/
|
|
HB_FUNC( HB_THREADONCE )
|
|
{
|
|
PHB_ITEM pItem = hb_param( 1, HB_IT_ANY );
|
|
if( pItem && ISBYREF( 1 ) && ( HB_IS_NIL( pItem ) || HB_IS_LOGICAL( pItem ) ) )
|
|
{
|
|
BOOL fFirstCall = FALSE;
|
|
if( HB_IS_NIL( pItem ) || !hb_itemGetL( pItem ) )
|
|
{
|
|
PHB_ITEM pAction = hb_param( 2, HB_IT_BLOCK | HB_IT_SYMBOL );
|
|
|
|
#if defined( HB_MT_VM )
|
|
if( !s_pOnceMutex )
|
|
{
|
|
if( !s_fThreadInit )
|
|
hb_threadInit();
|
|
HB_CRITICAL_LOCK( s_once_mtx );
|
|
if( !s_pOnceMutex )
|
|
s_pOnceMutex = hb_threadMutexCreate( FALSE );
|
|
HB_CRITICAL_UNLOCK( s_once_mtx );
|
|
}
|
|
if( hb_threadMutexLock( s_pOnceMutex ) )
|
|
{
|
|
if( HB_IS_NIL( pItem ) )
|
|
{
|
|
if( pAction )
|
|
{
|
|
hb_storl( FALSE, 1 );
|
|
hb_vmEvalBlock( pAction );
|
|
}
|
|
hb_storl( TRUE, 1 );
|
|
fFirstCall = TRUE;
|
|
}
|
|
hb_threadMutexUnlock( s_pOnceMutex );
|
|
}
|
|
#else
|
|
hb_storl( TRUE, 1 );
|
|
fFirstCall = TRUE;
|
|
if( pAction )
|
|
hb_vmEvalBlock( pAction );
|
|
#endif
|
|
}
|
|
hb_retl( fFirstCall );
|
|
}
|
|
else
|
|
hb_errRT_BASE_SubstR( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
|
|
}
|
|
|
|
/* 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_l;
|
|
HB_RAWCOND_T cond_w;
|
|
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_mutexListUnlock( PHB_MUTEX * pList, PHB_MTXLST * pStore )
|
|
{
|
|
HB_CRITICAL_LOCK( s_mutexlst_mtx );
|
|
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;
|
|
if( pMutex->lockers )
|
|
HB_COND_SIGNAL( pMutex->cond_l );
|
|
}
|
|
HB_CRITICAL_UNLOCK( pMutex->mutex );
|
|
}
|
|
pMutex = pMutex->pNext;
|
|
}
|
|
while( pMutex != *pList );
|
|
}
|
|
HB_CRITICAL_UNLOCK( s_mutexlst_mtx );
|
|
}
|
|
|
|
static void hb_mutexListLock( PHB_MTXLST pList )
|
|
{
|
|
while( pList )
|
|
{
|
|
PHB_MUTEX pMutex = pList->pMutex;
|
|
|
|
HB_CRITICAL_LOCK( pMutex->mutex );
|
|
while( pMutex->lock_count != 0 )
|
|
{
|
|
pMutex->lockers++;
|
|
#if defined( HB_PTHREAD_API )
|
|
pthread_cond_wait( &pMutex->cond_l, &pMutex->mutex );
|
|
#else
|
|
HB_CRITICAL_UNLOCK( pMutex->mutex );
|
|
( void ) HB_COND_WAIT( pMutex->cond_l );
|
|
HB_CRITICAL_LOCK( pMutex->mutex );
|
|
pMutex->lockers--;
|
|
#endif
|
|
}
|
|
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_mutexListUnlock( &s_pMutexList, NULL );
|
|
hb_mutexListUnlock( &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_mutexlst_mtx );
|
|
hb_mutexUnlink( pMutex->fSync ? &s_pSyncList : &s_pMutexList, pMutex );
|
|
HB_CRITICAL_UNLOCK( s_mutexlst_mtx );
|
|
#else
|
|
hb_mutexUnlink( pMutex->fSync ? &s_pSyncList : &s_pMutexList, pMutex );
|
|
#endif
|
|
|
|
if( pMutex->events )
|
|
hb_itemRelease( pMutex->events );
|
|
|
|
#if !defined( HB_MT_VM )
|
|
/* nothing */
|
|
#else
|
|
HB_CRITICAL_DESTROY( pMutex->mutex );
|
|
HB_COND_DESTROY( pMutex->cond_l );
|
|
HB_COND_DESTROY( pMutex->cond_w );
|
|
#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 */
|
|
#else
|
|
HB_CRITICAL_INIT( pMutex->mutex );
|
|
HB_COND_INIT( pMutex->cond_l );
|
|
HB_COND_INIT( pMutex->cond_w );
|
|
#endif
|
|
|
|
pMutex->fSync = fSync;
|
|
#if defined( HB_MT_VM )
|
|
HB_CRITICAL_LOCK( s_mutexlst_mtx );
|
|
hb_mutexLink( fSync ? &s_pSyncList : &s_pMutexList, pMutex );
|
|
HB_CRITICAL_UNLOCK( s_mutexlst_mtx );
|
|
#else
|
|
hb_mutexLink( fSync ? &s_pSyncList : &s_pMutexList, pMutex );
|
|
#endif
|
|
|
|
return pItem;
|
|
}
|
|
|
|
BOOL hb_threadMutexUnlock( PHB_ITEM pItem )
|
|
{
|
|
PHB_MUTEX pMutex = hb_mutexPtr( pItem );
|
|
BOOL fResult = FALSE;
|
|
|
|
if( pMutex )
|
|
{
|
|
#if !defined( HB_MT_VM )
|
|
if( HB_THREAD_EQUAL( pMutex->owner, HB_THREAD_SELF() ) )
|
|
{
|
|
if( --pMutex->lock_count == 0 )
|
|
pMutex->owner = ( HB_THREAD_ID ) 0;
|
|
fResult = TRUE;
|
|
}
|
|
#else
|
|
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;
|
|
if( pMutex->lockers )
|
|
HB_COND_SIGNAL( pMutex->cond_l );
|
|
}
|
|
fResult = TRUE;
|
|
}
|
|
HB_CRITICAL_UNLOCK( pMutex->mutex );
|
|
#endif
|
|
}
|
|
return fResult;
|
|
}
|
|
|
|
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++;
|
|
fResult = TRUE;
|
|
}
|
|
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 );
|
|
while( pMutex->lock_count != 0 )
|
|
{
|
|
pMutex->lockers++;
|
|
#if defined( HB_PTHREAD_API )
|
|
pthread_cond_wait( &pMutex->cond_l, &pMutex->mutex );
|
|
#else
|
|
HB_CRITICAL_UNLOCK( pMutex->mutex );
|
|
( void ) HB_COND_WAIT( pMutex->cond_l );
|
|
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++;
|
|
fResult = TRUE;
|
|
}
|
|
else
|
|
{
|
|
hb_vmUnlock();
|
|
|
|
#if !defined( HB_MT_VM )
|
|
HB_SYMBOL_UNUSED( ulMilliSec );
|
|
pMutex->lock_count = 1;
|
|
pMutex->owner = HB_THREAD_SELF();
|
|
fResult = TRUE;
|
|
#else
|
|
HB_CRITICAL_LOCK( pMutex->mutex );
|
|
if( ulMilliSec && pMutex->lock_count != 0 )
|
|
{
|
|
#if defined( HB_PTHREAD_API )
|
|
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_l, &pMutex->mutex, &ts ) != 0 )
|
|
break;
|
|
}
|
|
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
|
|
}
|
|
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;
|
|
}
|
|
|
|
void hb_threadMutexNotify( PHB_ITEM pItem, PHB_ITEM pNotifier, BOOL fWaiting )
|
|
{
|
|
PHB_MUTEX pMutex = hb_mutexPtr( pItem );
|
|
|
|
if( pMutex )
|
|
{
|
|
#if !defined( HB_MT_VM )
|
|
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 );
|
|
}
|
|
else 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 > 0 )
|
|
{
|
|
if( pNotifier && !HB_IS_NIL( pNotifier ) )
|
|
{
|
|
int iSet = iCount;
|
|
do
|
|
hb_arraySet( pMutex->events, ++ulLen, pNotifier );
|
|
while( --iSet );
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
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 );
|
|
if( pMutex->waiters )
|
|
HB_COND_SIGNAL( pMutex->cond_w );
|
|
}
|
|
else 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 > 0 )
|
|
{
|
|
if( pNotifier && !HB_IS_NIL( pNotifier ) )
|
|
{
|
|
int iSet = iCount;
|
|
do
|
|
hb_arraySet( pMutex->events, ++ulLen, pNotifier );
|
|
while( --iSet );
|
|
}
|
|
if( iCount == 1 )
|
|
HB_COND_SIGNAL( pMutex->cond_w );
|
|
else
|
|
HB_COND_SIGNALN( pMutex->cond_w, iCount );
|
|
}
|
|
}
|
|
HB_CRITICAL_UNLOCK( pMutex->mutex );
|
|
#endif
|
|
}
|
|
}
|
|
|
|
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;
|
|
BOOL fSync = TRUE;
|
|
int lock_count = 0;
|
|
|
|
hb_vmUnlock();
|
|
|
|
HB_CRITICAL_LOCK( pMutex->mutex );
|
|
|
|
if( fClear && pMutex->events )
|
|
hb_arraySize( pMutex->events, 0 );
|
|
|
|
/* release own locak from this mutex */
|
|
if( HB_THREAD_EQUAL( pMutex->owner, HB_THREAD_SELF() ) )
|
|
{
|
|
lock_count = pMutex->lock_count;
|
|
pMutex->lock_count = 0;
|
|
pMutex->owner = ( HB_THREAD_ID ) 0;
|
|
if( pMutex->lockers )
|
|
HB_COND_SIGNAL( pMutex->cond_l );
|
|
}
|
|
|
|
while( !pMutex->events || hb_arrayLen( pMutex->events ) == 0 )
|
|
{
|
|
if( fSync )
|
|
{
|
|
/* SYNC method mutexes cannot be used for subscribe so it's safe
|
|
* to unlock them when THIS mutex is internally locked
|
|
*/
|
|
hb_mutexListUnlock( &s_pSyncList, &pSyncList );
|
|
fSync = FALSE;
|
|
}
|
|
|
|
pMutex->waiters++;
|
|
# if defined( HB_PTHREAD_API )
|
|
pthread_cond_wait( &pMutex->cond_w, &pMutex->mutex );
|
|
# else
|
|
HB_CRITICAL_UNLOCK( pMutex->mutex );
|
|
( void ) HB_COND_WAIT( pMutex->cond_w );
|
|
HB_CRITICAL_LOCK( pMutex->mutex );
|
|
pMutex->waiters--;
|
|
# endif
|
|
}
|
|
|
|
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 );
|
|
}
|
|
|
|
/* restore the own lock on this mutex if necessary */
|
|
if( lock_count )
|
|
{
|
|
if( pMutex->owner )
|
|
{
|
|
pMutex->lockers++;
|
|
while( pMutex->lock_count != 0 )
|
|
{
|
|
#if defined( HB_PTHREAD_API )
|
|
pthread_cond_wait( &pMutex->cond_l, &pMutex->mutex );
|
|
#else
|
|
HB_CRITICAL_UNLOCK( pMutex->mutex );
|
|
( void ) HB_COND_WAIT( pMutex->cond_l );
|
|
HB_CRITICAL_LOCK( pMutex->mutex );
|
|
#endif
|
|
}
|
|
pMutex->lockers--;
|
|
}
|
|
pMutex->lock_count = lock_count;
|
|
pMutex->owner = HB_THREAD_SELF();
|
|
}
|
|
|
|
HB_CRITICAL_UNLOCK( pMutex->mutex );
|
|
|
|
hb_mutexListLock( pSyncList );
|
|
|
|
hb_vmLock();
|
|
#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;
|
|
int lock_count = 0;
|
|
|
|
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 ) )
|
|
{
|
|
/* release own locak from this mutex */
|
|
if( HB_THREAD_EQUAL( pMutex->owner, HB_THREAD_SELF() ) )
|
|
{
|
|
lock_count = pMutex->lock_count;
|
|
pMutex->lock_count = 0;
|
|
pMutex->owner = ( HB_THREAD_ID ) 0;
|
|
if( pMutex->lockers )
|
|
HB_COND_SIGNAL( pMutex->cond_l );
|
|
}
|
|
|
|
/* SYNC method mutexes cannot be used for subscribe so it's safe
|
|
* to unlock them when THIS mutex is internally locked
|
|
*/
|
|
hb_mutexListUnlock( &s_pSyncList, &pSyncList );
|
|
|
|
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
|
|
{
|
|
HB_CRITICAL_UNLOCK( pMutex->mutex );
|
|
( void ) HB_COND_TIMEDWAIT( pMutex->cond_w, 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 );
|
|
}
|
|
|
|
/* restore the own lock on this mutex if necessary */
|
|
if( lock_count )
|
|
{
|
|
if( pMutex->owner )
|
|
{
|
|
pMutex->lockers++;
|
|
while( pMutex->lock_count != 0 )
|
|
{
|
|
#if defined( HB_PTHREAD_API )
|
|
pthread_cond_wait( &pMutex->cond_l, &pMutex->mutex );
|
|
#else
|
|
HB_CRITICAL_UNLOCK( pMutex->mutex );
|
|
( void ) HB_COND_WAIT( pMutex->cond_l );
|
|
HB_CRITICAL_LOCK( pMutex->mutex );
|
|
#endif
|
|
}
|
|
pMutex->lockers--;
|
|
}
|
|
pMutex->lock_count = lock_count;
|
|
pMutex->owner = HB_THREAD_SELF();
|
|
}
|
|
|
|
HB_CRITICAL_UNLOCK( pMutex->mutex );
|
|
|
|
hb_mutexListLock( pSyncList );
|
|
|
|
hb_vmLock();
|
|
#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( 2 );
|
|
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( 2 );
|
|
if( dTimeOut > 0 )
|
|
ulMilliSec = ( ULONG ) ( dTimeOut * 1000 );
|
|
pResult = hb_threadMutexTimedSubscribe( pItem, ulMilliSec, FALSE );
|
|
}
|
|
else
|
|
pResult = hb_threadMutexSubscribe( pItem, FALSE );
|
|
|
|
if( pResult )
|
|
{
|
|
hb_itemParamStoreForward( 3, pResult );
|
|
hb_itemRelease( pResult );
|
|
hb_retl( TRUE );
|
|
}
|
|
else
|
|
hb_retl( FALSE );
|
|
}
|
|
}
|
|
|
|
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( 2 );
|
|
if( dTimeOut > 0 )
|
|
ulMilliSec = ( ULONG ) ( dTimeOut * 1000 );
|
|
pResult = hb_threadMutexTimedSubscribe( pItem, ulMilliSec, TRUE );
|
|
}
|
|
else
|
|
pResult = hb_threadMutexSubscribe( pItem, TRUE );
|
|
|
|
if( pResult )
|
|
{
|
|
hb_itemParamStoreForward( 3, pResult );
|
|
hb_itemRelease( pResult );
|
|
hb_retl( TRUE );
|
|
}
|
|
else
|
|
hb_retl( FALSE );
|
|
}
|
|
}
|
|
|
|
HB_FUNC( HB_MTVM )
|
|
{
|
|
#if defined( HB_MT_VM )
|
|
hb_retl( TRUE );
|
|
#else
|
|
hb_retl( FALSE );
|
|
#endif
|
|
}
|