wxMSW : Event Handler is multi-threaded???

If you are using the main C++ distribution of wxWidgets, Feel free to ask any question related to wxWidgets development here. This means questions regarding to C++ and wxWidgets, not compile problems.
Post Reply
Virchanza
Experienced Solver
Experienced Solver
Posts: 78
Joined: Sun Jul 19, 2009 6:12 am

wxMSW : Event Handler is multi-threaded???

Post by Virchanza » Sun Jun 13, 2010 10:28 am

OK I'm getting really weird behaviour from wxMSW. It appears to be able to process two events at the same time, (i.e. it runs the code of two event handlers simultaneously), which indicates to me that the event handler is multi-threaded.



But the event handler isn't supposed to be multi-threaded... is it?! If that were the case then I would have to use Critical Sections when accessing member objects or global objects from within my event handler functions.



I've put together a minimalistic sample program that exhibits what I'm on about.



OK my program contains two threads:

1) The Main Thread (which displays a Dialog Box)

2) The Secondary Thread which is Joinable



The Secondary Joinable Thread is Create'd and Run'd from within the constructor of the Main Dialog Box.



The Secondary Joinable Thread doesn't actually do anything. It just runs in an eternal loop until the Main Thread invokes the Wait member function on the thread, at which point the Secondary Thread will stall for 5 seconds before exiting.



On the Main Dialog Box, there is a button that says "Sleep for 5 seconds". When this button is clicked, it invokes "Wait" on the secondary thread. The "Wait" member function will take at least 5 seconds to return, so you would expect the Main Dialog Box to freeze up for 5 seconds and not react to any input (I mean how can any other event handlers be called if the BUTTON_CLICKED event handler hasn't returned yet?)



My program works exactly as intended on Ubuntu using wxGTK (i.e. the GUI freezes for 5 seconds). However on MS-Windows using wxMSW, the Main Dialog Box doesn't freeze when the Wait function is called. Not only that, other event handlers are invoked before the BUTTON_CLICKED event handler returns! That makes me think that the Event Handler is multi-threaded and that it can invoke multiple event handlers simultaneously.



Run my sample program. A dialog box will appear. Do the following:



1) Click on the second tab on the Notebook. A message box will appear telling you that it's about to change tab (I call the wxMessageBox function from within the PAGE_CHANGING event handler). Click OK on the message box, and notice that the tab changes normally. Navigate back to the first tab, and again you'll see the same message box appear.



2) Now, click the "Sleep for 5 seconds" button, and then straight away try to navigate to the second tab on the Notebook. You would expect the dialog box to be frozen because the BUTTON_CLICKED event handler hasn't returned yet, however it lets you navigate to the second tab... and not only that, it even triggers the PAGE_CHANGING event which displays the message box! (But how the hell can the PAGE_CHANGING event be processed if the button's BUTTON_CLICKED event handler hasn't yet returned?????). The Notebook will immediately change its active tab... however it won't display the new panel until the BUTTON_CLICKED event handler returns.



I'm confused by all this. I've never thought that the Event Handler could invoke more than one event handler function at the same time. If this were the case, then we'd need to used Critical Sections all over the place in our GUI code.



Here's my sample program:

Code: Select all


/* ========== BEGIN: All the header files needed (auto-generated by wxFormBuilder) ========== */

#include "wx/wxprec.h"



#ifdef WX_PRECOMP

#include "wx_pch.h"

#endif



#include <wx/app.h>

#include <wx/thread.h>

#include <wx/msgdlg.h>

#include <wx/string.h>

#include <wx/button.h>

#include <wx/gdicmn.h>

#include <wx/font.h>

#include <wx/colour.h>

#include <wx/settings.h>

#include <wx/stattext.h>

#include <wx/sizer.h>

#include <wx/panel.h>

#include <wx/bitmap.h>

#include <wx/image.h>

#include <wx/icon.h>

#include <wx/notebook.h>

#include <wx/dialog.h>

/* ========== END: All the header files needed (auto-generated by wxFormBuilder) ========== */





/* ========== BEGIN: The global objects used by more than one thread ========== */

