Implementation of hb_threadStart() right at the main menu option

603 views
Skip to first unread message

Davaoz Best

unread,
Dec 7, 2014, 4:28:15 AM12/7/14
to harbou...@googlegroups.com

Hi All,

Please help me create a function that will try to implement the Xbase++-style multi-threading design right at the "Main Menu" option/s. For example, we have this function in one of Xbase++ 3rd Party libraries, which we would just call here as "Launch()."

Here's the way the Thread() was implemented:

***
FUNCTION Launch( cFuncName,x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,;  
                x11,x12,x13,x14,x15,x16,x17,x18,x19,x20,;
                x21,x22,x23,x24,x25,x26,x27,x28 )
  LOCAL oThread
  oThread:=Thread():New()
  oThread:Start( cFuncName,x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,;
                  x11,x12,x13,x14,x15,x16,x17,x18,x19,x20,;
                  x21,x22,x23,x24,x25,x26,x27,x28 )
RETURN NIL

The following is the usage of Launch() right at the Main Menu Option of the program. The rationale is for the multi-threading system to kick in, when there are many users sharing the same dbf file.

***
PULLDOWN(;
         {{  "File"       ,,;
         {{ "Edit DBF"    ,,{ || Launch(Edit1()) }},;
          { "Exit"        ,,{ || Exit_1() }}   }}...
  
Honestly, I don't fully understand the implementation of multi-threading feature of Harbour. I searched the Web and found this from Alexander Kresin's site, http://www.kresin.ru/en/hrbfaq_3.html#Doc12

***
      IF !hb_mtvm()
        ? "There is no support for multi-threading, clocks will not be seen."
         WAIT
      ELSE
        hb_threadStart( @Show_Time() )
      ENDIF

From Kresin's tutorial, I created a function that would somehow simulate Xbase++'s "Launch()"

***
FUNCTION Try_Launch( givefuncnamehere,P1,P2,P3,P4,P5,P6,P7,P8,P9,P10,P11,P12,P13,P14,P15 )

   RETURN hb_threadStart( @givefuncnamehere,P1,P2,P3,P4,P5,P6,P7,P8,P9,P10,P11,P12,P13,P14,P15 )


Then, I used Try_Launch() at the Main Menu:
***
PULLDOWN(;
         {{  "File"       ,,;
         {{ "Edit DBF"    ,,{ || Try_Launch(Edit1()) }},;
          { "Exit"        ,,{ || Exit_1() }}   }}...

It didn't work. FYI: I used option -mt in hbmk2.

Thanks so much. (I'm visiting and reading emails from this forum almost daily. Thanks for your time and generousity, guys!)

Jun P. Espina





Sami Laham

unread,
Dec 7, 2014, 5:51:11 AM12/7/14
to harbou...@googlegroups.com
Hi, I am not a expert in mt but you have some samples to see how to implement it 


see test folder too 


some samples 


and José M. C. Quintas samples attached


Regards, 

Sami Laham
allinone.zip

elch

unread,
Dec 7, 2014, 7:47:18 AM12/7/14
to harbou...@googlegroups.com
Hi Jun,

found in the source the hint:
hb_threadStart( [<nThreadAttrs> ,] <@sStart()> | <bStart> | <cStart> [, <params,...> ] ) -> <pThID>

About these 'nThreadAttrs' ( numeric values ), i do not know anything, maybe these can be explained by someone else ??
But it is optional -- this case the next param will be the first one.

So we can see, that the param can be a
function/ procedure passed by reference '@',
codeblock "{||..}"
the function name as string.

In case of using the function name as string, i would advice to add an errorblock for catching possible runtime errors in case the function is not available.
So not as below directly returning the thread id value.
Using a codeblock i still have not tested ..

So it should be e.g. possible:
FUNCTION Try_Launch( cFunction, ... )
RETURN hb_threadStart( cFunction, ... )

or
FUNCTION Try_Launch( bFunction, ... )

Note also my use of 3 points '...' instead of all these placeholders, from which many will be only empty.
And maybe sometime in future a placeholder would miss ... ]

Then you can use in your menu:
{{ "Edit DBF"    ,,{ || Try_Launch( "Edit1" ) }}
or
{{ "Edit DBF"    ,,{ || Try_Launch( { || Edit1() } ) }}

---
So far, so good -- but we have to consider more points, as example each thread have its own workareas, etc.
About this and more, read also the parts related to the topic: thread in file: 'xhb-diff.txt' in the 'doc' directory.

best regards
Rolf

Daniele Campagna

unread,
Dec 7, 2014, 8:21:45 AM12/7/14
to harbou...@googlegroups.com
Il 07/12/2014 10:28, Davaoz Best ha scritto:
>
> Hi All,
>
> Please help me create a function that will try to implement the
> Xbase++-style multi-threading design right at the "Main Menu"
> option/s. For example, we have this function in one of Xbase++ 3rd
> Party libraries, which we would just call here as "Launch()."
>
> Here's the way the Thread() was implemented:
>
> ***
> FUNCTION Launch( cFuncName,x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,;
> x11,x12,x13,x14,x15,x16,x17,x18,x19,x20,;
> x21,x22,x23,x24,x25,x26,x27,x28 )
> LOCAL oThread
> oThread:=Thread():New()
> oThread:Start( cFuncName,x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,;
> x11,x12,x13,x14,x15,x16,x17,x18,x19,x20,;
> x21,x22,x23,x24,x25,x26,x27,x28 )
> RETURN NIL
>
> The following is the usage of Launch() right at the Main Menu Option
> of the program. The rationale is for the multi-threading system to
> kick in, when there are many users sharing the same dbf file.
>
Well, if the rationale is to allow for multi-user share the same dbf you
don't need multi-thread at all.
I suspect the code is multi-threaded fos some more complex task/purpose.
Unfortunately I used trial Alaska some 14 years ago and while I was
impressed by the maturity of Alaska at the time, I never had the
necessity to switch to Alaska, so sorry, no help available on that side
by me.
Try to figure out why exactly the multi-threaded architecture was
implemented.

My 2 cents
Dan

marek.h...@interia.pl

unread,
Dec 7, 2014, 8:37:28 AM12/7/14
to harbou...@googlegroups.com
Od: "Davaoz Best" <info.da...@gmail.com>
Do: harbou...@googlegroups.com;
Wysłane: 10:28 Niedziela 2014-12-07
Temat: [harbour-users] Implementation of hb_threadStart() right at the main menu option
Hi All,
Please help me create a function that will try to implement the Xbase++-style multi-threading design right at the "Main Menu" option/s. For example, we have this function in one of Xbase++ 3rd Party libraries, which we would just call here as "Launch()."

*---------------------

Hi,
have you tried examples from \harbour\tests\mt ?
I use MT and my knowledge of MT comes from there (and from analysis uhttp).
MT I use most often in multi-threaded sql database queries.

Regards,
Marek Horodyski

Davaoz Best

unread,
Dec 10, 2014, 10:39:12 AM12/10/14
to harbou...@googlegroups.com
Hi Guys,

Thank you for your quick reply. 

It took me days to figure out a solution on how to use hb_threadstart() at the Main Menu Option/s in order to effect multi-threading. I even went as far as consulting the Wiki for the meaning of "Mutex" (or Mutual Exclusion). So far I noticed that, adding "mutexes" (hb_MutexCreate(), etc. seems to slow down execution speed. But in some discussions, we learned that it is absolutely necessary as what José M. C. Quintas did to his "RunThread()" function. More, I noticed some weird behavior when I executed the following code 3 or 4 times successively:

hbmk2 try2.prg -mt

FUNC MAIN

hb_threadstart({||myfunc()})
cls
? "a"
? "b"
? "c"
return nil

PROC  myfunc()

? "1"
? "2"
? "3"

return

***1. The results for two or three consecutive executions are the same:
1
2
3a
b
c
***In the fourth or fifth time, it would return like this:

a
b
c
d:\dev\hb...<-cursor already in place

1
2
3

***2. Note: cursor was already in the directory, then the "1 2 3".

Then, I used setmode(25,80), but sad that said weird behavior increased all the more in its weirdness, as the program didn't anymore return the complete values, viz., "a b c 1 2 3".

Frustrated with my ignorance, I went to Sami Laham's suggestion to try to check José M. C. Quintas "allinone.zip."

Impressed, José's function, RunThread(), handsomely named and written, was the key, to my mind, in emulating the Xbase++'s 3rd Party's function I renamed for obvious reasons as "Launch()." But, I failed when I used RunThread().

My question is, 1) What will happen if we will remove RunThread()'s call to HarbourInit(), so that we can use it anywhere there is a dbf manipulation, just like the function trim() or eof()? 

The topic on "Przemek's response to xBase++ Stepphen's" contention on the architecture on xBase's Multi-Threading is too hard for me to digest.

José M. C. Quintas' thought thus on this matter, being the author of RunThread(), is precious as gold indeed, not to mention the pieces of brilliant minds out there from those well-experienced in this forum.

Your kind help will be so well appreciated indeed.

Thanks. 

All is well.

Jun

********
 José M. C. Quintas' work from AllInOne.Zip (test.prg)

PROCEDURE Main
   HarbourInit()
   RunThread( { || MainMenu( .F. ) } )
   DO WHILE RunThread()
      HB_IdleSleep(1)
   ENDDO
   RETURN

FUNCTION RunThread( bCode )
   STATIC AppThreadList := {}, s_Mutex := hb_MutexCreate()
   LOCAL  nCont, lIsRunning := .F.

   hb_MutexLock( s_Mutex )

   IF bCode == NIL
      IF Len( AppThreadList ) != 0
         lIsRunning := .F.
         FOR nCont = Len( AppThreadList ) TO 1 STEP -1
            IF hb_ThreadWait( AppThreadList[ nCont ], 0.1, .T. ) != 1
               lIsRunning := .T.
            ELSE
               aDel( AppThreadList, nCont )
               aSize( AppThreadList, Len( AppThreadList ) - 1 )
            ENDIF
         NEXT
      ENDIF
   ELSE
      Aadd( AppThreadList, hb_ThreadStart( bCode ) )
   ENDIF
   hb_MutexUnlock( s_Mutex )
   RETURN lIsRunning


--
--
You received this message because you are subscribed to the Google
Groups "Harbour Users" group.
Unsubscribe: harbour-user...@googlegroups.com
Web: http://groups.google.com/group/harbour-users

---
You received this message because you are subscribed to the Google Groups "Harbour Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to harbour-user...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

José Quintas

unread,
Dec 10, 2014, 1:25:47 PM12/10/14
to harbou...@googlegroups.com
Attention about thread execution.
If you close main window, close all threads.

I create these functions to help multithread control.
Note Inkey(1) before check RunThread().
I add this because in a quick execution, application was closed before thread start.

1) Application load

PROCEDURE Main
   PARAMETERS cParam
   MEMVAR cParam
   LOCAL xParam

   xParam := iif( cParam == NIL, "", cParam )
   RunThread( { || Sistema( xParam ) } )
   Inkey(1)

   DO WHILE RunThread()
      HB_IdleSleep(1)
   ENDDO
   RETURN


2) Run a module with or without thread

FUNCTION RunModule( cModule, cTitulo )
   LOCAL mHrInic
   IF AppIsMultiThread()
      RunThread( { || doPrg( cModule, cTitulo ) } )
      Inkey(1)
   ELSE
      wSave()
      Mensagem()
      SayTitulo( cTitulo )
      Scroll( 1, 0, MaxRow() - 3, MaxCol(), 0 )
      @ MaxRow() - 2, 0 TO MaxRow() - 2, MaxCol() COLOR SetColorTraco()
       DO ( cModule )
      wRestore()
   ENDIF
   RETURN NIL

3) Auxiliar function to create the GT (if needed screen)

FUNCTION DoPrg( cModule, cTitulo )
   LOCAL mHrInic
   MEMVAR m_Prog // , oStatusBar
   PRIVATE m_Prog // , oStatusBar
   m_Prog := cModule

   hb_gtReload( hb_GTInfo( HB_GTI_VERSION ) )
   HB_GtInfo( HB_GTI_WINTITLE, cTitulo )
   SetColor( SetColorNormal() )
   CLS
   SayTitulo( cTitulo )
   @ MaxRow() - 2, 0 TO MaxRow() - 2, MaxCol() COLOR SetCOlorTraco()
    DO( cModule )
   RETURN NIL


4) RunThread, I do not know if is the same as before

FUNCTION RunThread( bCode )
   STATIC aThreadList := {}, s_Mutex := hb_MutexCreate()

   LOCAL  nCont, lIsRunning := .F.

   hb_MutexLock( s_Mutex )

   IF bCode == NIL
      IF Len( aThreadList ) != 0
         lIsRunning := .F.
         FOR nCont = Len( aThreadList ) TO 1 STEP -1
            IF hb_ThreadWait( aThreadList[ nCont ], 0.1, .T. ) != 1
               lIsRunning := .T.
            ELSE
               aDel( aThreadList, nCont )
               aSize( aThreadList, Len( aThreadList ) - 1 )
            ENDIF
         NEXT
      ENDIF
   ELSE
      Aadd( aThreadList, hb_ThreadStart( bCode ) )

   ENDIF
   hb_MutexUnlock( s_Mutex )
   RETURN lIsRunning


5) Class to  automatically close a thread when close the module that makes the call.


oRun := RunWhileThreadClass():New()
oRun:Execute( bCode )




CREATE CLASS RunWhileThreadClass
   VAR lExit        INIT .F.
   VAR nThreadId
   VAR nInterval    INIT 600
   VAR cWindowTitle INIT ""
   VAR bCode
   METHOD New()
   METHOD Execute( bCode )
   END CLASS


METHOD New() CLASS RunWhileThreadClass
   ::nThreadId := hb_ThreadSelf()
   RETURN SELF


METHOD Execute( bCode ) CLASS RunWhileThreadClass
   LOCAL nCont

   hb_gtReload( "WVG" )
   IF bCode != NIL
      ::bCode := bCode
   ENDIF
   AppInitSets()
   HB_GtInfo( HB_GTI_WINTITLE, ::cWindowTitle )
   wvgSetAppWindow():Hide()
   DO WHILE .NOT. ::lExit
      Eval( ::bCode )
      FOR nCont = 1 TO ::nInterval
         hb_ReleaseCPU()
         IF hb_ThreadWait( ::nThreadId, 0.1, .T. ) == 1
            ::lExit := .T.
         ENDIF
         Inkey(1)
         IF ::lExit
            EXIT
        ENDIF
      NEXT
   ENDDO
   RETURN NIL


José M. C. Quintas

José Quintas

unread,
Dec 10, 2014, 1:42:25 PM12/10/14
to harbou...@googlegroups.com
Complement:
When you run as thread, you do not have a separated screen, and you do not have control about order execution.
Messages of thread can be mixed in screen in any order.

Or you can use one screen for each thread, if GT accept this (hb_gtReload())


José M. C. Quintas
Reply all
Reply to author
Forward
0 new messages