Prevent re-entry but do not discard the event

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.
Virchanza
I live to help wx-kind
I live to help wx-kind
Posts: 172
Joined: Sun Jul 19, 2009 6:12 am

Prevent re-entry but do not discard the event

Post by Virchanza »

I have written an event handler, and in order to prevent re-entry, I do:

{

static mutex mtx;
if ( false == mtx.try_lock() ) return;

// event handler code goes here

mtx.unlock();
}

Instead of just discarding the event though, I would like to re-queue it, something like:

{

static mutex mtx;
if ( false == mtx.try_lock() )
{
this.RequeueEvent(event);
return;
}

// event handler code goes here

mtx.unlock();
}

Is this possible with wxWidgets? How would I do it?
User avatar
doublemax
Moderator
Moderator
Posts: 19160
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Prevent re-entry but do not discard the event

Post by doublemax »

Did you actually experience re-entrancy your app?

The wxWidgets event system is single threaded, an event handler can not be interrupted by another event, unless you do something in your event handler that causes triggering the event loop.
Use the source, Luke!
Virchanza
I live to help wx-kind
I live to help wx-kind
Posts: 172
Joined: Sun Jul 19, 2009 6:12 am

Re: Prevent re-entry but do not discard the event

Post by Virchanza »

doublemax wrote: Sat May 27, 2023 12:26 pm Did you actually experience re-entrancy your app?

The wxWidgets event system is single threaded, an event handler can not be interrupted by another event, unless you do something in your event handler that causes triggering the event loop.
My event handler calls '::wxSafeYield'.
ONEEYEMAN
Part Of The Furniture
Part Of The Furniture
Posts: 7477
Joined: Sat Apr 16, 2005 7:22 am
Location: USA, Ukraine

Re: Prevent re-entry but do not discard the event

Post by ONEEYEMAN »

Hi,
What event is that? You own?
Why does UT happen to be re-entered?

Thank you.
Virchanza
I live to help wx-kind
I live to help wx-kind
Posts: 172
Joined: Sun Jul 19, 2009 6:12 am

Re: Prevent re-entry but do not discard the event

Post by Virchanza »

I have event handlers that cannot be re-entered, for example the 'OnClick' event for a wxButton. I also have 'CallAfter' methods that cannot be re-entered. I'm thinking that I should handle the 'CallAfter' methods as follows:

Code: Select all

template<typename Lambda>
struct OnScopeExit {
    Lambda f;
    explicit OnScopeExit(Lambda arg) : f(arg) {}
    ~OnScopeExit(void) { f(); }
};

void Dialog_Main::CallAfter_ReceiveFileShareName(uint32_t const ip, string str)
{
    static std::binary_semaphore sem(1);

    if ( false == sem.try_acquire() )
    {
        this->CallAfter(&Dialog_Main::CallAfter_ReceiveFileShareName, ip, std::move(str));
        return;
    }

    OnScopeExit dummy0(  [](){ sem.release(); }  );

    // Remainder of method goes here
}
and I think I should handle the actual events as follows:

Code: Select all

void Dialog_Main::OnButtonClick_Send(wxCommandEvent &event)
{
    static std::binary_semaphore sem(1);

    if ( false == sem.try_acquire() )
    {
        this->QueueEvent( &event.Clone() );
        return;
    }

    OnScopeExit dummy0(  [](){ sem.release(); }  );
    
    // Remainder of method goes here
}
How does that look? Do I need to 'Clone' the event for QueueEvent?
The wxWidgets event system is single threaded, an event handler can not be interrupted by another event, unless you do something in your event handler that causes triggering the event loop.
I start another event loop when I call ::wxSafeYield.
Last edited by Virchanza on Sat May 27, 2023 9:32 pm, edited 1 time in total.
User avatar
doublemax
Moderator
Moderator
Posts: 19160
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Prevent re-entry but do not discard the event

Post by doublemax »

That looks like it could work, but i'm not sure if it could have negative side effects. Just try it out.
Use the source, Luke!
ONEEYEMAN
Part Of The Furniture
Part Of The Furniture
Posts: 7477
Joined: Sat Apr 16, 2005 7:22 am
Location: USA, Ukraine

Re: Prevent re-entry but do not discard the event

Post by ONEEYEMAN »

Hi,
Do you have a proof of repentance for a simple button click event?
If so - I'd like to see the code.

Unless you, yourself will call that handler inside the handler..
As doublemax pointed out - the event loop is one threaded and so even if you call wxYield() other events will be processed during this call, but not this one.

But maybe you shouldn't call Yield() in the first place? What's your scenario? Maybe we can help you avoid that call...

Thank you.
Virchanza
I live to help wx-kind
I live to help wx-kind
Posts: 172
Joined: Sun Jul 19, 2009 6:12 am

Re: Prevent re-entry but do not discard the event

Post by Virchanza »

ONEEYEMAN wrote: Sat May 27, 2023 1:47 pm the event loop is one threaded and so even if you call wxYield() other events will be processed during this call, but not this one.
I can't find anywhere in the documentation that says that the current event handler cannot be re-entered by a call to ::wxSafeYield.

Where do you get this info?
Virchanza
I live to help wx-kind
I live to help wx-kind
Posts: 172
Joined: Sun Jul 19, 2009 6:12 am