wxCriticalSection g_cs;

bool g_is_main_thread_waiting = false;  /* Must use Critical Section when accessing this */

/* ========== END: The global objects used by more than one thread ========== */



class Class_For_Secondary_Joinable_Thread : public wxThread {

public:



    Class_For_Secondary_Joinable_Thread() : wxThread(wxTHREAD_JOINABLE) {}



    void *Entry()

    {

        for (;;)

        {

            /* Jut keep looping eternally until

               the main thread changes the

               value of g_is_main_thread_waiting */



            g_cs.Enter();



            bool const is_waiting = g_is_main_thread_waiting;



            g_is_main_thread_waiting = false;



            g_cs.Leave();



            if (is_waiting)

            {

                /* When main thread calls Wait,

                   wait 5 seconds and then end

                   the thread */



                this->Sleep(5000);

                return 0;

            }

        }

    }

};





class Main_Dialog : public wxDialog

{

protected:



    wxThread *p_secondary_joinable_thread;  /* Keep track of the secondary thread */



    wxButton* m_button1;

    wxNotebook* m_notebook1;

    wxPanel* m_panel1;

    wxStaticText* m_staticText2;

    wxPanel* m_panel2;

    wxStaticText* m_staticText3;



    void OnClose( wxCloseEvent& event );

    void OnButtonClick_Sleep( wxCommandEvent& event );

    void OnTabChanging( wxNotebookEvent& event );



public:

    Main_Dialog( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("Blah blah blah"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 296,254 ), long style = wxDEFAULT_DIALOG_STYLE );



    ~Main_Dialog()

    {

        /* We can't just exit our program instantaneously,

           we must wait for the secondary thread to end */



        if (p_secondary_joinable_thread)

        {

            g_cs.Enter();



            g_is_main_thread_waiting = true;



            g_cs.Leave();



            p_secondary_joinable_thread->Wait(); /* This function takes 5 seconds to return! */

        }

    }

};



class Class_For_My_App : public wxApp

{

public:

    virtual bool OnInit()

    {

        Main_Dialog *dlg = new Main_Dialog(0L);



        if (!dlg)

            return false;



        dlg->Show();



        return true;

    }

};



IMPLEMENT_APP(Class_For_My_App);



Main_Dialog::Main_Dialog( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style )

{

    /* ========== BEGIN: The stuff auto-generated by wxFormBuilder ========== */



	this->SetSizeHints( wxDefaultSize, wxDefaultSize );



	wxBoxSizer* bSizer1;

	bSizer1 = new wxBoxSizer( wxVERTICAL );



	m_button1 = new wxButton( this, wxID_ANY, wxT("Sleep for 5 seconds"), wxDefaultPosition, wxDefaultSize, 0 );

	bSizer1->Add( m_button1, 0, wxALL|wxALIGN_CENTER_HORIZONTAL, 5 );



	m_notebook1 = new wxNotebook( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 );

	m_panel1 = new wxPanel( m_notebook1, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );

	wxBoxSizer* bSizer3;

	bSizer3 = new wxBoxSizer( wxVERTICAL );



	m_staticText2 = new wxStaticText( m_panel1, wxID_ANY, wxT("\n\nThis is the wxPanel for Tab 1\n\nONE ONE ONE ONE ONE"), wxDefaultPosition, wxDefaultSize, 0 );

	m_staticText2->Wrap( -1 );

	bSizer3->Add( m_staticText2, 0, wxALL|wxALIGN_CENTER_HORIZONTAL, 5 );



	m_panel1->SetSizer( bSizer3 );

	m_panel1->Layout();

	bSizer3->Fit( m_panel1 );

	m_notebook1->AddPage( m_panel1, wxT("Tab 1"), true );

	m_panel2 = new wxPanel( m_notebook1, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );

	wxBoxSizer* bSizer4;

	bSizer4 = new wxBoxSizer( wxVERTICAL );



	m_staticText3 = new wxStaticText( m_panel2, wxID_ANY, wxT("\n\nThis is the wxPanel for Tab 2\n\nTWO TWO TWO TWO TWO"), wxDefaultPosition, wxDefaultSize, 0 );

	m_staticText3->Wrap( -1 );

	bSizer4->Add( m_staticText3, 0, wxALL|wxALIGN_CENTER_HORIZONTAL, 5 );



	m_panel2->SetSizer( bSizer4 );

	m_panel2->Layout();

	bSizer4->Fit( m_panel2 );

	m_notebook1->AddPage( m_panel2, wxT("Tab 2"), false );



	bSizer1->Add( m_notebook1, 1, wxEXPAND | wxALL, 5 );



	this->SetSizer( bSizer1 );

	this->Layout();



	// Connect Events

	this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( Main_Dialog::OnClose ) );

	m_button1->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( Main_Dialog::OnButtonClick_Sleep ), NULL, this );

	m_notebook1->Connect( wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING, wxNotebookEventHandler( Main_Dialog::OnTabChanging ), NULL, this );



    /* ========== END: The stuff auto-generated by wxFormBuilder ========== */



    p_secondary_joinable_thread = new Class_For_Secondary_Joinable_Thread;



    if (     !p_secondary_joinable_thread

          || (p_secondary_joinable_thread->Create() != wxTHREAD_NO_ERROR)

          || (p_secondary_joinable_thread->Run() != wxTHREAD_NO_ERROR)

        )

    {

        delete p_secondary_joinable_thread; /* OK to call delete on a null pointer */

        p_secondary_joinable_thread = 0;



        this->Destroy();

    }

}



