Prevent re-entry but do not discard the event
Prevent re-entry but do not discard the event
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?
{
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?
Re: Prevent re-entry but do not discard the event
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.
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!
Re: Prevent re-entry but do not discard the event
My event handler calls '::wxSafeYield'.
Re: Prevent re-entry but do not discard the event
Hi,
What event is that? You own?
Why does UT happen to be re-entered?
Thank you.
What event is that? You own?
Why does UT happen to be re-entered?
Thank you.
Re: Prevent re-entry but do not discard the event
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:
and I think I should handle the actual events as follows:
How does that look? Do I need to 'Clone' the event for QueueEvent?
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
}
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
}
I start another event loop when I call ::wxSafeYield.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.
Last edited by Virchanza on Sat May 27, 2023 9:32 pm, edited 1 time in total.
Re: Prevent re-entry but do not discard the event
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!
Re: Prevent re-entry but do not discard the event
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.
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.
Re: Prevent re-entry but do not discard the event
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?
Re: Prevent re-entry but do not discard the event
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.
Re: Prevent re-entry but do not discard the event
I have this working now the way I want it (pretty much). Here's what I do:
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.
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));
}
Re: Prevent re-entry but do not discard the event
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.
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.
Re: Prevent re-entry but do not discard the event
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.
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.
Re: Prevent re-entry but do not discard the event
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.
Not sure what you mean - message loop is single threaded independently of whether it's a dialog loop or the application one.
Thank you.
Re: Prevent re-entry but do not discard the event
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..........