Gui menu select blocks thread ?

Just starting out? Need help? Post your questions and find answers here.
User avatar
Martt
User
User
Posts: 18
Joined: Sat Dec 23, 2017 4:03 pm
Location: The Netherlands

Gui menu select blocks thread ?

Post by Martt »

Code: Select all

EnableExplicit

Global Semaphore.i

Declare Main()
Declare Thread(*Dummy)

Enumeration #PB_Event_FirstCustomValue
    #Thread_Finished
    #Thread_AddText
EndEnumeration

Enumeration
    #MAIN_WINDOW
    #EDITOR_WINDOW
    #MENU
    #FILE_RUN
    #FILE_QUIT
EndEnumeration

Procedure Main()

    Protected Exit.b
    Protected Event.i
    Protected Thread.i
    
    #Width  = 400
    #Height = 400
    #Space = 10
    
    OpenWindow(#MAIN_WINDOW, 100, 100, #Width, #Height, "Main Window")
    EditorGadget(#EDITOR_WINDOW, #Space, #Space, #Width - (#Space * 2), #Height - MenuHeight() - (#Space * 2), #PB_Editor_ReadOnly)
    
    CreateMenu(#MENU, WindowID(#MAIN_WINDOW))
        MenuTitle("File")
            MenuItem(#FILE_RUN,  "Run")
            MenuItem(#FILE_QUIT, "Quit")
        
    Repeat
        Select WaitWindowEvent()
            Case #PB_Event_CloseWindow
                Exit = #True
                
            Case #Thread_AddText
                AddGadgetItem(#EDITOR_WINDOW, -1, PeekS(EventData()))
                ; Goto last character (auto scroll)
                SendMessage_(GadgetID(#EDITOR_WINDOW), #EM_SETSEL, -1, -1)
                SignalSemaphore(Semaphore)

            Case #Thread_Finished
                DisableMenuItem(#MENU, #FILE_RUN, #False)
                SignalSemaphore(Semaphore)                
                
            Case #PB_Event_Menu
                Event = EventMenu()
                Select Event
                    Case #FILE_RUN
                        DisableMenuItem(#MENU, #FILE_RUN, #True)
                        Thread = CreateThread(@Thread(), #Null)
                        
                    Case #FILE_QUIT
                        Exit = #True
                EndSelect
        EndSelect
    Until Exit
    
    ; Kill thread if needed...
    If IsThread(Thread) : KillThread(Thread) : EndIf
    
    FreeMenu(#MENU)
    CloseWindow(#MAIN_WINDOW)

EndProcedure

Procedure Thread(*Dummy)
    
    Protected LoopCnt.i
    Protected Output.s
    
    For LoopCnt = 1 To 10000
        Output = "This string is number " + Str(LoopCnt)
        
        ; Send output to gui and wait for gui to process...
        Semaphore = CreateSemaphore()

        PostEvent(#Thread_AddText, #Null, #Null, #Null, @Output)
        WaitSemaphore(Semaphore)
        FreeSemaphore(Semaphore)
    Next
    
    ; Done, let the gui know were done...
    Semaphore = CreateSemaphore()

    PostEvent(#Thread_Finished, #Null, #Null, #Null, #Null)
    WaitSemaphore(Semaphore)
    FreeSemaphore(Semaphore)

EndProcedure

Main()
End
Newbie question here. In this test program, the menuoption File->Run will run the thread. But if the thread is running and I click on File, the thread stops displaying output. Likely because of the Semaphore.
Is there a way around it besides HideMenu(), which messes up the coordinates of the editorgadget ?
infratec
Always Here
Always Here
Posts: 7582
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: Gui menu select blocks thread ?

Post by infratec »

Hi,

the thread is still running, but paused by waiting on the semaphore.
The main eventloop is stooped by your menu access, so it can not process the Events.

You can avoid this by using BindEvent()

Code: Select all

EnableExplicit

Structure ThreadParameterStructure
  Thread.i
  Semaphore.i
  Exit.i
EndStructure



Declare Main()
Declare Thread(*Dummy)

Enumeration #PB_Event_FirstCustomValue
  #Thread_Finished
  #Thread_AddText
EndEnumeration

Enumeration
  #MAIN_WINDOW
  #EDITOR_WINDOW
  #MENU
  #FILE_RUN
  #FILE_QUIT
EndEnumeration

Global ThreadParameter.ThreadParameterStructure

Procedure Test()
  AddGadgetItem(#EDITOR_WINDOW, -1, PeekS(EventData()))
  SendMessage_(GadgetID(#EDITOR_WINDOW), #EM_SETSEL, -1, -1)
  SignalSemaphore(ThreadParameter\Semaphore)
EndProcedure


Procedure Main()
  
  Protected Exit.b
  Protected Event.i
  Protected Thread.i
  ;Protected ThreadParameter.ThreadParameterStructure
  
  #Width  = 400
  #Height = 400
  #Space = 10
  
  OpenWindow(#MAIN_WINDOW, 100, 100, #Width, #Height, "Main Window")
  EditorGadget(#EDITOR_WINDOW, #Space, #Space, #Width - (#Space * 2), #Height - MenuHeight() - (#Space * 2), #PB_Editor_ReadOnly)
  
  CreateMenu(#MENU, WindowID(#MAIN_WINDOW))
  MenuTitle("File")
  MenuItem(#FILE_RUN,  "Run")
  MenuItem(#FILE_QUIT, "Quit")
  
  ThreadParameter\Semaphore = CreateSemaphore()
  
  BindEvent(#Thread_AddText, @Test())
  
  Repeat
    Select WaitWindowEvent()
      Case #PB_Event_CloseWindow
        Exit = #True
        
;       Case #Thread_AddText
;         AddGadgetItem(#EDITOR_WINDOW, -1, PeekS(EventData()))
;         ; Goto last character (auto scroll)
;         SendMessage_(GadgetID(#EDITOR_WINDOW), #EM_SETSEL, -1, -1)
;         SignalSemaphore(ThreadParameter\Semaphore)
        
      Case #Thread_Finished
        DisableMenuItem(#MENU, #FILE_RUN, #False)
        
      Case #PB_Event_Menu
        Event = EventMenu()
        Select Event
          Case #FILE_RUN
            DisableMenuItem(#MENU, #FILE_RUN, #True)
            ThreadParameter\Thread = CreateThread(@Thread(), @ThreadParameter)
            
          Case #FILE_QUIT
            Exit = #True
        EndSelect
    EndSelect
  Until Exit
  
  ; Kill thread if needed...
  If IsThread(Thread)
    ThreadParameter\Exit = #True
    If WaitThread(ThreadParameter\Thread, 1000) = 0
      KillThread(ThreadParameter\Thread)
    EndIf
  EndIf
  
  FreeSemaphore(ThreadParameter\Semaphore)
  
  FreeMenu(#MENU)
  CloseWindow(#MAIN_WINDOW)
  
EndProcedure

Procedure Thread(*Parameter.ThreadParameterStructure)
  
  Protected LoopCnt.i
  Protected Output.s
  
  For LoopCnt = 1 To 10000
    Output = "This string is number " + Str(LoopCnt)
    
    ; Send output to gui and wait for gui to process...
    PostEvent(#Thread_AddText, #Null, #Null, #Null, @Output)
    WaitSemaphore(*Parameter\Semaphore)
  Next
  
  PostEvent(#Thread_Finished, #Null, #Null, #Null, #Null)
  
EndProcedure

Main()
I also eliminated your CreateSemaphore() marathon :wink:

Bernd
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: Gui menu select blocks thread ?

Post by srod »

Yes Windows will enter a modal event loop when a menu is opened which will block the main process's message retrieval. Your thread still runs, but it is of course waiting for your semaphore to be signaled which won't happen until Window's releases the message queue etc.

You can make the menu modeless with the #MNS_MODELESS flag, but you'll need to do this through API and you'll end up with a menu which won't behave the way your user's expect! :)

**EDIT : now I didn't think of using BindEvent()! :) Nice one.
I may look like a mule, but I'm not a complete ass.
User avatar
skywalk
Addict
Addict
Posts: 4211
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

Re: Gui menu select blocks thread ?

Post by skywalk »

Nice 8)
I did not expect BindEvent() to update the gui while the user interacts with the gui?
So, BindEvent()'s can access GUI elements, while our threads cannot.
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: Gui menu select blocks thread ?

Post by srod »

The event procedures are still being called in the context of the main process so there is no problem there. The menu modal loop will ensure that all controls still receive their paint events (else the controls will appear to freeze whilst a menu is displayed) and it is invariably here that our bound event procedures are being called.
I may look like a mule, but I'm not a complete ass.
User avatar
skywalk
Addict
Addict
Posts: 4211
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

Re: Gui menu select blocks thread ?

Post by skywalk »

Yes, dropping bindevent() and applying the gui update in the main event loop, causes the original complaint of locking the thread. bindevent() is updating in parallel with the main event loop. Our threads do not have the same luxury.
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
User avatar
Martt
User
User
Posts: 18
Joined: Sat Dec 23, 2017 4:03 pm
Location: The Netherlands

Re: Gui menu select blocks thread ?

Post by Martt »

infratec wrote:Hi,

the thread is still running, but paused by waiting on the semaphore.
The main eventloop is stooped by your menu access, so it can not process the Events.

You can avoid this by using BindEvent()

I also eliminated your CreateSemaphore() marathon :wink:

Bernd
Thanks for the BindEvent. I did not expect Windows to stop the eventloop on a single menu click :oops: .
And the CreateSemaphore marathon, yeah.... Kind of dumb.

Thanks for the answer. Now the project can continue....
User avatar
mk-soft
Always Here
Always Here
Posts: 6207
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: Gui menu select blocks thread ?

Post by mk-soft »

For easy work between threads and GUI i have wrote a module. Perhaps help this...

Link: Module ThreadToGUI
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
User avatar
skywalk
Addict
Addict
Posts: 4211
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

Re: Gui menu select blocks thread ?

Post by skywalk »

Thanks mk-soft and infratec for pointing out the BindEvent() behavior with threads. :idea:
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
Post Reply