void Main_Dialog::OnTabChanging( wxNotebookEvent& event )

{

    wxMessageBox(wxT("About to change tab"));

}



void Main_Dialog::OnClose( wxCloseEvent& event )

{

    this->Destroy();

}



void Main_Dialog::OnButtonClick_Sleep( wxCommandEvent& event )

{

    g_cs.Enter();



    g_is_main_thread_waiting = true;



    g_cs.Leave();



    p_secondary_joinable_thread->Wait(); /* This function takes at least 5 seconds to return! */



    /* OK, now re-create the secondary thread */



    delete p_secondary_joinable_thread;



    p_secondary_joinable_thread = new Class_For_Secondary_Joinable_Thread;



    if (     !p_secondary_joinable_thread

          || (p_secondary_joinable_thread->Create() != wxTHREAD_NO_ERROR)

          || (p_secondary_joinable_thread->Run() != wxTHREAD_NO_ERROR)

        )

    {

        delete p_secondary_joinable_thread; /* OK to call delete on a null pointer */

        p_secondary_joinable_thread = 0;



        this->Destroy();

    }

}



Now here's something else to try. In my code, look for the "Main_Dialog::OnButtonClick_Sleep" function, and replace it with this:


Code: Select all


void Main_Dialog::OnButtonClick_Sleep( wxCommandEvent& event )

{

    ::wxSleep(5);

}



After having made this change, re-run the program. This time around, when you click the "Sleep for 5 seconds" button, the GUI actually freezes us as you would expect it to. The GUI doesn't quite ignore all input though, instead it appears to add events to some sort of queue, and all of these events are processed once the BUTTON_CLICKED event handler has returned (this happens with wxGTK too).


So I've got two questions about wxMSW:
1) Why does the Event Handler system appear to be multi-threaded?
2) Why should the call to "::wxSleep(5)" be any different than the call to "p_secondary_thread->Wait()"? I would expect both calls to make the GUI freeze.


Can anyone shed some light on this?

Virchanza
Experienced Solver
Experienced Solver
Posts: 78
Joined: Sun Jul 19, 2009 6:12 am

Post by Virchanza » Mon Jun 14, 2010 9:46 am

Anybody?

Can anyone explain how another event handler function begins execution before the first event handler function returns?

Does anyone agree with me that the Event Handler System seems to be multi-threaded on wxMSW somehow (I mean how else can it call two functions at once?!).

What's going on?

catalin
Moderator
Moderator
Posts: 1597
Joined: Wed Nov 12, 2008 7:23 am
Location: Romania

Post by catalin » Mon Jun 14, 2010 10:18 am