Re: Prevent re-entry but do not discard the event

Post by Virchanza »

By the way in my previous code snippet I should have used a binary_semaphore instead of a mutex, otherwise there will be thread-lock. An atomic_flag would work fine too.
Virchanza
I live to help wx-kind
I live to help wx-kind
Posts: 172
Joined: Sun Jul 19, 2009 6:12 am

Re: Prevent re-entry but do not discard the event

Post by Virchanza »

I have this working now the way I want it (pretty much). Here's what I do:

Code: Select all

std::atomic_flag myflag = ATOMIC_FLAG_INIT;

void Dialog_Main::CallAfter_ReceiveFileShareName_FTP(uintIP const ip, string str)
{
    assert( wxIsMainThread() );
    assert( false == str.empty() );

    if ( myflag.test_and_set() )
    {
        this->CallAfter(&Dialog_Main::CallAfter_ReceiveFileShareName_FTP, ip, std::move(str));
        return;
    }

    OnScopeExit dummy0(  [this](){ myflag.clear(); }  );

    // Do some processing here

    wxGetApp().SafeYieldFor(nullptr, static_cast<long>(wxEVT_CATEGORY_ALL) & ~static_cast<long>(wxEVT_CATEGORY_UNKNOWN));
    
    // Do some processing here

    wxGetApp().SafeYieldFor(nullptr, static_cast<long>(wxEVT_CATEGORY_ALL) & ~static_cast<long>(wxEVT_CATEGORY_UNKNOWN));

    // Do some processing here

    wxGetApp().SafeYieldFor(nullptr, static_cast<long>(wxEVT_CATEGORY_ALL) & ~static_cast<long>(wxEVT_CATEGORY_UNKNOWN));   
}
Note that I clear the bit for wxEVT_CATEGORY_UNKNOWN, as this is the category for all the 'CallAfter' events. If I don't clear that bit, then the invocation of SafeYieldFor processes the event that was generated by 'CallAfter', which means that it gets stuck in an endless loop.
Virchanza
I live to help wx-kind
I live to help wx-kind
Posts: 172
Joined: Sun Jul 19, 2009 6:12 am

Re: Prevent re-entry but do not discard the event

Post by Virchanza »

I wonder if it would have been simpler to use "IsYielding()" instead of an atomic_flag?

The only thing though is that I want to prevent all re-entry, not just the re-entry caused by Yield. For example if I have a modal dialog box with its own event loop then I want re-entry to be prevented there too.
ONEEYEMAN
Part Of The Furniture
Part Of The Furniture
Posts: 7477
Joined: Sat Apr 16, 2005 7:22 am
Location: USA, Ukraine

Re: Prevent re-entry but do not discard the event

Post by ONEEYEMAN »

Hi,
So now all you need is to have this in a function and call it in every single event handler.

But why do you need a wxYield()?

And as doublemax explained - there is only wxYield() re-entrancy. GUI event loop is single threaded.

Thank you.
Virchanza
I live to help wx-kind
I live to help wx-kind
Posts: 172
Joined: Sun Jul 19, 2009 6:12 am

Re: Prevent re-entry but do not discard the event

Post by Virchanza »

ONEEYEMAN wrote: Sun May 28, 2023 12:52 am And as doublemax explained - there is only wxYield() re-entrancy. GUI event loop is single threaded.
Also wxDialog::ShowModal.
ONEEYEMAN
Part Of The Furniture
Part Of The Furniture
Posts: 7477
Joined: Sat Apr 16, 2005 7:22 am
Location: USA, Ukraine

Re: Prevent re-entry but do not discard the event

Post by ONEEYEMAN »

Hi,
Not sure what you mean - message loop is single threaded independently of whether it's a dialog loop or the application one.

Thank you.
Virchanza
I live to help wx-kind
I live to help wx-kind
Posts: 172
Joined: Sun Jul 19, 2009 6:12 am

Re: Prevent re-entry but do not discard the event

Post by Virchanza »

ONEEYEMAN wrote: Sun May 28, 2023 11:58 am Hi,
Not sure what you mean - message loop is single threaded independently of whether it's a dialog loop or the application one.

Thank you.
Imagine the following scenario.

There are two threads in a program. The first thread is the GUI thread. The second thread is a worker thread.

Every 200 milliseconds, the worker thread invokes 'wxDialog::CallAfter' in order to send a message to the GUI thread.

In the GUI thread, let's say we enter one of the 'CallAfter' methods. Inside this method, we spend 420 milliseconds sorting a container, and so we split the sorting operation into ten separate steps, something like:

Code: Select all

Step1();
wxGetApp().SafeYield(nullptr, wxEVENT_CATEGORY_X);
Step2();
wxGetApp().SafeYield(nullptr, wxEVENT_CATEGORY_X);
Step3();
wxGetApp().SafeYield(nullptr, wxEVENT_CATEGORY_X);
Step4();
wxGetApp().SafeYield(nullptr, wxEVENT_CATEGORY_X);
Step5();
wxGetApp().SafeYield(nullptr, wxEVENT_CATEGORY_X);
..........and so on..........
In the above code snippet, every time we call 'SafeYield', it might process another message from the worker thread, and this will result in us re-entering the same 'CallAfter' method.
Post Reply