Is WaitWindowEvent(variable) usable or is it unstable?

Just starting out? Need help? Post your questions and find answers here.
hoerbie
Enthusiast
Enthusiast
Posts: 119
Joined: Fri Dec 06, 2013 11:57 am
Location: DE/BY/MUC

Is WaitWindowEvent(variable) usable or is it unstable?

Post by hoerbie »

Hi,

I would like to know, if it is stable to use WaitWindowEvent(variable.i) with a sometimes between the calls changed integer variable?

I understand, that WaitWindowEvent will come back faster then the variable timeout, when there are new events, and this wouldn't be a problem.
But mostly on Win10 it looks to me, that sometimes WaitWindowEvent doesn't come back, when the variable timeout should have been reached long ago.

In the manual I haven't found a clear info, if the timeout possibly has to be a constant or it also could be a changed variable, the compiler doesn't complain about my variable. Also there are no hints about the reliability of this timeout in the manual.
For example at AddWindowTimer there is a hint in the manual, that these timers will possibly not be near to exact, because they have low priority.

My use case: I wrote a PB program waiting for a job-file from a good old DOS program, doing the job(s) and answering with a file to the DOS program.
I set the variable when my program only waits for a new job from the DOS program to a timeout of 250ms.
But when doing different jobs I use timeouts between 25 and 250ms for WaitWindowEvent. The real jobs started by my program are done async on external devices with communication through Tcp/Ip or serial/com-port or filesystem and also need some time.

In general this works good, but sometimes and mostly on Win10 it looks to me, that WaitWindowEvent(variable.i) and variable with changing values of 25, 50, 100 or 250 sometimes for some seconds doesn't come back, so starting of a new job or working on an actual job sometimes really gets slow, and when for example moving the mouse wake up and become fast.

Putting an additional AddWindowTimer with for example 100ms makes everything faster, although these timer events have low priority according to the manual.

I know that I possibly could solve the timing problems with threads and Delay(), but this would be a really new experience with a big possibility of a lot new mistakes and bugs for me.

Does anyone have any tips for me?

Greetings and happy easter,
Hoerbie
User avatar
mk-soft
Always Here
Always Here
Posts: 5335
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: Is WaitWindowEvent(variable) usable or is it unstable?

Post by mk-soft »

I assume that you are doing the asynchronous processing in threads.

Just send a PostEvent to the GUI that the processing is done. You can also transmit an EventData with the PostEvent.
But remember that an open "menu" stops the event loop.

Small simple example

Code: Select all

;-TOP

Enumeration Windows
  #Main
EndEnumeration

Enumeration Gadgets
  
EndEnumeration

Enumeration Status
  #MainStatusBar
EndEnumeration

Enumeration CustomEvent #PB_Event_FirstCustomValue
  #MyEvent_ThreadFinished
EndEnumeration

