Files
harbour-core/contrib/xhb/bkgtsks.c
Viktor Szakats 5a2a287752 2017-09-08 16:00 UTC Viktor Szakats (vszakats users.noreply.github.com)
* *
    * partial sync with the 3.4 fork codebase. These are the things
      synces for the most part:
      - copyright headers
      - grammar/typos in comments and some readmes
      - comment/whitespace/decorations
      - variable scoping in C files
      - DO CASE/SWITCH and some other alternate syntax usage
      - minimal amount of human readable text in strings
      - minor code updates
      - HB_TRACE() void * casts for pointers and few other changes to
        avoid C compiler warnings
      - various other, minor code cleanups
      - only Harbour/C code/headers were touched in src, utils, contrib,
        include. No 3rd party code, no make files, and with just a few
        exceptions, no 'tests' code was touched.
      - certain components were not touched were 3.4 diverged too much
        already, like f.e. hbmk2, hbssl, hbcurl, hbexpat
      - the goal was that no actual program logic should be altered by
        these changes. Except some possible minor exceptions, any such
        change is probably a bug in this patch.
      It's a massive patch, if you find anything broken after it, please
      open an Issue with the details. Build test was done on macOS.
      The goal is make it easier to see what actual code/logic was changed
      in 3.4 compared to 3.2 and to make patches easier to apply in both
      ways.
2017-09-08 16:25:13 +00:00

458 lines
13 KiB
C

