Page 1 of 1

Threading questions

PostPosted: Mon Apr 21, 2008 3:16 pm
by mezzo
1) hmm, I'm probably doing something dumb, but just wondering is someone could shed some light on this.

Code: Select all
/////////////////////////////////////////////////////////////////////////////////////////////
VOID mainthread() {
Clear();

DWORD dwParm = 0;
ERRORCODE = 0;
int counter = 0;

HANDLE hThread = CreateThread("for_real", dwParm);

if ( !hThread ) {
   PrintF("[ERROR] Failed to create the run thread!");
   return;
   }
PrintF("[RUN] Started the run in a thread..");

while ( ERRORCODE < 1 ) {
Sleep(1000);
counter++;
if ( debugflag ) PrintF("[DEBUG] We are currently %u seconds into the run.", counter);
if (counter > 180) {
   ERRORCODE = 2;
   PrintF("Errorcode was nr 2");
   }
}
CloseHandle( hThread );

}


When debugflag is set to false, the PrintF statements inside the thread (for_real function), don't get printed until the thread is closed.
When I do set debugflag, the main loop prints a line every second and
the output generated in the thread ends up in between the debug lines.
(as I would expect it; ie during the running of the thread).
Why doesn't it output the text when the thread is running and debugflag is off ?

2) I added my 'automisation' routine in a different thread, so that I can
check from inside the thread for certain global variables.. ie bind a hotkey
to a function that sets a bool to TRUE to make the thread abort.
For some strange reason when I hit the hotkey in the middle of the thread
execution, it will only set the bool after the thread has finished..
Could anybody give me a small example of how a hotkey stops a thread?
Do I need 2 threads to accomplish this ?

PostPosted: Mon Apr 21, 2008 4:05 pm
by L. Spiro
#1: When text is sent to the output window from another thread it may not update immediately. This is because of Windows® and how controls (such as the text box used to display the printed text) are single-threaded; the text is sent to a waiting area and the thread may continue but the textbox can only be updated from the main thread and the holding area needs to wait for the right moment.
When the text is updated from the main thread it is pulled from the waiting area and used then. Otherwise, an update request has to be sent to the first thread (which handles both the control and the main thread of your script) and it may get blocked for a while depending on how Windows® handles threads.
I have considered adding a function to update the text box entirely.

In the meantime there is nothing you or I can do about it.

#2: Hotkeys are handled on the main MHS thread. Which means scripts that are executed via Hotkeys are run on the main MHS thread. Which means Hotkeys can not be polled again until the script finishes whatever it is doing. Hence if you use the main thread to scan for a change in a global it will never see the change until the script finishes and the main thread goes back to MHS to allow it to poll Hotkeys again.

The solution is to use the main thread to start a second thread which does all the work. The second thread does the long task that needs to be done while the main thread is in MHS polling Hotkeys (the first script thread has already exited). When a Hotkey is hit the main thread calls a script function which changes a global which is checked by the second thread.

No example needed for this simple task.


L. Spiro

PostPosted: Tue Apr 22, 2008 7:23 pm
by mezzo
1) fair enough, I can get the output I want to file or simple keep on using the debugflag for this purpose.

2) I think I understand.. something like the below, right ?

Code: Select all
HANDLE HNDworkthread;

bool Bstop_workthread = FALSE;

bool Bstart_run;

//////////////////////////////////////////////////////////////
void start_threads() {
HNDworkthread = CreateThread("workerthread", workparams);
if ( !HNDworkthread ) {
   PrintF("[ERROR] Failed to create the work thread!");
   return;
   }
}
//////////////////////////////////////////////////////////////
void workerthread() {
   while ( !Bstop_workthread ) {
      if ( Bstart_run ) {
         //do whatever you want done
      }
      else { Sleep(1000); }
   }
   Bstop_workthread = FALSE;
   return;
}
//////////////////////////////////////////////////////////////
void on_hk_1() {
start_threads();
}
//////////////////////////////////////////////////////////////
void on_hk_2() {
Bstart_run = TRUE;
}
//////////////////////////////////////////////////////////////
void on_hk_3() {
CloseHandle( HNDworkthread );
}
//////////////////////////////////////////////////////////////

PostPosted: Tue Apr 22, 2008 10:21 pm
by L. Spiro
Code: Select all

bool Bstop_workthread = FALSE;

bool Bstart_run;

//////////////////////////////////////////////////////////////
void start_threads() {
    HANDLE HNDworkthread = CreateThread("workerthread", workparams);
    if ( !HNDworkthread ) {
        PrintF("[ERROR] Failed to create the work thread!");
        return;
    }
    CloseHandle( HNDworkthread );
}
//////////////////////////////////////////////////////////////
void workerthread() {
   while ( !Bstop_workthread ) {
      if ( Bstart_run ) {
         //do whatever you want done
      }
      else { Sleep(1000); }
   }
   Bstop_workthread = FALSE;
   return;
}
//////////////////////////////////////////////////////////////
void on_hk_1() {
    start_threads();
}
//////////////////////////////////////////////////////////////
void on_hk_2() {
    Bstart_run = TRUE;
}
//////////////////////////////////////////////////////////////




L. Spiro

PostPosted: Thu Apr 24, 2008 3:53 am
by mezzo
I tried implementing this on my big script... but it's not working..
+ the threads are not providing me any output whatsoever, so I have no
clue what they are doing :p

will have to go back to basics and make some test proggies first to see
if what I'm doing is actually feasible.

PostPosted: Sat Apr 26, 2008 10:44 pm
by CoMPMStR
Shouldn't the workerthread be declared with the dwParam variable?

Code: Select all
void workerthread(DWORD dwParam)


In the helpfile, doesn't it say that the function used for CreateThread must be declared like this or the thread won't work?

The target function must be in the format DWORD [function]( DWORD dwParm ).

PostPosted: Sun Apr 27, 2008 9:43 am
by mezzo
In all honesty, I thought that line just meant that only 1 parameter max is allowed and that it has to be a DWORD.. (so I was guessing that no parameter would be good too :p

thanks for pointing that out, will try tomorrow.

PostPosted: Sun Apr 27, 2008 8:55 pm
by L. Spiro
Somewhere in the help file it says that all callback functions (functions called by MHS itself directly for any reason, including Hotkey functions) can omit any unneeded parameters in order from the last to the first.
This includes the function called by CreateThread(). The parameter is optional.


L. Spiro

PostPosted: Sun May 04, 2008 10:09 am
by mezzo
finally figured it out.. it's all working now.
It works when I do the bool checks in full instead of short:

doesn't seem to work:
if ( run ) { //blah blah }

is working:
if ( run == TRUE ) { //more blah blah }

Perhaps because I don't declare the bools explicitly as TRUE or FALSE

Thanks for the help :-) threading rocks !

PostPosted: Sun May 04, 2008 11:03 am
by L. Spiro
That might indicate a minor error in the script language but right now I do not have time to look into it, and it works with the alternative anyway.


L. Spiro

PostPosted: Sun May 04, 2008 8:38 pm
by mezzo
Don't worry about it, writing it in full makes it more readable anyway.