There is no "the Event Handler". Calling it event handling system makes more sense IMO, although not sure it is the "official terminology", if there is any at all.

During Wait() some GUI related events are processed by using Yield* calls. Search the docs for 'yield' term.
The simplest purpose to mention is to have the GUI repainted when the main thread is waiting (and in this case I think the GUI repaints under GTK too, not only MSW). Otherwise i.e. if you overlap another window with your app's main frame you'll see artifacts. Example 2: interactively cancel a thread before it ends its iteration (IOW push a button -> handle the event and cancel a secondary thread).
This behavior was changed/improved to some extent in 2.9, see Ticket #10320, and some [other] fixes were done since, so using the trunk would be best.

All events in this case are handled in the main thread. So it is not necessarily related to multi-threading. You can get similar behavior by calling Yield functions from an executing event handler and thus have other events be handled (not encouraged due to re-entrancy risks).

Virchanza
Experienced Solver
Experienced Solver
Posts: 78
Joined: Sun Jul 19, 2009 6:12 am

Post by Virchanza » Tue Jun 15, 2010 9:53 am

Code: Select all

 	
During Wait() some GUI related events are processed by using Yield* calls.
Amazing. I'm actually amazed. Does this not cause all sorts of problems?

I had no idea at all that two of my event handler routines could be executed simultaneously. What if these two functions access shared resources? I don't make any allowances for that at all in my code, because I assume that only one event handler routine can execute at a time.

Really I'm amazed by this, I've never heard of an Event Handling System exhibiting multi-threaded behaviour like this.

Do you know any way that I can prevent this multi-threaded-event-handling behaviour? I need the Event Handling System to only be inside one event handler routine at a time. I need the GUI to be frozen when I invoke the Wait method on a thread object from within one of my event handler routines.

Let me give you a quick example. I have multiple buttons on my dialog box.

1) One button opens a network interface for processing.
2) Another button sends ARP requests.
3) Another button sends TCP/IP packets.

