Files
harbour-core/tests/speedtst.prg
Przemysław Czerpak 9eec626663 2017-03-22 09:39 UTC+0100 Przemyslaw Czerpak (druzus/at/poczta.onet.pl)
* src/compiler/hbdead.c
  * src/compiler/hbfix.c
  * src/compiler/hblbl.c
  * src/compiler/hbopt.c
  * src/compiler/hbpcode.c
  * src/compiler/hbstripl.c
    * small comment cleanup in PCODE tables

  * include/hberrors.h
  * src/compiler/hbgenerr.c
    + added new compile time error messages for incorrect
      ENDWITH, ENDSWITCH and END SEQUENCE
    * changed error message:
         "CASE or OTHERWISE does not match DO CASE"
      to
         "CASE or OTHERWISE does not match DO CASE or SWITCH"

  * src/compiler/harbour.y
    * if control structure is closed by wrong statement generate
      error and close it. Cl*pper works in similar way so now compile
      time errors are much more close to Clipper in such cases and
      this modification fixes also problem with long list of errors
      reported to the end of function when in fact only one control
      structure was wrongly closed.
    * generate error messages for parts of control structures used
      in wrong context

  * src/compiler/harbour.yyc
  * src/compiler/harbour.yyh
    * regenerated

  * src/compiler/complex.c
    - removed wrongly working old error messages - initially they were
      implemented in old Flex lexer but they couldn't work correctly
      inside any lexer. When I wrote new lexer I simply copied them to
      new code with old broken logic just for backward compatibility.
      Now such messages are generated by grammar parser for all cases
      not only chose ones so I can clean lexer code.
    * set more precisely lexer state for error messages generated by
      grammar parser
    * simplified the code after above modifications

  * tests/speedtst.prg
    ! fixed memory statistic detection
2017-03-22 09:39:47 +01:00

1031 lines
26 KiB
Plaintext

