Files
harbour-core/src/vm/task.c
Viktor Szakats e364f8634b 2015-06-17 12:39 UTC+0200 Przemyslaw Czerpak (druzus/at/poczta.onet.pl)
* src/rtl/filesys.c
    ! fixed FXO_TRUNCATE flag used without FXO_SHARELOCK in POSIX systems

  * src/rtl/net.c
    ! typo in variable name

  * src/vm/task.c
    ! variable scope

  * src/compiler/compi18n.c
    * minor variable type update

  * ChangeLog.txt
    ! formatting
2015-06-17 13:30:50 +02:00

1148 lines
28 KiB
C

/*
* Harbour Project source code:
* platform independent task system. It's used when when OS does not
* support threads
*
* Copyright 2009 Przemyslaw Czerpak <druzus / at / priv.onet.pl>
* www - http://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.txt. 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_TASK_DEBUG
#include "hbapi.h"
#include "hbtask.h"
#include "hbapierr.h"
#if defined( HB_OS_WIN )
# include <windows.h>
#endif
#include <time.h>
#if defined( HB_OS_UNIX )
# include <sys/time.h>
# include <sys/types.h>
# include <unistd.h>
#elif defined( __DJGPP__ )
# include <unistd.h>
#elif defined( __MINGW32__ )
# include <sys/time.h>
#endif
#if ! defined( HB_HAS_UCONTEXT )
# if ( defined( HB_OS_LINUX ) && ! defined( __WATCOMC__ ) ) || defined( HB_OS_MINIX )
# define HB_HAS_UCONTEXT
# endif
#endif
#if defined( HB_HAS_UCONTEXT )
# include <ucontext.h>
#else
# include <setjmp.h>
#endif
#define HB_TASK_STACK_MIN 16384
#define HB_TASK_STACK_ALIGN 16
#define HB_TASK_NO_DELAY 0
#define HB_TASK_INFINITE_DELAY HB_VMLONG_MAX
#undef HB_TASK_STACK_INIT
#if ! defined( HB_HAS_UCONTEXT )
# if defined( __GNUC__ )
# if defined( __DJGPP__ )
# define HB_TASK_STACK_INIT( jmp, sp ) \
do { (jmp)[0].__esp = (unsigned) (sp); } while( 0 )
# elif defined( HB_OS_LINUX )
# if defined( JB_SP )
# define HB_TASK_STACK_INIT( jmp, sp ) \
do { (jmp)[0].__jmpbuf[JB_SP] = (int) (sp); } while( 0 )
# else
# define HB_TASK_STACK_INIT( jmp, sp ) \
do { (jmp)[0].__jmpbuf[4] = (int) (sp); } while( 0 )
# endif
# elif defined( HB_OS_WIN )
# define HB_TASK_STACK_INIT( jmp, sp ) \
do { (jmp)[7] = (unsigned) (sp); } while( 0 )
# endif
# elif defined( __WATCOMC__ )
# if defined( __386__ )
# define HB_TASK_STACK_INIT( jmp, sp ) \
do { (jmp)[7] = (unsigned int) (sp); } while( 0 )
# endif
# endif
# ifndef HB_TASK_STACK_INIT
# error Unsupported system/architecture
# endif
#endif
typedef enum
{
TASK_INIT = 0,
TASK_RUNNING,
TASK_SLEEPING,
TASK_SUSPEND,
TASK_DONE,
TASK_ZOMBIE
}
HB_TASKSTATE;
struct _HB_TASKMTX;
struct _HB_TASKCOND;
typedef struct _HB_TASKINFO
{
int id;
HB_TASKSTATE state;
HB_BOOL detached;
HB_BOOL stop;
HB_MAXINT wakeup;
char * stack;
long stack_size;
#if defined( HB_HAS_UCONTEXT )
ucontext_t context;
#else
jmp_buf context;
#endif
void * ( * start ) ( void * );
void * cargo;
void * result;
void * data;
int joiners;
int locked;
struct _HB_TASKINFO * joining;
struct _HB_TASKMTX * locking;
struct _HB_TASKCOND * waiting;
struct _HB_TASKINFO * pSleepNext;
struct _HB_TASKINFO * pBlockNext;
struct _HB_TASKINFO * pWaitNext;
struct _HB_TASKINFO * pNext;
struct _HB_TASKINFO * pPrev;
}
HB_TASKINFO, * PHB_TASKINFO;
typedef struct _HB_TASKMTX
{
int count;
PHB_TASKINFO task;
PHB_TASKINFO lockers;
struct _HB_TASKMTX * next;
}
HB_TASKMTX, * PHB_TASKMTX;
typedef struct _HB_TASKCOND
{
PHB_TASKINFO waiters;
PHB_TASKMTX mutex;
struct _HB_TASKCOND * next;
}
HB_TASKCOND, * PHB_TASKCOND;
static PHB_TASKINFO s_taskSleep = NULL;
static PHB_TASKINFO s_taskList = NULL;
static PHB_TASKINFO s_currTask = NULL;
static PHB_TASKINFO s_mainTask = NULL;
static PHB_TASKCOND s_condList = NULL;
static PHB_TASKMTX s_mutexList = NULL;
static int s_iTaskID = 0;
static HB_MAXINT hb_taskTimeStop( unsigned long ulMilliSec )
{
if( ulMilliSec == HB_TASK_INFINITE_WAIT )
return HB_TASK_INFINITE_DELAY;
else
{
#if defined( __DJGPP__ )
/* uclock_t uclock() * 1000 / UCLOCKS_PER_SEC */
return ( HB_MAXINT ) clock() * 1000 / CLOCKS_PER_SEC + ulMilliSec;
#elif _POSIX_C_SOURCE >= 199309L
struct timespec ts;
clock_gettime( CLOCK_REALTIME, &ts );
return ( HB_MAXINT ) ts.tv_sec * 1000 + ts.tv_nsec / 1000000 + ulMilliSec;
#elif defined( HB_OS_UNIX )
struct timeval tv;
gettimeofday( &tv, NULL );
return ( HB_MAXINT ) tv.tv_sec * 1000 + tv.tv_usec / 1000 + ulMilliSec;
#else
return ( HB_MAXINT ) clock() * 1000 / CLOCKS_PER_SEC + ulMilliSec;
#endif
}
}
static void hb_taskFreeze( HB_MAXINT wakeup )
{
#if defined( HB_OS_DOS )
while( wakeup > hb_taskTimeStop( 0 ) )
{
union REGS regs;
regs.h.ah = 2;
regs.HB_XREGS.ax = 0x1680;
HB_DOS_INT86( 0x2F, &regs, &regs );
}
#else
wakeup -= hb_taskTimeStop( 0 );
if( wakeup > 0 )
{
# if defined( __DJGPP__ )
usleep( wakeup * 1000 );
# elif defined( HB_OS_WIN )
Sleep( wakeup );
# elif defined( HB_OS_OS2 )
DosSleep( wakeup ); /* Duration is in milliseconds */
# elif defined( HB_OS_UNIX )
struct timeval tv;
tv.tv_sec = wakeup / 1000;
tv.tv_usec = ( wakeup % 1000 ) * 1000;
select( 0, NULL, NULL, NULL, &tv );
# endif
}
#endif
}
static void hb_taskLink( PHB_TASKINFO * pList, PHB_TASKINFO pTask )
{
if( *pList )
{
pTask->pNext = *pList;
( pTask->pPrev = ( *pList )->pPrev )->pNext = pTask;
( *pList )->pPrev = pTask;
}
else
*pList = pTask->pNext = pTask->pPrev = pTask;
}
static void hb_taskUnlink( PHB_TASKINFO * pList, PHB_TASKINFO pTask )
{
pTask->pPrev->pNext = pTask->pNext;
pTask->pNext->pPrev = pTask->pPrev;
if( *pList == pTask )
{
*pList = pTask->pNext;
if( *pList == pTask )
*pList = NULL;
}
}
static PHB_TASKMTX hb_taskMutexNew( void )
{
PHB_TASKMTX pMutex;
pMutex = ( PHB_TASKMTX ) hb_xgrab( sizeof( HB_TASKMTX ) );
pMutex->count = 0;
pMutex->task = NULL;
pMutex->lockers = NULL;
pMutex->next = s_mutexList;
s_mutexList = pMutex;
return pMutex;
}
static PHB_TASKCOND hb_taskCondNew( void )
{
PHB_TASKCOND pCond;
pCond = ( PHB_TASKCOND ) hb_xgrab( sizeof( HB_TASKCOND ) );
pCond->waiters = NULL;
pCond->mutex = NULL;
pCond->next = s_condList;
s_condList = pCond;
return pCond;
}
static void hb_taskSetSleep( PHB_TASKINFO pTask, unsigned long ulMilliSec )
{
PHB_TASKINFO * pSleep;
pTask->wakeup = hb_taskTimeStop( ulMilliSec );
pTask->state = TASK_SLEEPING;
/* insert current task to sleepers queue */
pSleep = &s_taskSleep;
while( *pSleep && ( *pSleep )->wakeup <= pTask->wakeup )
pSleep = &( *pSleep )->pSleepNext;
pTask->pSleepNext = *pSleep;
*pSleep = pTask;
}
static void hb_taskWakeUp( PHB_TASKINFO pTask )
{
PHB_TASKINFO * pSleep = &s_taskSleep;
while( *pSleep )
{
if( *pSleep == pTask )
{
*pSleep = ( *pSleep )->pSleepNext;
break;
}
pSleep = &( *pSleep )->pSleepNext;
}
}
static PHB_TASKINFO hb_taskSwitchLock( PHB_TASKMTX pMutex )
{
PHB_TASKINFO pTask = pMutex->lockers;
if( pTask )
{
pMutex->lockers = pTask->pBlockNext;
#ifdef HB_TASK_DEBUG
if( pTask->locking != pMutex )
hb_errInternal( HB_EI_ERRUNRECOV, "TaskSwitchLock: broken lock", NULL, NULL );
#endif
pTask->locking = NULL;
pTask->locked++;
pMutex->task = pTask;
pMutex->count = 1;
if( pTask->state == TASK_SLEEPING )
{
hb_taskWakeUp( pTask );
pTask->state = TASK_RUNNING;
}
else if( pTask->state != TASK_RUNNING )
hb_errInternal( HB_EI_ERRUNRECOV, "TaskSwitchLock: task resumed", NULL, NULL );
}
return pTask;
}
static void hb_taskFree( PHB_TASKINFO pTask )
{
hb_taskUnlink( &s_taskList, pTask );
if( pTask->stack )
hb_xfree( pTask->stack );
hb_xfree( pTask );
}
static void hb_taskFinalize( PHB_TASKINFO pTask )
{
pTask->data = NULL;
if( pTask->joiners )
{
PHB_TASKINFO * pSleep = &s_taskSleep, pJoiner;
while( *pSleep )
{
if( ( *pSleep )->joining == pTask )
{
pJoiner = *pSleep;
pJoiner->joining = NULL;
*pSleep = ( *pSleep )->pSleepNext;
pJoiner->wakeup = 0;
pJoiner->pSleepNext = s_taskSleep;
s_taskSleep = pJoiner;
if( --pTask->joiners == 0 )
break;
}
else
pSleep = &( *pSleep )->pSleepNext;
}
#ifdef HB_TASK_DEBUG
if( pTask->joiners )
hb_errInternal( HB_EI_ERRUNRECOV, "TaskFinalize: dummy joiners", NULL, NULL );
#endif
}
/* it cannot happen for runing threads */
/* remove from mutex lockers queue */
if( pTask->locking )
{
PHB_TASKINFO * pLock = &pTask->locking->lockers;
while( *pLock )
{
if( *pLock == pTask )
{
*pLock = pTask->pBlockNext;
pTask->locking = NULL;
break;
}
else
pLock = &( *pLock )->pBlockNext;
}
#ifdef HB_TASK_DEBUG
if( pTask->locking )
hb_errInternal( HB_EI_ERRUNRECOV, "TaskFinalize: dummy lock", NULL, NULL );
#endif
}
/* remove from condition queue */
if( pTask->waiting )
{
PHB_TASKINFO * pWait = &pTask->waiting->waiters;
while( *pWait )
{
if( *pWait == pTask )
{
*pWait = pTask->pWaitNext;
pTask->waiting = NULL;
break;
}
else
pWait = &( *pWait )->pWaitNext;
}
#ifdef HB_TASK_DEBUG
if( pTask->waiting )
hb_errInternal( HB_EI_ERRUNRECOV, "TaskFinalize: dummy cond", NULL, NULL );
#endif
}
if( pTask->state == TASK_SLEEPING )
hb_taskWakeUp( pTask );
pTask->state = TASK_DONE;
if( pTask->locked )
{
PHB_TASKMTX pMutex = s_mutexList;
while( pMutex )
{
if( pMutex->task == pTask && pMutex->count )
{
pTask->locked--;
pMutex->task = NULL;
pMutex->count = 0;
hb_taskSwitchLock( pMutex );
}
pMutex = pMutex->next;
}
#ifdef HB_TASK_DEBUG
if( pTask->locked )
hb_errInternal( HB_EI_ERRUNRECOV, "TaskFinalize: dummy lock", NULL, NULL );
#endif
}
if( pTask->detached )
{
pTask->state = TASK_ZOMBIE;
if( pTask == s_currTask )
{
/* switch to next active task */
hb_taskYield();
/* unreachable code */
}
else
hb_taskFree( pTask );
}
}
static void hb_taskRun( void )
{
/* execute task startup function */
s_currTask->result = s_currTask->start( s_currTask->cargo );
/* mark task as finished */
hb_taskFinalize( s_currTask );
/* switch to next active task */
hb_taskYield();
/* unreachable code */
}
static PHB_TASKINFO hb_taskNew( long stack_size )
{
PHB_TASKINFO pTask;
if( stack_size < HB_TASK_STACK_MIN )
stack_size = HB_TASK_STACK_MIN;
pTask = ( PHB_TASKINFO ) hb_xgrabz( sizeof( HB_TASKINFO ) );
pTask->stack = ( char * ) hb_xgrab( stack_size );
stack_size += ( HB_PTRDIFF ) pTask->stack;
stack_size &= ~( HB_TASK_STACK_ALIGN - 1 );
stack_size -= ( HB_PTRDIFF ) pTask->stack;
pTask->stack_size = stack_size;
pTask->id = ++s_iTaskID;
pTask->state = TASK_INIT;
#if defined( HB_HAS_UCONTEXT )
/* create new execution context and initialize its private stack */
if( getcontext( &pTask->context ) == -1 )
hb_errInternal( HB_EI_ERRUNRECOV, "getcontext", NULL, NULL );
pTask->context.uc_link = NULL;
pTask->context.uc_stack.ss_sp = pTask->stack;
pTask->context.uc_stack.ss_size = pTask->stack_size;
makecontext( &pTask->context, hb_taskRun, 0 );
#endif
return pTask;
}
#if ! defined( HB_HAS_UCONTEXT )
static void hb_taskStart( void )
{
static jmp_buf context;
s_currTask->state = TASK_RUNNING;
/* create new execution context and initialize its private stack */
if( setjmp( context ) == 0 )
{
HB_TASK_STACK_INIT( context, s_currTask->stack + s_currTask->stack_size );
/* switch to new stack */
longjmp( context, 1 );
}
hb_taskRun();
/* unreachable code */
}
#endif
/* initialize task switching, create and register main task structure */
void hb_taskInit( void )
{
if( s_iTaskID == 0 )
{
s_mainTask = s_currTask = ( PHB_TASKINFO ) hb_xgrabz( sizeof( HB_TASKINFO ) );
/* main task uses default application stack */
s_currTask->id = ++s_iTaskID;
s_currTask->state = TASK_RUNNING;
hb_taskLink( &s_taskList, s_currTask );
}
}
/* uninitialize task switching, release main task structure */
void hb_taskExit( void )
{
if( s_mainTask )
{
s_mainTask->state = TASK_ZOMBIE;
hb_taskFree( s_mainTask );
s_mainTask = s_currTask = NULL;
s_iTaskID = 0;
/* release all mutexes */
while( s_mutexList )
{
PHB_TASKMTX pMutex = s_mutexList;
s_mutexList = pMutex->next;
hb_xfree( pMutex );
}
/* release all conditional variables */
while( s_condList )
{
PHB_TASKCOND pCond = s_condList;
s_condList = pCond->next;
hb_xfree( pCond );
}
}
}
/* return main task pointer */
void * hb_taskMain( void )
{
return s_mainTask;
}
/* return current task pointer */
void * hb_taskSelf( void )
{
return s_currTask;
}
/* return given task number */
int hb_taskID( void * pTask )
{
return ( ( PHB_TASKINFO ) pTask )->id;
}
/* get current task user data */
void * hb_taskGetData( void )
{
return s_currTask->data;
}
/* set current task user data */
void hb_taskSetData( void * pData )
{
s_currTask->data = pData;
}
/* get given task user data */
void * hb_taskRestoreData( void * pTask )
{
return ( ( PHB_TASKINFO ) pTask )->data;
}
/* set given task user data */
void hb_taskSaveData( void * pTask, void * pData )
{
( ( PHB_TASKINFO ) pTask )->data = pData;
}
/* get result of task execution */
void * hb_taskResult( void * pTask )
{
return ( ( PHB_TASKINFO ) pTask )->result;
}
void hb_taskSleep( unsigned long ulMilliSec )
{
if( ulMilliSec > 0 )
hb_taskSetSleep( s_currTask, ulMilliSec );
hb_taskYield();
}
/* switch execution context to next task */
void hb_taskYield( void )
{
PHB_TASKINFO pTask = s_currTask->pNext;
while( pTask != s_currTask )
{
if( pTask->state == TASK_RUNNING || pTask->state == TASK_INIT )
break;
else if( pTask->state == TASK_ZOMBIE )
{
PHB_TASKINFO pFree = pTask;
pTask = pTask->pNext;
hb_taskFree( pFree );
}
else
{
if( pTask->state == TASK_SLEEPING && pTask == s_taskSleep )
{
if( s_taskSleep->wakeup == HB_TASK_NO_DELAY ||
( s_taskSleep->wakeup != HB_TASK_INFINITE_DELAY &&
s_taskSleep->wakeup <= hb_taskTimeStop( 0 ) ) )
{
s_taskSleep = pTask->pSleepNext;
pTask->state = TASK_RUNNING;
break;
}
}
pTask = pTask->pNext;
}
}
if( pTask->state != TASK_RUNNING && pTask->state != TASK_INIT )
{
if( s_taskSleep && s_taskSleep->wakeup != HB_TASK_INFINITE_DELAY )
{
hb_taskFreeze( s_taskSleep->wakeup );
pTask = s_taskSleep;
s_taskSleep = pTask->pSleepNext;
pTask->state = TASK_RUNNING;
}
else
hb_errInternal( HB_EI_ERRUNRECOV, "DEADLOCK", NULL, NULL );
}
hb_taskResume( pTask );
}
void hb_taskSheduler( void )
{
/* The cost of accessing time in many systems is huge and causes
* noticeable slowness so we not use it and switch task very often
* on each call to hb_taskSheduler(). It also reduces the total
* performance though the overhead is smaller.
* The problem can be resolved in many system by chip access to some
* counter updated by interrupt.
* In DJGPP clock() seems to be such chip counter.
*/
#if defined( __DJGPP__ )
static clock_t s_timer;
clock_t timer = clock();
if( s_timer != timer )
{
s_timer = timer;
hb_taskYield();
}
#else
hb_taskYield();
#endif
}
/* suspend current task execution */
void hb_taskSuspend( void )
{
s_currTask->state = TASK_SUSPEND;
/* switch to next active task */
hb_taskYield();
}
/* resume given task execution */
/* TODO: do not start task immediately */
void hb_taskResume( void * pTaskPtr )
{
PHB_TASKINFO pTask = ( PHB_TASKINFO ) pTaskPtr;
if( s_currTask != pTask )
{
switch( pTask->state )
{
#if ! defined( HB_HAS_UCONTEXT )
case TASK_INIT:
/* save current execution context */
if( setjmp( s_currTask->context ) == 0 )
{
s_currTask = pTask;
hb_taskStart();
/* unreachable code */
}
break;
#endif
case TASK_SLEEPING:
hb_taskWakeUp( pTask );
/* no break */
#if defined( HB_HAS_UCONTEXT )
case TASK_INIT:
#endif
case TASK_SUSPEND:
pTask->state = TASK_RUNNING;
/* no break */
case TASK_RUNNING:
#if defined( HB_HAS_UCONTEXT )
{
PHB_TASKINFO pCurrTask = s_currTask;
s_currTask = pTask;
/* save current execution context and switch to the new one */
swapcontext( &pCurrTask->context, &pTask->context );
}
#else
/* save current execution context */
if( setjmp( s_currTask->context ) == 0 )
{
s_currTask = pTask;
/* switch execution context */
longjmp( pTask->context, 1 );
/* unreachable code */
}
#endif
break;
case TASK_DONE:
break;
case TASK_ZOMBIE:
/* It should not happen - it's bug in user code */
hb_errInternal( HB_EI_ERRUNRECOV, "TaskResume: zombie", NULL, NULL );
/*
default:
hb_errInternal( HB_EI_ERRUNRECOV, "TaskResume: corrupt", NULL, NULL );
*/
}
}
}
/* create new task */
void * hb_taskCreate( void * ( *start )( void * ), void * cargo, long stack_size )
{
PHB_TASKINFO pTask;
pTask = hb_taskNew( stack_size );
pTask->start = start;
pTask->cargo = cargo;
hb_taskLink( &s_taskList, pTask );
return pTask;
}
/* destroy given task */
void hb_taskDestroy( void * pTaskPtr )
{
PHB_TASKINFO pTask = ( PHB_TASKINFO ) pTaskPtr;
if( pTask != s_mainTask )
{
pTask->detached = HB_TRUE;
if( pTask->state == TASK_ZOMBIE || pTask->state == TASK_DONE )
hb_taskFree( pTask );
else
hb_taskFinalize( pTask );
}
}
/* wait for given task termination */
int hb_taskJoin( void * pTaskPtr, unsigned long ulMilliSec, void ** pResult )
{
PHB_TASKINFO pTask = ( PHB_TASKINFO ) pTaskPtr;
int result = 0;
if( pTask != s_mainTask && pTask != s_currTask )
{
if( ( pTask->state == TASK_INIT || pTask->state == TASK_RUNNING ) &&
ulMilliSec > 0 )
{
s_currTask->joining = pTask;
pTask->joiners++;
hb_taskSleep( ulMilliSec );
if( s_currTask->joining )
{
s_currTask->joining = NULL;
pTask->joiners--;
}
}
if( pTask->state == TASK_DONE )
{
if( pResult )
*pResult = pTask->result;
pTask->state = TASK_ZOMBIE;
hb_taskFree( pTask );
result = 1;
}
}
return result;
}
/* detach given task - it will be removed automatically */
void hb_taskDetach( void * pTask )
{
( ( PHB_TASKINFO ) pTask )->detached = HB_TRUE;
}
/* current task quit */
void hb_taskQuit( void * result )
{
if( s_currTask != s_mainTask )
{
s_currTask->result = result;
hb_taskFinalize( s_currTask );
/* switch to next active task */
hb_taskYield();
/* unreachable code */
}
}
/* (try to) lock given mutex */
int hb_taskLock( void ** pMutexPtr, unsigned long ulMilliSec )
{
PHB_TASKMTX pMutex;
if( s_iTaskID == 0 )
return 0;
if( *pMutexPtr == NULL )
*pMutexPtr = ( void * ) hb_taskMutexNew();
pMutex = ( PHB_TASKMTX ) *pMutexPtr;
if( pMutex->count == 0 )
{
s_currTask->locked++;
pMutex->task = s_currTask;
return ++pMutex->count;
}
else if( pMutex->task == s_currTask )
{
return ++pMutex->count;
}
if( ulMilliSec )
{
PHB_TASKINFO * pLockers = &pMutex->lockers;
while( *pLockers )
pLockers = &( *pLockers )->pBlockNext;
*pLockers = s_currTask;
s_currTask->pBlockNext = NULL;
s_currTask->locking = pMutex;
hb_taskSleep( ulMilliSec );
if( s_currTask->locking )
{
pLockers = &pMutex->lockers;
while( *pLockers )
{
if( *pLockers == s_currTask )
{
*pLockers = s_currTask->pBlockNext;
s_currTask->locking = NULL;
break;
}
else
pLockers = &( *pLockers )->pBlockNext;
}
#ifdef HB_TASK_DEBUG
if( s_currTask->locking )
hb_errInternal( HB_EI_ERRUNRECOV, "TaskLock: dummy lock", NULL, NULL );
#endif
}
}
return pMutex->task == s_currTask ? pMutex->count : 0;
}
/* unlock given mutex */
void hb_taskUnlock( void ** pMutexPtr )
{
PHB_TASKMTX pMutex = ( PHB_TASKMTX ) *pMutexPtr;
if( pMutex && pMutex->task == s_currTask )
{
if( --pMutex->count == 0 )
{
pMutex->task = NULL;
s_currTask->locked--;
if( hb_taskSwitchLock( pMutex ) )
hb_taskYield();
}
}
}
void hb_taskSignal( void ** pCondPtr )
{
PHB_TASKCOND pCond;
if( *pCondPtr == NULL )
*pCondPtr = ( void * ) hb_taskCondNew();
pCond = ( PHB_TASKCOND ) *pCondPtr;
if( pCond->waiters )
{
PHB_TASKINFO * pLockers = &pCond->mutex->lockers, pTask;
while( *pLockers )
pLockers = &( *pLockers )->pBlockNext;
pTask = pCond->waiters;
#ifdef HB_TASK_DEBUG
if( pTask->waiting != pCond )
hb_errInternal( HB_EI_ERRUNRECOV, "TaskSignal: broken cond", NULL, NULL );
#endif
pTask->waiting = NULL;
pCond->waiters = pTask->pWaitNext;
pTask->locking = pCond->mutex;
pTask->pBlockNext = NULL;
*pLockers = pTask;
if( pCond->mutex->count == 0 )
hb_taskSwitchLock( pCond->mutex );
}
}
void hb_taskBroadcast( void ** pCondPtr )
{
PHB_TASKCOND pCond;
if( *pCondPtr == NULL )
*pCondPtr = ( void * ) hb_taskCondNew();
pCond = ( PHB_TASKCOND ) *pCondPtr;
if( pCond->waiters )
{
PHB_TASKINFO * pLockers = &pCond->mutex->lockers;
while( *pLockers )
pLockers = &( *pLockers )->pBlockNext;
do
{
PHB_TASKINFO pTask = pCond->waiters;
#ifdef HB_TASK_DEBUG
if( pTask->waiting != pCond )
hb_errInternal( HB_EI_ERRUNRECOV, "TaskBroadcast: broken cond", NULL, NULL );
#endif
pTask->waiting = NULL;
pCond->waiters = pTask->pWaitNext;
pTask->locking = pCond->mutex;
pTask->pBlockNext = NULL;
*pLockers = pTask;
pLockers = &pTask->pBlockNext;
}
while( pCond->waiters );
if( pCond->mutex->count == 0 )
hb_taskSwitchLock( pCond->mutex );
}
}
int hb_taskWait( void ** pCondPtr, void ** pMutexPtr, unsigned long ulMilliSec )
{
PHB_TASKINFO * pWaiters;
PHB_TASKMTX pMutex = ( PHB_TASKMTX ) *pMutexPtr;
PHB_TASKCOND pCond;
int iCount;
if( pMutex == NULL )
hb_errInternal( HB_EI_ERRUNRECOV, "TaskWait: no mutex", NULL, NULL );
if( pMutex->count == 0 || pMutex->task != s_currTask )
hb_errInternal( HB_EI_ERRUNRECOV, "TaskWait: no mutex lock", NULL, NULL );
if( *pCondPtr == NULL )
*pCondPtr = ( void * ) hb_taskCondNew();
pCond = ( PHB_TASKCOND ) *pCondPtr;
/* POSIX threads have such condition */
if( pCond->waiters && pCond->mutex != pMutex )
hb_errInternal( HB_EI_ERRUNRECOV, "TaskWait: wrong mutex", NULL, NULL );
/* add task to conditional variable waiting queue */
pWaiters = &pCond->waiters;
while( *pWaiters )
pWaiters = &( *pWaiters )->pWaitNext;
*pWaiters = s_currTask;
s_currTask->pWaitNext = NULL;
s_currTask->waiting = pCond;
/* unlock mutex with recursive locks if any */
pCond->mutex = pMutex;
iCount = pMutex->count;
pMutex->task = NULL;
pMutex->count = 0;
s_currTask->locked--;
hb_taskSwitchLock( pMutex );
hb_taskSleep( ulMilliSec );
if( ! hb_taskLock( pMutexPtr, HB_TASK_INFINITE_WAIT ) )
hb_errInternal( HB_EI_ERRUNRECOV, "TaskWait: lock fail", NULL, NULL );
pMutex->count = iCount;
if( s_currTask->waiting )
{
pWaiters = &pCond->waiters;
while( *pWaiters )
{
if( *pWaiters == s_currTask )
{
*pWaiters = s_currTask->pWaitNext;
s_currTask->waiting = NULL;
break;
}
else
pWaiters = &( *pWaiters )->pWaitNext;
}
#ifdef HB_TASK_DEBUG
if( s_currTask->waiting )
hb_errInternal( HB_EI_ERRUNRECOV, "TaskWait: dummy cond", NULL, NULL );
#endif
return 0;
}
else
return 1;
}
void hb_taskDestroyMutex( void ** pMutexPtr )
{
PHB_TASKMTX pMutex = ( PHB_TASKMTX ) *pMutexPtr;
if( pMutex )
{
PHB_TASKMTX * pMutexLst = &s_mutexList;
while( *pMutexLst )
{
if( *pMutexLst == pMutex )
{
*pMutexLst = pMutex->next;
if( pMutex->count )
hb_errInternal( HB_EI_ERRUNRECOV, "TaskDestroyMutex: locked", NULL, NULL );
else if( pMutex->lockers )
hb_errInternal( HB_EI_ERRUNRECOV, "TaskDestroyMutex: lockers", NULL, NULL );
hb_xfree( pMutex );
return;
}
pMutexLst = &( *pMutexLst )->next;
}
hb_errInternal( HB_EI_ERRUNRECOV, "TaskDestroyMutex: not a mutex", NULL, NULL );
}
}
void hb_taskDestroyCond( void ** pCondPtr )
{
PHB_TASKCOND pCond = ( PHB_TASKCOND ) *pCondPtr;
if( pCond )
{
PHB_TASKCOND * pCondLst = &s_condList;
while( *pCondLst )
{
if( *pCondLst == pCond )
{
*pCondLst = pCond->next;
if( pCond->waiters )
hb_errInternal( HB_EI_ERRUNRECOV, "TaskDestroyCond: waiters", NULL, NULL );
hb_xfree( pCond );
return;
}
pCondLst = &( *pCondLst )->next;
}
hb_errInternal( HB_EI_ERRUNRECOV, "TaskDestroyCond: not a cond", NULL, NULL );
}
}