Procedure thWork(id)
  Delay(Random(5000, 2000))
  PostEvent(#MyEvent_ThreadFinished, 0, 0, 0, id)
EndProcedure


Procedure Main()
  
  #MainStyle = #PB_Window_SystemMenu | #PB_Window_MaximizeGadget | #PB_Window_MinimizeGadget
  
  If OpenWindow(#Main, #PB_Ignore, #PB_Ignore, 800, 600, "Window" , #MainStyle)
    CreateThread(@thWork(), 1)
    CreateThread(@thWork(), 2)
    CreateThread(@thWork(), 3)
    
    Repeat
      Select WaitWindowEvent()
        Case #PB_Event_CloseWindow
          Break
          
        Case #MyEvent_ThreadFinished
          Debug "Thread Finished ID " + EventData()
          
      EndSelect
    ForEver
    
  EndIf
  
EndProcedure : Main()
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
Jeff8888
User
User
Posts: 38
Joined: Fri Jan 31, 2020 6:48 pm

Re: Is WaitWindowEvent(variable) usable or is it unstable?

Post by Jeff8888 »

Interesting , looking at the example in the reply, I didn't realize you could use the same procedure multiple times by threading.
hoerbie
Enthusiast
Enthusiast
Posts: 119
Joined: Fri Dec 06, 2013 11:57 am
Location: DE/BY/MUC

Re: Is WaitWindowEvent(variable) usable or is it unstable?

Post by hoerbie »

Hi,

@mk-soft: Thanks for the example, but I'm actually not doing the jobs in one or multiple thread(s). There is simply one (sub)job that can be done at a time on the device, maybe directly followed by other (sub)jobs, but always only one device, so no really parallel work needed.

On all my PCs it works perfectly with doing all work (getting job from old prog, sending job to device, waiting, getting job answer from device, sending answer to old prog) in the event queue one after one. And modifying the timeout variable for the next call of WaitWindowEvent(timeout) seems to work too, when needed for different jobs.

But although calling WaitWindowEvent(timeout) with timeout=25 or timeout=100 etc. sometimes Win10 waits for multiple seconds without coming back to the event queue, but when moving the mouse it gets back to work, and using AddWindowTimer works better too.

I'm willing to give threads a try, but after getting problems with the timing of WaitWindowEvent(timeout), I need to be sure, that the timing of Delay(waitmilliseconds) is really reliable and not stopping like the event-queue?
And I'm really anxious to get a lot of new problems, as I've never needed and used threads until now, all the mutex, lock, thread safe stuff etc. are new to me.

Greetings, Hoerbie
User avatar
mk-soft
Always Here
Always Here
Posts: 5335
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: Is WaitWindowEvent(variable) usable or is it unstable?

Post by mk-soft »

You can also create one or more master threads and start work threads in them.

To wait for the work threads, I wrote a simple job manager.

To do this, the data structure of the work thread must always be extended with the structure "udtJobData".

To create the master thread, use the function CreateJobMaster.
To create work threads, use the function CreateJobWork.
In addition, there is the thread function JobSignal for work threads and the functions JobWait and JobTry for the master thread.

Code: Select all

;-TOP

; Comment : Simple Thread Job Management
; Author  : mk-soft
; Version : 1.01.0
; Create  : 03.04.2021

; ********

EnableExplicit

CompilerIf Not #PB_Compiler_Thread
  CompilerError "Use Compiler Option ThreadSafe!"
CompilerEndIf

; ----

Structure udtJobEvent
  Semaphore.i
  Mutex.i
  List *Data()
EndStructure

Structure udtJobData
  *JobEvent.udtJobEvent
  Thread.i
  Exit.i
  Type.i
EndStructure

Structure udtJobMaster Extends udtJobData
  JobEventData.udtJobEvent
EndStructure

; ----

Procedure JobSignal(*JobData.udtJobData)
  Protected *JobEvent.udtJobEvent = *JobData\JobEvent
  With *JobEvent
    LockMutex(\Mutex)
    LastElement(\Data())
    AddElement(\Data())
    \Data() = *JobData
    SignalSemaphore(\Semaphore)
    UnlockMutex(\Mutex)
  EndWith
EndProcedure

; ----

Procedure JobWait(*JobData.udtJobData)
  Protected *JobEvent.udtJobEvent = *JobData\JobEvent
  Protected *Data
  With *JobEvent
    WaitSemaphore(\Semaphore)
    LockMutex(\Mutex)
    If FirstElement(\Data())
      *Data = \Data()
      DeleteElement(\Data())
    EndIf
    UnlockMutex(\Mutex)
  EndWith
  ProcedureReturn *Data
EndProcedure

; ----

Procedure JobTry(*JobData.udtJobData)
  Protected *JobEvent.udtJobEvent = *JobData\JobEvent
  Protected *Data
  With *JobEvent
    If TrySemaphore(\Semaphore) > 0
      LockMutex(\Mutex)
      If FirstElement(\Data())
        *Data = \Data()
        DeleteElement(\Data())
      EndIf
      UnlockMutex(\Mutex)
    EndIf
  EndWith
  ProcedureReturn *Data
EndProcedure

; ----

Procedure CreateJobMaster(*Callback, *JobMaster.udtJobMaster)
  With *JobMaster
    \JobEvent = *JobMaster\JobEventData
    \JobEventData\Mutex = CreateMutex()
    \JobEventData\Semaphore = CreateSemaphore()
    \Exit = #False
    \Thread = CreateThread(*Callback, *JobMaster)
  EndWith
EndProcedure

; ----

Procedure CreateJobWork(*Callback, *JobMaster.udtJobMaster, *JobData.udtJobData, JobType)
  With *JobData
    \JobEvent = *JobMaster\JobEvent
    \Exit = #False
    \Type = JobType
    \Thread = CreateThread(*Callback, *JobData)
  EndWith
EndProcedure

; ----

; ****

Enumeration Windows
  #Main
EndEnumeration

Enumeration Gadgets
  
EndEnumeration

Enumeration Status
  #MainStatusBar
EndEnumeration

Enumeration CustomEvent #PB_Event_FirstCustomValue
  #MyEvent_ThreadFinished
EndEnumeration

; ----

Structure udtWorkData1 Extends udtJobData
  iVal.i
EndStructure

Structure udtWorkData2 Extends udtJobData
  sVal.s
EndStructure

; ----

Global JobMaster.udtJobMaster
Global JobWork1.udtWorkData1
Global JobWork2.udtWorkData1
Global JobWork3.udtWorkData2

; ----

Procedure thJobWork1(*pData.udtWorkData1)
  With *pData
    Delay(Random(5000, 2000))
    *pData\iVal = Random(10)
    JobSignal(*pData)
  EndWith
EndProcedure

Procedure thJobWork2(*pData.udtWorkData2)
  With *pData
    Delay(Random(5000, 2000))
    *pData\sVal = "Hello World!"
    JobSignal(*pData)
  EndWith
EndProcedure

; ----

Procedure thJobMaster(*JobMaster.udtJobMaster)
  Protected *Data.udtJobData
  Protected *Data1.udtWorkData1, *Data2.udtWorkData2
  
  ; Create Jobs
  CreateJobWork(@thJobWork1(), @JobMaster, @JobWork1, 1)
  CreateJobWork(@thJobWork1(), @JobMaster, @JobWork2, 1)
  CreateJobWork(@thJobWork2(), @JobMaster, @JobWork3, 2)
    
  ; Wait 
  Repeat
    *Data = JobWait(*JobMaster)
    ;*Data = JobTry(*JobMaster)
    If *JobMaster\Exit
      Break
    EndIf
    If *Data
      ; Type of Thread
      Select *Data\Type
        Case 1
          *Data1 = *Data
          Debug "Job Type 1: Value = " + *Data1\iVal
          
        Case 2
          *Data2 = *Data
          Debug "Job Type 2: String = " + *Data2\sVal
          
      EndSelect
      PostEvent(#MyEvent_ThreadFinished, 0, 0, 0, *Data\Type)
    Else
      Delay(10)
    EndIf
  ForEver
EndProcedure

; ----

Procedure Main()
  
  #MainStyle = #PB_Window_SystemMenu | #PB_Window_MaximizeGadget | #PB_Window_MinimizeGadget
  
  If OpenWindow(#Main, #PB_Ignore, #PB_Ignore, 400, 200, "Thread Example" , #MainStyle)
    
    CreateJobMaster(@thJobMaster(), @JobMaster)
    
    Repeat
      Select WaitWindowEvent()
        Case #PB_Event_CloseWindow
          Break
          
        Case #MyEvent_ThreadFinished
          Debug "Thread Finished Type " + EventData()
          
      EndSelect
    ForEver
    
    JobMaster\Exit = #True
    JobSignal(JobMaster)
    WaitThread(JobMaster\Thread)
    
  EndIf
  
EndProcedure : Main()
Last edited by mk-soft on Sat Apr 03, 2021 4:49 pm, edited 1 time in total.
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
BarryG
Addict
Addict
Posts: 3292
Joined: Thu Apr 18, 2019 8:17 am

Re: Is WaitWindowEvent(variable) usable or is it unstable?

Post by BarryG »

hoerbie wrote:it looks to me, that WaitWindowEvent(variable.i) and variable with changing values of 25, 50, 100 or 250 sometimes for some seconds doesn't come back
We'd need to see your code to see what you're doing. But WaitWindowEvent(variable.i) is reliable, yes. Sounds like a coding error on your part (sorry).
Post Reply