It would be disastrous for the "Send ARP requests" routine to begin execution before the "Open Network Interface" routine ends execution. (I know you're going to suggest that the latter two buttons be disabled until the network interface is open, but I don't want to do that, and I shouldn't have to do that).

Does wxWidgets have a function which just freezes the GUI and the Event Handler System? If so, I could call it before I invoke the Wait method on the secondary thread:

Code: Select all

p_my_main_dialog_box->Freeze_and_dont_accept_any_input_or_process_any_events();

p_thread->Wait();
Or perhaps there's a function that turns off multi-threading altogether for the Event Handler System?

Code: Select all

wxEventHandler::Multithreaded(false);
I've only tested out my program on wxGTK and wxMSW.

My program works absolutely fine on Linux because the Event Handling System doesn't exhibit any multi-threaded behaviour.

On MS-Windows however, my program acts weird and eventually crashes because two event handler routines get executed simultaneously.

catalin
Moderator
Moderator
Posts: 1597
Joined: Wed Nov 12, 2008 7:23 am
Location: Romania

Post by catalin » Tue Jun 15, 2010 12:00 pm

Whether the current implementation is good or bad is at least debatable. But I'm sure it will help you if at least you took a minute to understand how it works.

As I wrote you in the previous post, there is nothing multi-threaded about this scenario. All events that are handled when the main thread calls Wait are handled in the main thread - let's count it: one thread :)
When a yield function is called, the execution of the current routine is suspended and events are selected to be handled - this happens inside the yield call. When all [selectable] events are handled, the suspended routine is resumed. In a few words: there is nothing parallel about it.
As a consequence, if Wait is called from an event handling routine, the Yield call implicitly suspends that routine.

Now it should be clear that there are no concurrency risks, so no mutexes etc. are needed. But there is a risk of "re-entrance" - that is calling yield from a previous yield call - which would be a design bug if it happened.

But in your case, I understand that the problem is that you need event B to be handled after event A and that is not working properly as the events are not ordered. IOW it is not necessarily a problem that Wait allows for other events to execute, but the fact that in this way event B may come before event A.
IMO events based functionality does not guarantee/enforce an order of events. If you need your app to only react to a specific order of events you need to guard it yourself, not rely on the fact that event B must not be triggered before event A. I believe this is the nature of events - consider the possibility that they appear in random order. And there are quite many such occurrences (for some read "defects") beyond the reach of wxW, in MSW native API, especially regarding sockets.

Another thing that is good to keep in mind is that wxW is not about having identical implementation for all platforms, but similar implementations based on native widgets for each platform. The affected part here may be event generation that could differ from one platform to another (or of course a bug in wxW; I can't vouch for any of the two).

I don't know of any way to turn off yield-ing functionality for calls like wxThread::Wait.

Virchanza
Experienced Solver
Experienced Solver
Posts: 78
Joined: Sun Jul 19, 2009 6:12 am

Post by Virchanza » Wed Jun 16, 2010 1:36 pm

OK I understand what you're saying about there being only one thread.

The behaviour exhibited by the Event Handling System in wxMSW is very similar to interrupts on microcontrollers. With microcontrollers, when an interrupt is triggered, the currently executing function has all of its CPU registers saved somewhere in RAM, then the interrupt routine gets called, and then the CPU registers are restored and the original function continues its execution as if it were never stalled.

This is the first time I've seen interrupts on a PC though!

For now I'm gonna use a band-aid solution to make sure only one routine gets called at a time:

Code: Select all

wxCriticalSection g_cs_for_event_handling_routines;

void OnButtonClick_Button1(wxCommandEvent &event)
{
    wxCriticalSectionLocker locker (g_cs_for_event_handling_routines);

    ...
    ...
}

void OnButtonClick_Button2(wxCommandEvent &event)
{
    wxCriticalSectionLocker locker (g_cs_for_event_handling_routines);

    ...
    ...
}
Also I need to find a way of freezing the GUI so that it doesn't accept input. I see that wxWindow has a method called Freeze but it doesn't do what I want.

One other thing, are you sure we can't rely on the Event Handling System to ensure the order of events? I mean let's say the user clicks Button1 and then later clicks Button2... well I think everyone expects the routine for Button1 to get executed before the routine for Button2!

Virchanza
Experienced Solver
Experienced Solver
Posts: 78
Joined: Sun Jul 19, 2009 6:12 am

Post by Virchanza » Wed Jun 16, 2010 1:38 pm

OK I understand what you're saying about there being only one thread.

The behaviour exhibited by the Event Handling System in wxMSW is very similar to interrupts on microcontrollers. With microcontrollers, when an interrupt is triggered, the currently executing function has all of its CPU registers saved somewhere in RAM, then the interrupt routine gets called, and then the CPU registers are restored and the original function continues its execution as if it were never stalled.

This is the first time I've seen interrupts on a PC though!

For now I'm gonna use a band-aid solution to make sure only one routine gets called at a time:

Code: Select all

wxCriticalSection g_cs_for_event_handling_routines;

void OnButtonClick_Button1(wxCommandEvent &event)
{
    wxCriticalSectionLocker locker (g_cs_for_event_handling_routines);

    ...
    ...
}

void OnButtonClick_Button2(wxCommandEvent &event)
{
    wxCriticalSectionLocker locker (g_cs_for_event_handling_routines);

    ...
    ...
}
Also I need to find a way of freezing the GUI so that it doesn't accept input. I see that wxWindow has a method called Freeze but it doesn't do what I want.

One other thing, are you sure we can't rely on the Event Handling System to ensure the order of events? I mean let's say the user clicks Button1 and then later clicks Button2... well I think everyone expects the routine for Button1 to get executed before the routine for Button2!

Virchanza
Experienced Solver
Experienced Solver
Posts: 78
Joined: Sun Jul 19, 2009 6:12 am

Post by Virchanza » Wed Jun 16, 2010 1:39 pm

OK I understand what you're saying about there being only one thread.

The behaviour exhibited by the Event Handling System in wxMSW is very similar to interrupts on microcontrollers. With microcontrollers, when an interrupt is triggered, the currently executing function has all of its CPU registers saved somewhere in RAM, then the interrupt routine gets called, and then the CPU registers are restored and the original function continues its execution as if it were never stalled.

This is the first time I've seen interrupts on a PC though!

For now I'm gonna use a band-aid solution to make sure only one routine gets called at a time:

Code: Select all

wxCriticalSection g_cs_for_event_handling_routines;

void OnButtonClick_Button1(wxCommandEvent &event)
{
    wxCriticalSectionLocker locker (g_cs_for_event_handling_routines);

    ...
    ...
}

void OnButtonClick_Button2(wxCommandEvent &event)
{
    wxCriticalSectionLocker locker (g_cs_for_event_handling_routines);

    ...
    ...
}
Also I need to find a way of freezing the GUI so that it doesn't accept input. I see that wxWindow has a method called Freeze but it doesn't do what I want.

One other thing, are you sure we can't rely on the Event Handling System to ensure the order of events? I mean let's say the user clicks Button1 and then later clicks Button2... well I think everyone expects the routine for Button1 to get executed before the routine for Button2!

catalin
Moderator
Moderator
Posts: 1597
Joined: Wed Nov 12, 2008 7:23 am
Location: Romania

Post by catalin » Wed Jun 16, 2010 2:18 pm

Virchanza wrote:The behaviour exhibited by the Event Handling System in wxMSW is very similar to interrupts on microcontrollers.
No, sorry but you prove you still do not understand and I'm not sure I can and have the "urge" to rephrase...
Only the use of i.e. wxYield() will make the app behave as you describe. It is not something that defines events handling, and with a little bit of attention I'm sure you (as everybody else using this library) can make it work according to your needs.

Using wxCriticalSection in this case totally puts you on the wrong track. Did you try it yet? What happens in this scenario: when button1 is pushed, if the handler for button1 contains a Yield call after it has entered the critical section, and in consequence the handler for button2 is executed and the same thread will try to enter the same critical section, what will happen then?
a) deadlock - most likely;
b) nothing and the whole use of critical sections is useless, in case recursive acquirement is permitted on your system.
Virchanza wrote:Also I need to find a way of freezing the GUI so that it doesn't accept input.
IMO you just need a better design, and also to get away from what tends to be a fixed idea about wxW events handling and try to grasp it again.
Virchanza wrote:One other thing, are you sure we can't rely on the Event Handling System to ensure the order of events? I mean let's say the user clicks Button1 and then later clicks Button2... well I think everyone expects the routine for Button1 to get executed before the routine for Button2!
I'm sure in practice you will not find anything to contradict the above, including wxW. What I was saying is not only for buttons, where you can control the order of clicking them. It was more general about events (see socket events, or events generated by threads) where the order in which they are fired is not up to you.