/*
* The background tasks - an extension of idle state
*
* Copyright 2003-2008 Francesco Saverio Giudice <info@fsgiudice.com>
*
* 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 program; see the file LICENSE.txt. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA (or visit https://www.gnu.org/licenses/).
*
* 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.
*
*/
#include "hbapi.h"
#include "hbapiitm.h"
#include "hbstack.h"
#include "hbset.h"
#include "hbvm.h"
#include "hbdate.h"
#include "error.ch"
#if defined( HB_OS_UNIX )
#if defined( HB_OS_DARWIN )
#include <unistd.h> /* We need usleep() in Darwin */
#else
#include <time.h>
#endif
#endif
HB_EXTERN_BEGIN
typedef struct
{
HB_ULONG ulTaskID; /* task identifier */
PHB_ITEM pTask; /* pointer to the task item */
double dSeconds; /* internal - last time this task has gone */
int millisec; /* milliseconds after this task must run */
HB_BOOL bActive; /* task is active ? */
} HB_BACKGROUNDTASK, * PHB_BACKGROUNDTASK;
extern void hb_backgroundRunSingle( HB_ULONG ulID ); /* run a single background routine */
extern void hb_backgroundRunForced( void ); /* run all background routines also if them are not active*/
extern void hb_backgroundRun( void ); /* run all background routines but only if them are active*/
extern void hb_backgroundReset( void ); /* reset internal counter */
extern void hb_backgroundShutDown( void ); /* closes all background tasks */
extern HB_ULONG hb_backgroundAddFunc( PHB_ITEM pBlock, int nMillisec, HB_BOOL bActive ); /* Adds a codeblock or an executable array */
extern PHB_ITEM hb_backgroundDelFunc( HB_ULONG ulID ); /* Deletes a prevuiously added task */
extern PHB_BACKGROUNDTASK hb_backgroundFind( HB_ULONG ulID );
extern HB_BOOL hb_backgroundActive( HB_ULONG ulID, HB_BOOL bActive );
extern int hb_backgroundTime( HB_ULONG ulID, int nMillisec );
HB_EXTERN_END
#ifndef HB_THREAD_SUPPORT
/* Task ID */
static HB_ULONG s_ulBackgroundID = 0;
/* list of background tasks
* A pointer into an array of pointers to items with a codeblock
*/
static PHB_BACKGROUNDTASK * s_pBackgroundTasks = NULL;
static HB_BOOL s_bEnabled = HB_FALSE;
/* flag to prevent recursive calls of hb_backgroundRun() */
static HB_BOOL s_bIamBackground = HB_FALSE;
/* current task to be executed */
static HB_USHORT s_uiBackgroundTask = 0;
/* number of tasks in the list */
static HB_USHORT s_uiBackgroundMaxTask = 0;
#else
#define s_ulBackgroundID HB_VM_STACK.ulBackgroundID
#define s_pBackgroundTasks ( HB_VM_STACK.pBackgroundTasks )
#define s_bIamBackground HB_VM_STACK.bIamBackground
#define s_uiBackgroundTask HB_VM_STACK.uiBackgroundTask
#define s_uiBackgroundMaxTask HB_VM_STACK.uiBackgroundMaxTask
#endif
/* ------------------------ C LEVEL ------------------------------ */
HB_ULONG hb_backgroundAddFunc( PHB_ITEM pBlock, int nMillisec, HB_BOOL bActive )
{
PHB_BACKGROUNDTASK pBkgTask;
/* store a copy of passed codeblock
*/
pBkgTask = ( PHB_BACKGROUNDTASK ) hb_xgrab( sizeof( HB_BACKGROUNDTASK ) );
pBkgTask->pTask = hb_itemNew( pBlock );
pBkgTask->dSeconds = hb_dateSeconds();
pBkgTask->millisec = nMillisec;
pBkgTask->bActive = bActive;
if( ! s_pBackgroundTasks )
{
pBkgTask->ulTaskID = s_ulBackgroundID = 1;
s_pBackgroundTasks = ( PHB_BACKGROUNDTASK * ) hb_xgrab( sizeof( HB_BACKGROUNDTASK ) );
}
else
{
pBkgTask->ulTaskID = ++s_ulBackgroundID;
if( pBkgTask->ulTaskID == 0 ) /* the counter reach maximum value */
{
/* find unique task ID and set the counter for next */
int iTask = 0;
while( iTask < s_uiBackgroundMaxTask )
{
if( s_pBackgroundTasks[ iTask ]->ulTaskID == pBkgTask->ulTaskID )
{
pBkgTask->ulTaskID++;
/* This list is unsorted so we have to scan from the begining again */
iTask = 0;
}
else
{
iTask++;
if( s_ulBackgroundID < pBkgTask->ulTaskID )
s_ulBackgroundID = pBkgTask->ulTaskID;
}
}
}
s_pBackgroundTasks = ( PHB_BACKGROUNDTASK * ) hb_xrealloc( s_pBackgroundTasks, sizeof( HB_BACKGROUNDTASK ) * ( s_uiBackgroundMaxTask ) );
}
s_pBackgroundTasks[ s_uiBackgroundMaxTask ] = pBkgTask;
++s_uiBackgroundMaxTask;
return pBkgTask->ulTaskID;
}
/* RUN all tasks defined in background state but only if SET BACKGROUND TASKS is ON*/
void hb_backgroundRun( void )
{
PHB_BACKGROUNDTASK pBkgTask;
if( ! s_bIamBackground && s_bEnabled )
{
s_bIamBackground = HB_TRUE;
if( s_uiBackgroundTask < s_uiBackgroundMaxTask )
{
double dCurrSeconds = hb_dateSeconds();
pBkgTask = ( PHB_BACKGROUNDTASK ) s_pBackgroundTasks[ s_uiBackgroundTask ];
/* check if hb_dateSeconds() is lower than pBkgTask->dSeconds, if so midnight is reached */
if( ! ( pBkgTask->dSeconds ) || dCurrSeconds < pBkgTask->dSeconds )
pBkgTask->dSeconds = dCurrSeconds;
/* Check if a task can run */
if( pBkgTask->bActive &&
( pBkgTask->millisec == 0 ||
( ( ( hb_dateSeconds() - pBkgTask->dSeconds ) * 1000 ) >= pBkgTask->millisec )
)
)
{
hb_itemRelease( hb_itemDo( pBkgTask->pTask, 0 ) );
pBkgTask->dSeconds = hb_dateSeconds();
}
++s_uiBackgroundTask;
}
else
{
if( s_uiBackgroundMaxTask &&
s_uiBackgroundTask == s_uiBackgroundMaxTask )
s_uiBackgroundTask = 0;
}
s_bIamBackground = HB_FALSE;
}
}
/* RUN all tasks also if SET BACKGROUND TASKS is OFF */
void hb_backgroundRunForced( void )
{
HB_BOOL bOldSet = s_bEnabled;
s_bEnabled = HB_TRUE;
hb_backgroundRun();
s_bEnabled = bOldSet;
}
/* RUN only one tasks, intentionally no check if bacground are active is done */
void hb_backgroundRunSingle( HB_ULONG ulID )
{
PHB_BACKGROUNDTASK pBkgTask;
if( ! s_bIamBackground )
{
s_bIamBackground = HB_TRUE;
pBkgTask = hb_backgroundFind( ulID );
if( pBkgTask )
hb_itemRelease( hb_itemDo( pBkgTask->pTask, 0 ) );
s_bIamBackground = HB_FALSE;
}
}
/* reset background counter to 0 */
void hb_backgroundReset( void )
{
if( s_uiBackgroundTask == s_uiBackgroundMaxTask )
s_uiBackgroundTask = 0;
}
/* close all active background tasks on program exit */
void hb_backgroundShutDown( void )
{
if( s_pBackgroundTasks )
{
do
{
PHB_BACKGROUNDTASK pBkgTask;
pBkgTask = s_pBackgroundTasks[ --s_uiBackgroundMaxTask ];
hb_itemRelease( pBkgTask->pTask );
pBkgTask->pTask = NULL;
hb_xfree( pBkgTask );
}
while( s_uiBackgroundMaxTask );
hb_xfree( s_pBackgroundTasks );
s_pBackgroundTasks = NULL;
}
}
/* caller have to free return ITEM by hb_itemRelease() if it's not NULL */
PHB_ITEM hb_backgroundDelFunc( HB_ULONG ulID )
{
int iTask;
PHB_BACKGROUNDTASK pBkgTask;
PHB_ITEM pItem = NULL;
HB_BOOL bOldSet = s_bEnabled;
s_bEnabled = HB_FALSE;
iTask = 0;
while( iTask < s_uiBackgroundMaxTask )
{
pBkgTask = s_pBackgroundTasks[ iTask ];
if( ulID == pBkgTask->ulTaskID )
{
pItem = pBkgTask->pTask;
hb_xfree( pBkgTask );
if( --s_uiBackgroundMaxTask )
{
if( iTask != s_uiBackgroundMaxTask )
{
memmove( &s_pBackgroundTasks[ iTask ], &s_pBackgroundTasks[ iTask + 1 ],
sizeof( HB_BACKGROUNDTASK ) * ( s_uiBackgroundMaxTask - iTask ) );
}
s_pBackgroundTasks = ( PHB_BACKGROUNDTASK * ) hb_xrealloc( s_pBackgroundTasks, sizeof( HB_BACKGROUNDTASK ) * s_uiBackgroundMaxTask );
}
else
{
hb_xfree( s_pBackgroundTasks );
s_pBackgroundTasks = NULL;
}
/* Pitem has now a valid value */
break;
}
++iTask;
}
s_bEnabled = bOldSet;
return pItem;
}
/* Find a task */
PHB_BACKGROUNDTASK hb_backgroundFind( HB_ULONG ulID )
{
int iTask;
PHB_BACKGROUNDTASK pBkgTask;
iTask = 0;
while( iTask < s_uiBackgroundMaxTask )
{
pBkgTask = s_pBackgroundTasks[ iTask ];
if( ulID == pBkgTask->ulTaskID )
return pBkgTask;
++iTask;
}
return NULL;
}
/* Set task as active */
HB_BOOL hb_backgroundActive( HB_ULONG ulID, HB_BOOL bActive )
{
PHB_BACKGROUNDTASK pBkgTask;
HB_BOOL bOldState = HB_FALSE;
pBkgTask = hb_backgroundFind( ulID );
if( pBkgTask )
{
bOldState = pBkgTask->bActive;
pBkgTask->bActive = bActive;
}
return bOldState;
}
/* Set task time */
int hb_backgroundTime( HB_ULONG ulID, int nMillisec )
{
PHB_BACKGROUNDTASK pBkgTask;
int nOldState = 0;
pBkgTask = hb_backgroundFind( ulID );
if( pBkgTask )
{
nOldState = pBkgTask->millisec;
pBkgTask->millisec = nMillisec;
}
return nOldState;
}
/* ------------------------ PRG LEVEL ------------------------------ */
/* forces to run Background functions */
HB_FUNC( HB_BACKGROUNDRUN )
{
if( s_pBackgroundTasks )
{
if( HB_ISNUM( 1 ) )
{
/* TODO: access to pointers from harbour code */
hb_backgroundRunSingle( hb_parnl( 1 ) );
}
else
hb_backgroundRun();
}
}
/* forces to run Background functions */
HB_FUNC( HB_BACKGROUNDRUNFORCED )
{
if( HB_ISNUM( 1 ) )
hb_backgroundRunSingle( hb_parnl( 1 ) );
else
hb_backgroundRunForced();
}
/* call from user code to reset Background state */
HB_FUNC( HB_BACKGROUNDRESET )
{
hb_backgroundReset();
}
/* add a new background task and return its handle */
HB_FUNC( HB_BACKGROUNDADD )
{
PHB_ITEM pBlock = hb_param( 1, HB_IT_ANY );
PHB_ITEM pMillisec = hb_param( 2, HB_IT_NUMERIC );
PHB_ITEM pActive = hb_param( 3, HB_IT_LOGICAL );
if( HB_IS_BLOCK( pBlock ) || HB_IS_ARRAY( pBlock ) )
{
hb_retnl( hb_backgroundAddFunc( pBlock,
( pMillisec == NULL ? 0 : hb_itemGetNI( pMillisec ) ),
( pActive == NULL ? HB_TRUE : hb_itemGetL( pActive ) )
) );
}
else
hb_retnl( -1 ); /* error - a codeblock is required */
}
/* Delete a task with given handle and return a codeblock with this task */
HB_FUNC( HB_BACKGROUNDDEL )
{
PHB_ITEM pItem = NULL;
if( s_pBackgroundTasks && HB_ISNUM( 1 ) )
{
/* TODO: access to pointers from harbour code */
pItem = hb_backgroundDelFunc( hb_parnl( 1 ) );
}
if( pItem )
hb_itemReturnRelease( pItem ); /* return a codeblock */
}
/* Set a task as active or not */
HB_FUNC( HB_BACKGROUNDACTIVE )
{
HB_BOOL bOldActive = HB_FALSE;
if( s_pBackgroundTasks && HB_ISNUM( 1 ) )
{
/* TODO: access to pointers from harbour code */
bOldActive = hb_backgroundActive( hb_parnl( 1 ), hb_parldef( 2, HB_TRUE ) );
}
hb_retl( bOldActive ); /* return old active value */
}
/* Set milliseconds after which a task will be executed */
HB_FUNC( HB_BACKGROUNDTIME )
{
int nOldMillisec = 0;
if( s_pBackgroundTasks && HB_ISNUM( 1 ) )
{
/* TODO: access to pointers from harbour code */
nOldMillisec = hb_backgroundTime( hb_parnl( 1 ), hb_parnidef( 2, 1000 ) );
}
hb_retni( nOldMillisec ); /* return old millisecond value */
}