/*
* HVM speed test program
*
* Copyright 2008 Przemyslaw Czerpak <druzus / at / priv.onet.pl>
*
*/
/* Harbour MT functions used in this test */
/*
#xtranslate hb_mutexSubscribe( => mt_mutexSubscribe(
#xtranslate hb_mutexNotify( => mt_mutexNotify(
#xtranslate hb_mutexCreate( => mt_mutexCreate(
#xtranslate hb_threadOnce( => mt_threadOnce(
#xtranslate hb_threadStart( => mt_threadStart(
#xtranslate hb_threadJoin( => mt_threadJoin(
#xtranslate hb_threadWaitForAll( => mt_threadWaitForAll(
*/
#define N_TESTS 56
#define N_LOOPS 1000000
#define ARR_LEN 16
#ifndef __HARBOUR__
#ifndef __XPP__
#ifndef __CLIP__
#ifndef FlagShip
#define __CLIPPER__
#endif
#endif
#endif
#endif
#ifdef __CLIPPER__
/* Clipper does not support multithreading */
#ifndef __ST__
#define __ST__
#endif
/* Clipper does not have function to extract process time */
#xtranslate hb_secondsCPU([<x>]) => seconds()
#endif
#ifdef FlagShip
#define __NO_OBJ_ARRAY__
/* FlagShip does not support multithreading */
#ifndef __ST__
#define __ST__
#endif
#xtranslate hb_secondsCPU([<x>]) => secondsCPU(<x>)
/* the FlagShip version of seconds() returns integer values */
#xtranslate seconds() => fs_seconds()
#endif
#ifdef __XPP__
#define __NO_OBJ_ARRAY__
/* Has xBase++ function to extract process time? */
#xtranslate hb_secondsCPU([<x>]) => seconds()
#endif
#ifdef __CLIP__
#define __NO_OBJ_ARRAY__
/* CLIP version for MT performance testing is not ready yet */
#ifndef __ST__
#define __ST__
#endif
#xtranslate hb_secondsCPU([<x>]) => secondsCPU(<x>)
#endif
#ifdef __XHARBOUR__
/* By default build xHarbour binaries without MT support.
* xHarbour needs separated source code versions for MT and ST mode
* because standard MT functions are not available in ST libraries.
*/
#ifndef __ST__
#ifndef __MT__
#ifndef MT
#ifndef HB_THREAD_SUPPORT
#define __ST__
#endif
#endif
#endif
#endif
#xtranslate hb_secondsCPU([<x>]) => secondsCPU(<x>)
#endif
/* by default create MT version */
#ifndef __MT__
#ifndef __ST__
#define __MT__
#endif
#endif
#command ? => spd_out()
#command ? <xx,...> => spd_out();spd_out(<xx>)
#command ?? <xx,...> => spd_out(<xx>)
#ifdef __HARBOUR__
#ifdef __XHARBOUR__
#define EOL hb_osNewLine()
#else
#define EOL hb_eol()
#endif
#else
#ifndef EOL
#define EOL chr(10)
#endif
#endif
#xcommand TEST <testfunc> ;
[ WITH <locals,...> ] ;
[ STATIC <statics,...> ] ;
[ FIELD <fields,...> ] ;
[ MEMVAR <memvars,...> ] ;
[ PRIVATE <privates,...> ] ;
[ PUBLIC <publics,...> ] ;
[ INIT <init> ] ;
[ EXIT <exit> ] ;
[ INFO <info> ] ;
CODE [ <testExp,...> ] => ;
func <testfunc> ; ;
local time, i:=nil, x:=nil ; ;
[ local <locals> ; ] ;
[ static <statics> ; ] ;
[ field <fields> ; ] ;
[ memvar <memvars> ; ] ;
[ private <privates> ; ] ;
[ public <publics> ; ] ;
[ <init> ; ] ;
time := hb_secondsCPU() ; ;
for i:=1 to N_LOOPS ; ;
[ ( <testExp> ) ; ] ;
next ; ;
time := hb_secondsCPU() - time ; ;
[ <exit> ; ] ;
return { procname() + ": " + iif( <.info.>, <(info)>, #<testExp> ), time }
STATIC s_lStdOut := .F.
#ifdef __HARBOUR__
#ifndef __XHARBOUR__
#include "hbver.ch"
#endif
#endif
#ifdef __HARBOUR__
proc main( ... )
local aParams := hb_aparams()
#else
proc main( _p01, _p02, _p03, _p04, _p05, _p06, _p07, _p08, _p09, _p10, ;
_p11, _p12, _p13, _p14, _p15, _p16, _p17, _p18, _p19, _p20 )
local aParams := ;
asize( { _p01, _p02, _p03, _p04, _p05, _p06, _p07, _p08, _p09, _p10, ;
_p11, _p12, _p13, _p14, _p15, _p16, _p17, _p18, _p19, _p20 }, ;
min( pCount(), 20 ) )
#endif
local nMT, cExclude, lScale, cParam, cMemTests, lSyntax, i, j
Set( _SET_DATEFORMAT, "yyyy-mm-dd" )
lSyntax := lScale := .f.
cMemTests := "030 031 023 025 027 041 042 044 053 054 019 022 032 033 055 056 "
cExclude := ""
nMT := 0
for j := 1 to len( aParams )
cParam := lower( aParams[ j ] )
if cParam = "--thread"
if substr( cParam, 9, 1 ) == "="
if isdigit( substr( cParam, 10, 1 ) )
nMT := val( substr( cParam, 10 ) )
elseif substr( cParam, 10 ) == "all"
nMT := -1
else
lSyntax := .t.
endif
elseif empty( substr( cParam, 9 ) )
nMT := -1
else
lSyntax := .t.
endif
elseif cParam = "--exclude="
if substr( cParam, 11 ) == "mem"
cExclude += cMemTests
else
cExclude += strtran( strtran( strtran( substr( cParam, 11 ), ;
".", " " ), ".", " " ), "/", " " ) + " "
endif
elseif cParam = "--only="
cExclude := ""
if substr( cParam, 8 ) == "mem"
cParam := cMemTests
endif
for i := 1 to N_TESTS
if ! strzero( i, 3 ) $ cParam
cExclude += strzero( i, 3 ) + " "
endif
next
elseif cParam == "--scale"
lScale := .t.
elseif cParam == "--stdout"
s_lStdOut := .t.
else
lSyntax := .t.
endif
if lSyntax
? "Unknown option:", cParam
? "syntax: speedtst [--thread[=<num>]] [--only=<test(s)>] [--exclude=<test(s)>]"
?
return
endif
next
IF ! s_lStdOut
set alternate to ( spd_logfile() ) additive
set alternate on
ENDIF
// set console off
test( nMT, cExclude, lScale )
IF ! s_lStdOut
set alternate off
set alternate to
ENDIF
return
STATIC PROCEDURE spd_out( p1, p2, p3, p4, p5, p6 )
LOCAL nPCount := PCount()
IF s_lStdOut
DO CASE
CASE nPCount == 0 ; OutStd( EOL )
CASE nPCount == 1 ; OutStd( p1 )
CASE nPCount == 2 ; OutStd( p1, p2 )
CASE nPCount == 3 ; OutStd( p1, p2, p3 )
CASE nPCount == 4 ; OutStd( p1, p2, p3, p4 )
CASE nPCount == 5 ; OutStd( p1, p2, p3, p4, p5 )
CASE nPCount == 6 ; OutStd( p1, p2, p3, p4, p5, p6 )
ENDCASE
ELSE
DO CASE
CASE nPCount == 0 ; QOut()
CASE nPCount == 1 ; QQOut( p1 )
CASE nPCount == 2 ; QQOut( p1, p2 )
CASE nPCount == 3 ; QQOut( p1, p2, p3 )
CASE nPCount == 4 ; QQOut( p1, p2, p3, p4 )
CASE nPCount == 5 ; QQOut( p1, p2, p3, p4, p5 )
CASE nPCount == 6 ; QQOut( p1, p2, p3, p4, p5, p6 )
ENDCASE
ENDIF
RETURN
STATIC FUNCTION spd_logfile()
#ifndef __HARBOUR__
RETURN "speedtst.txt"
#else
LOCAL cName
hb_FNameSplit( hb_ArgV( 0 ),, @cName )
RETURN hb_FNameMerge( , cName, ".txt" )
#endif
/*** TESTS ***/
TEST t000 INFO "empty loop overhead" CODE
TEST t001 WITH L_C:=dtos(date()) CODE x := L_C
TEST t002 WITH L_N:=112345.67 CODE x := L_N
TEST t003 WITH L_D:=date() CODE x := L_D
TEST t004 STATIC s_once := NIL, S_C ;
INIT hb_threadOnce( @s_once, {|| S_C := dtos( date() ) } ) ;
CODE x := S_C
TEST t005 STATIC s_once := NIL, S_N ;
INIT hb_threadOnce( @s_once, {|| S_N := 112345.67 } ) ;
CODE x := S_N
TEST t006 STATIC s_once := NIL, S_D ;
INIT hb_threadOnce( @s_once, {|| S_D := date() } ) ;
CODE x := S_D
TEST t007 MEMVAR M_C ;
PRIVATE M_C := dtos( date() ) ;
CODE x := M->M_C
TEST t008 MEMVAR M_N ;
PRIVATE M_N := 112345.67 ;
CODE x := M->M_N
TEST t009 MEMVAR M_D ;
PRIVATE M_D := date() ;
CODE x := M->M_D
TEST t010 STATIC s_once := NIL ;
MEMVAR P_C ;
PUBLIC P_C ;
INIT hb_threadOnce( @s_once, {|| M->P_C := dtos( date() ) } ) ;
CODE x := M->P_C
TEST t011 STATIC s_once := NIL ;
MEMVAR P_N ;
PUBLIC P_N ;
INIT hb_threadOnce( @s_once, {|| M->P_N := 112345.67 } ) ;
CODE x := M->P_N
TEST t012 STATIC s_once := NIL ;
MEMVAR P_D ;
PUBLIC P_D ;
INIT hb_threadOnce( @s_once, {|| M->P_D := date() } ) ;
CODE x := M->P_D
TEST t013 FIELD F_C ;
INIT use_dbsh() EXIT close_db() ;
CODE x := F_C
TEST t014 FIELD F_N ;
INIT use_dbsh() EXIT close_db() ;
CODE x := F_N
TEST t015 FIELD F_D ;
INIT use_dbsh() EXIT close_db() ;
CODE x := F_D
TEST t016 WITH o := errorNew() CODE x := o:Args
TEST t017 WITH o := errorArray() CODE x := o[2]
TEST t018 CODE round( i / 1000, 2 )
TEST t019 CODE str( i / 1000 )
TEST t020 WITH s := stuff( dtos( date() ), 7, 0, "." ) CODE val( s )
TEST t021 WITH a := afill( array( ARR_LEN ), ;
stuff( dtos( date() ), 7, 0, "." ) ) ;
CODE val( a [ i % ARR_LEN + 1 ] )
TEST t022 WITH d := date() CODE dtos( d - i % 10000 )
TEST t023 CODE eval( {|| i % ARR_LEN } )
TEST t024 WITH bc := {|| i % ARR_LEN } ;
INFO eval( bc := {|| i % ARR_LEN } ) ;
CODE eval( bc )
TEST t025 CODE eval( {| x | x % ARR_LEN }, i )
TEST t026 WITH bc := {| x | x % ARR_LEN } ;
INFO eval( bc := {| x | x % ARR_LEN }, i ) ;
CODE eval( bc, i )
TEST t027 CODE eval( {| x | f1( x ) }, i )
TEST t028 WITH bc := {| x | f1( x ) } ;
INFO eval( bc := {| x | f1( x ) }, i ) ;
CODE eval( bc, i )
TEST t029 WITH bc := mkBlock( "{| x | f1( x ) }" ) ;
INFO eval( bc := &("{| x | f1( x ) }"), i ) ;
CODE eval( bc, i )
TEST t030 CODE x := &( 'f1(' + str(i) + ')' )
TEST t031 WITH bc CODE bc := &( '{| x |f1(x)}' ), eval( bc, i )
TEST t032 CODE x := valtype( x ) + valtype( i )
TEST t033 WITH a := afill( array( ARR_LEN ), ;
stuff( dtos( date() ), 7, 0, "." ) ) ;
CODE x := strzero( i % 100, 2 ) $ a[ i % ARR_LEN + 1 ]
TEST t034 WITH a := array( ARR_LEN ), s := dtos( date() ) ;
INIT aeval( a, {| x, i | a[i] := left( s + s, i ), x } ) ;
CODE x := a[ i % ARR_LEN + 1 ] == s
TEST t035 WITH a := array( ARR_LEN ), s := dtos( date() ) ;
INIT aeval( a, {| x, i | a[i] := left( s + s, i ), x } ) ;
CODE x := a[ i % ARR_LEN + 1 ] = s
TEST t036 WITH a := array( ARR_LEN ), s := dtos( date() ) ;
INIT aeval( a, {| x, i | a[i] := left( s + s, i ), x } ) ;
CODE x := a[ i % ARR_LEN + 1 ] >= s
TEST t037 WITH a := array( ARR_LEN ), s := dtos( date() ) ;
INIT aeval( a, {| x, i | a[i] := left( s + s, i ), x } ) ;
CODE x := a[ i % ARR_LEN + 1 ] <= s
TEST t038 WITH a := array( ARR_LEN ), s := dtos( date() ) ;
INIT aeval( a, {| x, i | a[i] := left( s + s, i ), x } ) ;
CODE x := a[ i % ARR_LEN + 1 ] < s
TEST t039 WITH a := array( ARR_LEN ), s := dtos( date() ) ;
INIT aeval( a, {| x, i | a[i] := left( s + s, i ), x } ) ;
CODE x := a[ i % ARR_LEN + 1 ] > s
TEST t040 WITH a := array( ARR_LEN ) ;
INIT aeval( a, {| x, i | a[i] := i, x } ) ;
CODE ascan( a, i % ARR_LEN )
TEST t041 WITH a := array( ARR_LEN ) ;
INIT aeval( a, {| x, i | a[i] := i, x } ) ;
CODE ascan( a, {| x | x == i % ARR_LEN } )
TEST t042 WITH a := {}, a2 := { 1, 2, 3 }, bc := {| x | f1(x) }, ;
s := dtos( date() ), s2 := "static text" ;
CODE iif( i%1000==0, a:={}, ) , aadd(a,{i,1,.t.,s,s2,a2,bc})
TEST t043 WITH a := {} CODE x := a
TEST t044 CODE x := {}
TEST t045 CODE f0()
TEST t046 CODE f1( i )
TEST t047 WITH c := dtos( date() ) ;
INFO "f2( c[1...8] )" ;
CODE f2( c )
TEST t048 WITH c := replicate( dtos( date() ), 5000 ) ;
INFO "f2( c[1...40000] )" ;
CODE f2( c )
TEST t049 WITH c := replicate( dtos( date() ), 5000 ) ;
INFO "f2( @c[1...40000] )" ;
CODE f2( @c )
TEST t050 WITH c := replicate( dtos( date() ), 5000 ), c2 ;
INFO "f2( @c[1...40000] ), c2 := c" ;
CODE f2( @c ), c2 := c
TEST t051 WITH a := {}, a2 := { 1, 2, 3 }, bc := {| x | f1(x) }, ;
s := dtos( date() ), s2 := "static text", n := 1.23 ;
CODE f3( a, a2, s, i, s2, bc, i, n, x )
TEST t052 WITH a := { 1, 2, 3 } CODE f2( a )
TEST t053 CODE x := f4()
TEST t054 CODE x := f5()
TEST t055 CODE x := space(16)
TEST t056 WITH c := dtos( date() ) CODE f_prv( c )
/*** end of tests ***/
#ifdef __MT__
function thTest( mtxJobs, aResults )
local xJob := NIL
while .T.
hb_mutexSubscribe( mtxJobs,, @xJob )
if xJob == NIL
exit
endif
aResults[ xJob ] := &( "t" + strzero( xJob, 3 ) )()
enddo
return nil
function thTestScale( mtxJobs, mtxResults )
local xJob := NIL
while .T.
hb_mutexSubscribe( mtxJobs,, @xJob )
if xJob == NIL
exit
endif
hb_mutexNotify( mtxResults, &( "t" + strzero( xJob, 3 ) )() )
enddo
return nil
#endif
proc test( nMT, cExclude, lScale )
local nLoopOverHead, nTimes, nSeconds, cNum, aThreads, aResults, ;
mtxJobs, mtxResults, nTimeST, nTimeMT, nTimeTotST, nTimeTotMT, ;
cTest, x, i, j
create_db()
#ifdef __HARBOUR__
#include "hbmemory.ch"
if MEMORY( HB_MEM_STATISTICS ) != 0
? "Warning !!! Memory statistic enabled."
?
endif
if type( "__DBGENTRY()" ) == "UI"
? "Warning !!! HVM debugger enabled."
?
endif
#endif
//? "Startup loop to increase CPU clock..."
//x := seconds() + 5; while x > seconds(); enddo
#ifdef __MT__
if ! hb_mtvm()
#else
if .t.
#endif
if lScale
? "scale test available only in MULTI THREAD mode"
?
return
endif
if nMT != 0
? "SINGLE THREAD mode, number of threads set to 0"
nMT := 0
endif
endif
? date(), time(), os()
? version() + iif( hb_mtvm(), " (MT)" + iif( nMT != 0, "+", "" ), "" ), ""
#ifdef __HARBOUR__
?? hb_compiler(), ""
#endif
?? spd_cpu()
if lScale .and. nMT < 1
nMT := 1
endif
? "THREADS:", iif( nMT < 0, "all->" + ltrim( str( N_TESTS ) ), ltrim( str( nMT ) ) )
? "N_LOOPS:", ltrim( str( N_LOOPS ) )
if ! empty( cExclude )
? "excluded tests:", cExclude
endif
x :=t000()
nLoopOverHead := x[2]
if lScale
? space(56) + "1 th." + str(nMT,3) + " th. factor"
? replicate("=",76)
else
? dsp_result( x, 0 )
? replicate("=",68)
endif
nSeconds := seconds()
nTimes := hb_secondsCPU()
nTimeTotST := nTimeTotMT := 0
#ifdef __MT__
if lScale
aThreads := array( nMT )
mtxJobs := hb_mutexCreate()
mtxResults := hb_mutexCreate()
for i:=1 to nMT
aThreads[ i ] := hb_threadStart( "thTestScale", mtxJobs, mtxResults )
next
for i:=1 to N_TESTS
cTest := strzero( i, 3 )
if ! cTest $ cExclude
/* linear execution */
nTimeST := seconds()
for j:=1 to nMT
hb_mutexNotify( mtxJobs, i )
hb_mutexSubscribe( mtxResults,, @x )
cTest := x[ 1 ]
next
nTimeST := seconds() - nTimeST
nTimeTotST += nTimeST
/* simultaneous execution */
nTimeMT := seconds()
for j:=1 to nMT
hb_mutexNotify( mtxJobs, i )
next
for j:=1 to nMT
hb_mutexSubscribe( mtxResults,, @x )
cTest := x[ 1 ]
next
nTimeMT := seconds() - nTimeMT
nTimeTotMT += nTimeMT
? dsp_scaleResult( cTest, nTimeST, nTimeMT, nMT, nLoopOverHead )
endif
next
for i:=1 to nMT
hb_mutexNotify( mtxJobs, NIL )
next
hb_threadWaitForAll( aThreads )
elseif nMT < 0
aThreads := array( N_TESTS )
for i:=1 to N_TESTS
cNum := strzero( i, 3 )
if ! cNum $ cExclude
aThreads[ i ] := hb_threadStart( "t" + cNum )
endif
next
for i:=1 to N_TESTS
if aThreads[ i ] != NIL .and. hb_threadJoin( aThreads[ i ], @x )
? dsp_result( x, nLoopOverHead )
endif
next
elseif nMT > 0
aThreads := array( nMT )
aResults := array( N_TESTS )
mtxJobs := hb_mutexCreate()
for i:=1 to nMT
aThreads[ i ] := hb_threadStart( "thTest", mtxJobs, aResults )
next
for i:=1 to N_TESTS
if ! strzero( i, 3 ) $ cExclude
hb_mutexNotify( mtxJobs, i )
endif
next
for i:=1 to nMT
hb_mutexNotify( mtxJobs, NIL )
next
hb_threadWaitForAll( aThreads )
for i:=1 to N_TESTS
if aResults[ i ] != NIL
? dsp_result( aResults[ i ], nLoopOverHead )
endif
next
mtxJobs := NIL
else
for i:=1 to N_TESTS
cNum := strzero( i, 3 )
if ! cNum $ cExclude
? dsp_result( &( "t" + cNum )(), nLoopOverHead )
endif
next
endif
#else
for i:=1 to N_TESTS
cNum := strzero( i, 3 )
if ! cNum $ cExclude
? dsp_result( &( "t" + cNum )(), nLoopOverHead )
endif
next
#endif
nTimes := hb_secondsCPU() - nTimes
nSeconds := seconds() - nSeconds
if lScale
? replicate("=",76)
? dsp_scaleResult( " TOTAL ", nTimeTotST, nTimeTotMT, nMT, 0 )
? replicate("=",76)
else
? replicate("=",68)
endif
? dsp_result( { "total application time:", nTimes }, 0)
? dsp_result( { "total real time:", nSeconds }, 0 )
?
remove_db()
return
function f0()
return nil
function f1(x)
return x
function f2(x)
return nil
function f3(a,b,c,d,e,f,g,h,i)
return nil
function f4()
return space(4000)
function f5()
return space(5)
function f_prv(x)
memvar PRV_C
private PRV_C := x
return nil
/*
function f_pub(x)
memvar PUB_C
public PUB_C := x
return nil
function f_stat(x)
static STAT_C
STAT_C := x
return nil
*/
static function mkBlock(x)
return &x
static function errorArray()
#ifdef __NO_OBJ_ARRAY__
return array(16)
#else
return errorNew()
#endif
static func dsp_result( aResult, nLoopOverHead )
return padr( "[ " + left( aResult[ 1 ], 56 ) + " ]", 60, "." ) + ;
strtran( str( max( aResult[ 2 ] - nLoopOverHead, 0 ), 8, 2 ), " ", "." )
static func dsp_scaleResult( cTest, nTimeST, nTimeMT, nMT, nLoopOverHead )
if .f.
nTimeST := max( 0, nTimeST - nMT * nLoopOverHead )
nTimeMT := max( 0, nTimeMT - nMT * nLoopOverHead )
endif
return padr( "[ " + left( cTest, 50 ) + " ]", 54, "_" ) + ;
str( nTimeST, 6, 2 ) + " " + str( nTimeMT, 6, 2 ) + " ->" + ;
str( nTimeST / nTimeMT, 6, 2 )
#define TMP_FILE "_tst_tmp.dbf"
static proc create_db()
remove_db()
dbcreate( TMP_FILE, { {"F_C", "C", 10, 0},;
{"F_N", "N", 10, 2},;
{"F_D", "D", 8, 0} } )
use TMP_FILE exclusive
dbappend()
field->F_C := dtos(date())
field->F_N := 112345.67
field->F_D := date()
dbclosearea()
return
static proc remove_db()
ferase( TMP_FILE )
return
static proc close_db()
dbclosearea()
return
static proc use_dbsh()
use TMP_FILE shared
return
#ifdef __HARBOUR__
#ifndef __XHARBOUR__
static function spd_cpu()
return hb_version( HB_VERSION_CPU )
#endif
#endif
#ifdef __CLIPPER__
static function spd_cpu()
return "x86"
#endif
#ifdef FlagShip
static function spd_cpu()
return "?"
#endif
#ifdef __CLIP__
static function spd_cpu()
return "?"
#endif
#ifdef __XPP__
static function spd_cpu()
return "x86"
#endif
#ifdef __XHARBOUR__
static function spd_cpu()
return "?"
#endif
#ifdef __CLIPPER__
static function hb_mtvm()
return .f. /* Clipper does not support MT */
#endif
#ifdef FlagShip
static function hb_mtvm()
return .f. /* FlagShip does not support MT */
#endif
#ifdef __CLIP__
static function hb_mtvm()
return .t. /* CLIP always uses VM with MT support */
#endif
#ifdef __XPP__
static function hb_mtvm()
return .t. /* xBase++ always uses VM with MT support */
#endif
#ifdef __XHARBOUR__
static function hb_mtvm()
return hb_multiThread() /* check for MT support in xHarbour VM */
#endif
#ifndef __MT__
/* trivial single thread version of once execution */
static function hb_threadOnce( xOnceControl, bAction )
local lFirstCall := .f.
if xOnceControl == NIL
if bAction != NIL
eval( bAction )
endif
xOnceControl := .t.
lFirstCall := .t.
endif
return lFirstCall
#else
/* Add support for MT functions for used compiler
*/
#ifdef __XHARBOUR__
static function hb_mutexSubscribe( mtx, nTimeOut, xSubscribed )
local lSubscribed
if valtype( nTimeOut ) == "N"
nTimeOut := round( nTimeOut * 1000, 0 )
xSubscribed := Subscribe( mtx, nTimeOut, @lSubscribed )
else
xSubscribed := Subscribe( mtx )
lSubscribed := .t.
endif
return lSubscribed
static function hb_mutexNotify( mtx, xValue )
Notify( mtx, xValue )
return nil
/* In xHarbour there is race condition in JoinThread() which fails if
* thread have ended before call to JoinThread() so we cannot use it.
* Exactly the same problem exists in GetThreadId().
* As workaround we will use mutexes as thread IDs and notify/subscribe
* mechanism to simulate thread join operation and passing thread return
* value.
*/
static function hb_threadStart( ... )
local thId
thId := hb_mutexCreate()
/* For some reasons codeblocks as thread startup entry are broken
* in xHarbour so we use intermediate function instead
*/
StartThread( @_thFuncFirst(), thId, hb_aParams() )
return thId
static function _thFuncFirst( thID, aParams )
Notify( thId, hb_execFromArray( aParams ) )
return nil
static function hb_threadJoin( thId, xResult )
xResult := Subscribe( thId )
return .t.
static function hb_threadWaitForAll()
WaitForThreads()
return nil
static function hb_threadOnce( xOnceControl, bAction )
static s_mutex
local lFirstCall := .f.
if s_mutex == NIL
s_mutex := hb_mutexCreate()
endif
if xOnceControl == NIL
hb_mutexLock( s_mutex )
if xOnceControl == NIL
if bAction != NIL
eval( bAction )
endif
xOnceControl := .t.
lFirstCall := .t.
endif
hb_mutexUnlock( s_mutex )
endif
return lFirstCall
init proc once_init()
/* set workareas local to thread */
set workarea private
/* initialize mutex in hb_threadOnce() */
hb_threadOnce()
/* initialize error object to reduce to chance for possible crash
* when two threads try to create new error class simultaneously.
* xHarbour does not have any protection against such situation
*/
errorNew()
return
#endif /* __XHARBOUR__ */
#ifdef __XPP__
#ifdef __HARBOUR__
/* for testing. Harbour also can use xBase++ API in this code */
#include "hbclass.ch"
#endif
INIT PROCEDURE once_init()
/* initialize sync object in hb_threadOnce() */
hb_threadOnce()
RETURN
CLASS Notifier
PROTECTED:
VAR aQueue
VAR oSignal
EXPORTED:
METHOD init
SYNC METHOD notify
SYNC METHOD subscribe
ENDCLASS
METHOD Notifier:init
::aQueue := {}
::oSignal := Signal():new()
RETURN self
METHOD Notifier:notify( xValue )
AAdd( ::aQueue, xValue )
::oSignal:signal()
RETURN self
METHOD Notifier:subscribe()
LOCAL xResult
WHILE Len( ::aQueue ) == 0
::oSignal:wait()
ENDDO
xResult := ::aQueue[ 1 ]
ADel( ::aQueue, 1 )
ASize( ::aQueue, Len( ::aQueue ) - 1 )
RETURN xResult
STATIC FUNCTION hb_mutexSubscribe( mtx, nTimeOut, xResult )
/* Ignore timeout - it's not used in this test */
xResult := mtx:subscribe()
RETURN .T.
STATIC FUNCTION hb_mutexNotify( mtx, xValue )
RETURN mtx:notify( xValue )
STATIC FUNCTION hb_mutexCreate()
RETURN Notifier():new()
CLASS Once
EXPORTED:
SYNC METHOD onceDo
ENDCLASS
METHOD Once:onceDo( xOnceControl, bAction )
LOCAL lFirstCall := .f.
IF xOnceControl == NIL
IF bAction != NIL
Eval( bAction )
ENDIF
xOnceControl := .t.
lFirstCall := .t.
ENDIF
RETURN lFirstCall
STATIC FUNCTION hb_threadOnce( xOnceControl, bAction )
STATIC s_oObject := NIL
IF s_oObject == NIL
s_oObject := Once():new()
ENDIF
RETURN s_oObject:onceDo( @xOnceControl, bAction )
STATIC FUNCTION hb_threadStart( cFunc, xPar1, xPar2, xPar3 )
LOCAL oThread
oThread := Thread():new()
oThread:start( cFunc, xPar1, xPar2, xPar3 )
RETURN oThread
STATIC FUNCTION hb_threadJoin( oThread, xResult )
oThread:synchronize( 0 )
xResult := oThread:result
RETURN .T.
STATIC FUNCTION hb_threadWaitForAll( aThreads )
ThreadWaitAll( aThreads )
RETURN NIL
#endif /* __XPP__ */
/*
static function hb_threadStart( cFunc, xPar1, xPar2, xPar3 )
return nil
static function hb_threadJoin( thId, xResult )
return nil
static function hb_mutexCreate()
return nil
static function hb_mutexSubscribe()
return nil
static function hb_mutexLock()
return nil
static function hb_mutexUnlock()
return nil
static function hb_mutexNotify()
return nil
static function hb_threadWaitForAll()
return nil
static function hb_mtvm()
return .f.
*/
#endif
#ifdef FlagShip
static function fs_seconds()
LOCAL_DOUBLE nret := 0
#Cinline
{
#include <sys/time.h>
struct timeval tv;
if( gettimeofday(&tv, NULL) == 0 )
nret = (double) tv.tv_sec + (double) (tv.tv_usec) / 1000000;
}
#endCinline
return nret
#ifndef FlagShip5
FUNCTION cursesinit()
return nil
#endif
#endif