p.s. You can delete 2 of the 3 identical posts. This forum can be quite slow sometimes, but some patience might help..

Virchanza
Experienced Solver
Experienced Solver
Posts: 78
Joined: Sun Jul 19, 2009 6:12 am

Post by Virchanza » Wed Jun 16, 2010 2:59 pm

About 15 minutes after I posted my last post, I realised I had entered the same Critical Section twice in the same thread.

I would have had to do something like this instead:

Code: Select all

class Locker_Only_Enter_One_Routine {

protected:

    static bool already_in_a_routine;

    bool is_this_locker_valid;

public:

    Locker_Only_Enter_One_Routine()
    {
        if (already_in_a_routine)
        {
            is_this_locker_valid = false;
        }
        else
        {
            is_this_locker_valid = true;
            already_in_a_routine = true;
        }
    }

    ~Locker_Only_Enter_One_Routine()
    {
        if (is_this_locker_valid)
            already_in_a_routine = false;
    }

    bool IsValid() const
    {
        return is_this_locker_valid;
    }  
};

bool Locker_Only_Enter_One_Routine::already_in_a_routine = false;

void OnButtonClick_Button1(wxCommandEvent &event)
{
    Locker_Only_Enter_One_Routine locker;

    if (!locker.IsValid())
    {
        /* Wups we're already in another routine */
    }
    ...
    ...
}

