Files
harbour-core/harbour/src/vm/task.c
Przemyslaw Czerpak 87cff88914 2010-02-20 18:05 UTC+0100 Przemyslaw Czerpak (druzus/at/priv.onet.pl)
* harbour/src/vm/task.c
    ! fixed casting for C++ builds

  * harbour/src/lang/msges.c
    ! added hack for Solaris builds where ES is macro
      TODDO: sync names of lang and corresponding code modules.

  * harbour/src/vm/dynlibhb.c
    * pacified warning

  * harbour/contrib/xpp/xppextrn.ch
  * harbour/contrib/xpp/dbcmdx.c
  * harbour/include/hbextern.ch
  * harbour/src/rdd/dbcmd53.c
    ! moved ordWildSeek() from XPP to HBRDD library.
      I have no idea if XPP has such function or if it's or not
      compatible with ORDWILDSEEK() functions I created for [x]Harbour
    * disable ordCount() and ordWildSeek() functions when HB_CLP_STRICT
      macro is set

  * harbour/include/hbsocket.ch
    + added macro values interface info flags: HB_SOCKET_IFF_*
    + added macro values for interface info array indexes: HB_SOCKET_IFINFO_*

  * harbour/include/hbsocket.h
  * harbour/src/rtl/hbsocket.c
    + added new C function:
         PHB_ITEM hb_socketGetIFaces( int af, HB_BOOL fNoAliases );
      it returns array with existing interfaces description.
      This code was added for non MS-Windows based platforms only.
      Support for MS-Windows has to be added yet.
      Some platforms may not fill all fields in the returned array.
      When some fields are not supported by platform or interface then
      they can contain NIL value.
      On some platforms this code can effectively work only with IP4
      interfaces and IP6 ones will need different implementation.
      TODO: add support for alternative long interface introduced in some
            new systems using 'struct lifreq' with SIOCGLIF* ioctls instead
            of 'struct ifreq' and SIOCGIF*
      TODO: add support for extracting hardware addreses (i.e. ethernet
            macaddresses) for systems which do not support SIOCGIFHWADDR.

      Please test this code with OS2 using OpenWatcom and GCC (old and
      new socket API). I enabled HB_HAS_SOCKADDR_SA_LEN in OS2 builds.
      OpenWatocm OS2 header files have such fields but I do know if it's
      also present in GCC socket API.

  * harbour/include/hbextern.ch
  * harbour/src/rtl/hbinet.c
    + added new PRG function:
         hb_inetIfInfo( [<lNoAliases>] [, <nAddrFamily>] ) -> <aInfo>
      <lNoAliases> is .F. by default so aliases are included
      <nAddrFamily> is HB_SOCKET_AF_INET by default
      <aInfo> is an array in which each entry is also array with the
      following fields:
         HB_SOCKET_IFINFO_FAMILY    1     // adress family
         HB_SOCKET_IFINFO_NAME      2     // interface name
         HB_SOCKET_IFINFO_FLAGS     3     // flags HB_SOCKET_IFF_*
         HB_SOCKET_IFINFO_ADDR      4     // interface address
         HB_SOCKET_IFINFO_NETMASK   5     // subnetmask
         HB_SOCKET_IFINFO_BROADCAST 6     // broadcast address
         HB_SOCKET_IFINFO_P2PADDR   7     // point-to-point address
         HB_SOCKET_IFINFO_HWADDR    8     // hardware address

      Please use this code on different non MS-Windows platforms:

            proc main( noAlias )
               local iface
               ? os(), version()
               ? "interfaces:"
               for each iface in hb_inetIfInfo( !empty( noAlias ), 0 )
                  ? hb_valToExp( iface )
               next
               ?
            return

      and check if it returns correct information about available
      interfaces.
2010-02-20 17:07:00 +00:00

1151 lines
28 KiB
C

/*
* $Id$
*/
/*
* 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://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_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__ )
# 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_LONG_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 ) memset( hb_xgrab( sizeof( HB_TASKINFO ) ), 0,
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 )
memset( hb_xgrab( sizeof( HB_TASKINFO ) ), 0, 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, pCurrTask;
if( s_currTask != pTask )
{
pCurrTask = s_currTask;
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 )
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 );
}
}