void OnButtonClick_Button2(wxCommandEvent &event)
{
    Locker_Only_Enter_One_Routine locker;

    if (!locker.IsValid())
    {
        /* Wups we're already in another routine */
    }
    ...
    ...
}
Only the use of i.e. wxYield() will make the app behave as you describe. It is not something that defines events handling, and with a little bit of attention I'm sure you (as everybody else using this library) can make it work according to your needs.
The thing is that I don't want to call Yield. All I want to do is make the main thread stall until the secondary joinable thread terminates. This is what the Wait method is for. Unfortunately, the Wait method, on wxMSW, does not make the main thread stall. It cause some sort of interrupt whereby the currently executing function is put on hold while another function executes.

If wxWidgets or wxMSW does not provide a facility for manually disabling this Interrupt Behaviour then I have two choices:
1) Accept it, and re-design my code.
2) Not accept it, and use some sort of Locker class as I described above.

Right now my decision is to not accept it.

Does anyone know a way of freezing the GUI so that it doesn't acknowledge any input or process any events? This would be the best solution because then I wouldn't have to do crazy stuff like make a Locker class.

Edit: I'm gonna play around with wxWindow::Disable and see if it does what I want. I'll try:

Code: Select all

my_main_dialog_box->Disable();
p_thread->Wait();
my_main_dialog_box->Enable();
I'm in Ubuntu right now so I have to reboot to try this in MS-Windows.

yuri
Earned some good credits
Earned some good credits
Posts: 104
Joined: Thu Apr 09, 2009 4:58 pm
Location: Russia

Post by yuri » Wed Jun 16, 2010 7:41 pm

It is not common in wx to get event while processing some other, but it indeed happens. Like, upon pressing toolbar button you get wxCommandEvent. And while processing it you present user with dialog, wait for user action then close dialog, then return from original message handler. While you waiting for dialog you may get quite a few other events: paint, timers etc etc. This has nothing to do with multy-threading and most of the time is a very nice feature (dialogs!).

And if you take a look at wxThread::Wait() source (it is there, like src/msw/thread.cpp) you'll see that it would process other events while waiting.

If you want to really block main thread - just use ::wxMilliSleep() + thread state poll cycle or something. This way wx would really have no chance to spoil it for you :)
But really, having responsive GUI while waiting looks rather like bonus, not problem to code around. And wx makes it easy to disable/enable given controls to conditionally block input from them, so please consider this approach too.

Virchanza
Experienced Solver
Experienced Solver
Posts: 78
Joined: Sun Jul 19, 2009 6:12 am

Post by Virchanza » Thu Jun 17, 2010 8:13 am

I solved me problem, my program works fine on wxMSW now.

I replaced:

Code: Select all

psniff->Wait();
with

Code: Select all

{
    wxWindowDisabler obj;
    psniff->Wait();
}
No need to write my own crazy locker class.

Virchanza
Experienced Solver
Experienced Solver
Posts: 78
Joined: Sun Jul 19, 2009 6:12 am

Post by Virchanza » Sat Jun 19, 2010 9:07 am

catalin wrote:Using wxCriticalSection in this case totally puts you on the wrong track. Did you try it yet? What happens in this scenario: when button1 is pushed, if the handler for button1 contains a Yield call after it has entered the critical section, and in consequence the handler for button2 is executed and the same thread will try to enter the same critical section, what will happen then?
a) deadlock - most likely;
b) nothing and the whole use of critical sections is useless, in case recursive acquirement is permitted on your system.
It took me a few days to realise it, but you just gave me a perfect example of how the "Interrupt-enabled Event Handling System" can cause a problem (other than the original problem I had of course).

Let's say we have two event handling routines, one for clicking Button1, and one for clicking Button2.

Let's say that the Button1 routine enters a critical section and then invokes the Wait method on a secondary thread.

Let's say that the Button2 routine enters the same critical section.

If the Button1 routine gets interrupted, then it's possible for the Button2 routine to enter the same critical section.

Leading to, as you said:
a) deadlock - most likely;
b) nothing and the whole use of critical sections is useless, in case recursive acquirement is permitted on your system.
Do I misunderstand something, or is this a realistic possibility?

Post Reply