How can a Model class(in a differnt thread) reference wxFrame? Topic is solved

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
tuk1
Earned some good credits
Earned some good credits
Posts: 114
Joined: Sun Oct 08, 2017 9:36 am

How can a Model class(in a differnt thread) reference wxFrame?

Post by tuk1 »

My button handler calls a Model(non UI) function which refreshes the data pool monitored by a virtual Listctrl

Code: Select all

void MAIN_FRAME::OnButtonClick_refreshLog( wxCommandEvent& WXUNUSED( event ) )
{
	
	this->t1 = std::thread( &MVC::refresh_data, MODEL );

	this->update_log_view();  // Updates ListCtrl view after data refresh
}
As can be seen above, the refresh function is called in a separate thread.
As can be seen below, the function body contains an infinite loop which sleeps every 5 seconds to prevent refreshing too often,

Code: Select all

void MVC::refresh_data()
{	
	while( true )
	{
		std::this_thread::sleep_for( std::chrono::seconds( 5 ) );		

		model_log.refresh_data();
	}

}
The problem is, after each data refresh I need a way of getting back to MAIN_FRAME to update the UI( MAIN_FRAME::update_log_view() ) so any new data is displayed.
I tried passing a ref to MAIN_FRAME as an arg of the refresh function, eg:

void MVC::refresh_data( MAIN_FRAME* );
MAIN_FRAME::Model.refresh_data(this);

But, I'm getting a lot of weird debug errors in unexpected places, making it difficult to work out where the problem lies.

Any ideas?
Last edited by tuk1 on Sat May 19, 2018 7:31 pm, edited 3 times in total.
wxWidgets(v3.2.2.1) - Vs2022(v143) - Win10(x64) - DialogBlocks(v5.16.5_Unicode)
User avatar
doublemax
Moderator
Moderator
Posts: 19116
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Referencing wxFrame from Model class

Post by doublemax »

You're not allowed to make any gui updates from secondary threads at all. Either send an event from the thread to the main frame that tells it to refresh or use a timer that polls some "dirty" flag and refreshes the gui if necessary.
Use the source, Luke!
tuk1
Earned some good credits
Earned some good credits
Posts: 114
Joined: Sun Oct 08, 2017 9:36 am

Re: Referencing wxFrame from Model class

Post by tuk1 »

I abandoned the thread solution and implemented wxTimer instead:

Code: Select all

#define ID_TIMER 10006

EVT_TIMER( ID_TIMER, MAIN_FRAME::OnTimer_refreshModelData )

void OnTimer_refreshModelData( wxTimerEvent& WXUNUSED( event ) );

wxTimer refresh_timer;
this->refresh_timer.SetOwner( this->GetEventHandler(), ID_TIMER );
this->refresh_timer.Start( 20000, false );
This does work, however the timer appears to fluctuate and is inaccurate.
Instead of the expected 20 secs, I get( using stopwatch and breakpoint in timer handler func) 15 secs, 10 secs, 14secs, 16secs.
The documentation says: "Its precision is platform-dependent, but in general will not be better than 1ms nor worse than 1s."
Is there something wrong with my implementation or is wxTimer not very accurate?
wxWidgets(v3.2.2.1) - Vs2022(v143) - Win10(x64) - DialogBlocks(v5.16.5_Unicode)
User avatar
doublemax
Moderator
Moderator
Posts: 19116
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Referencing wxFrame from Model class

Post by doublemax »

Instead of the expected 20 secs, I get( using stopwatch and breakpoint in timer handler func) 15 secs, 10 secs, 14secs, 16secs.
It shouldn't be that bad. Are you sure your measurements are correct? Try using wxLogDebug to output the value of wxGetLocalTime() at the beginning of the timer event handler.

Also, are there any long-running blocking tasks?
Use the source, Luke!
tuk1
Earned some good credits
Earned some good credits
Posts: 114
Joined: Sun Oct 08, 2017 9:36 am

Re: Referencing wxFrame from Model class

Post by tuk1 »

Hmmn, think it might have something to do with the breakpoint:

Free running, no breakpoint:

Code: Select all

void MAIN_FRAME::OnTimer_refreshModelData( wxTimerEvent& WXUNUSED( event ) )
{
	double d = wxGetLocalTime();
	wxString s = wxString::Format( wxT( "%f" ), d  );
	wxLogDebug( s );
	
	MODEL.refresh_data();
	this->update_log_view();		
}
Keeps perfect time, 20 sec increments, checked with stop watch also:
1526750989.000000
'Logger_v1_.exe' (Win32): Unloaded 'C:\Windows\SysWOW64\audiodev.dll'
'Logger_v1_.exe' (Win32): Unloaded 'C:\Windows\SysWOW64\WMVCORE.DLL'
'Logger_v1_.exe' (Win32): Unloaded 'C:\Windows\SysWOW64\WMASF.DLL'
'Logger_v1_.exe' (Win32): Unloaded 'C:\Windows\SysWOW64\wpdshext.dll'
'Logger_v1_.exe' (Win32): Unloaded 'C:\Windows\winsxs\x86_microsoft.windows.gdiplus_6595b64144ccf1df_1.1.7601.23894_none_5c0be957a009922e\GdiPlus.dll'
The thread 0x814 has exited with code 0 (0x0).
The thread 0x18c8 has exited with code 0 (0x0).
1526751009.000000
1526751029.000000

The thread 0xe78 has exited with code 0 (0x0).
1526751049.000000
The thread 0xe7c has exited with code 0 (0x0).
The thread 0xb74 has exited with code 0 (0x0).
The thread 0x300 has exited with code 0 (0x0).
The thread 0xb24 has exited with code 0 (0x0).
The thread 0xfc0 has exited with code 0 (0x0).
1526751069.000000
1526751089.000000
1526751109.000000
1526751129.000000
1526751149.000000
1526751169.000000
1526751189.000000
1526751209.000000
1526751229.000000
1526751249.000000
Obviously the logger will be skewed due to using local time with a breakpoint, but using 2 handheld stop watches( tested with internet time ) the breakpoint is still cutting in as shown in the OP, every 15 secs or so, and sometimes it breaks just 2-3 secs after running from the previous break:

Code: Select all

void MAIN_FRAME::OnTimer_refreshModelData( wxTimerEvent& WXUNUSED( event ) )
{
	double d = wxGetLocalTime();
	wxString s = wxString::Format( wxT( "%f" ), d  );
	wxLogDebug( s );
	
	MODEL.refresh_data();      //---->breakpoint here
	this->update_log_view();		
}
Technically it's working, as it does what it should at runtime with no breakpoint, but it is perplexing why a breakpoint should make a difference.
------------------------
doublemax wrote:Either send an event from the thread to the main frame that tells it to refresh....
Do you have a link/example how this would work?
wxWidgets(v3.2.2.1) - Vs2022(v143) - Win10(x64) - DialogBlocks(v5.16.5_Unicode)
User avatar
doublemax
Moderator
Moderator
Posts: 19116
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: How can a Model class(in a differnt thread) reference wxFrame?

Post by doublemax »

Do you have a link/example how this would work?
http://docs.wxwidgets.org/trunk/classwx_thread.html

But unless you're using secondary threads, you don't need this.
Technically it's working, as it does what it should at runtime with no breakpoint, but it is perplexing why a breakpoint should make a difference.
Are you talking about user breakpoints in the debugger or crashes/asserts?

If the program stops at a breakpoint, the whole execution stops. No events can be sent, received or handled. Of course that changes the timing.
Use the source, Luke!
tuk1
Earned some good credits
Earned some good credits
Posts: 114
Joined: Sun Oct 08, 2017 9:36 am

Re: How can a Model class(in a differnt thread) reference wxFrame?

Post by tuk1 »

doublemax wrote:Are you talking about user breakpoints in the debugger or crashes/asserts?

If the program stops at a breakpoint, the whole execution stops. No events can be sent, received or handled. Of course that changes the timing.
I'm talking about breakpoints in the debugger:
Capture.JPG
Capture.JPG (36.24 KiB) Viewed 2033 times
Yes, a 'localtime type logger' will not work properly with a debugger breakpoint, So what I do is run(from the breakpoint) the debugger and start the stopwatch at the same time, then when breakpoint re-engages I stop the stopwatch exactly at the same time... this method might be ( 0.5 secs -+ ) but its definitely not ( 6.0 secs -+ ) and then there are the random times the breakpoint re-engages after only 2-3 secs.

vs2015/wxTimer bug?
wxWidgets(v3.2.2.1) - Vs2022(v143) - Win10(x64) - DialogBlocks(v5.16.5_Unicode)
User avatar
doublemax
Moderator
Moderator
Posts: 19116
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: How can a Model class(in a differnt thread) reference wxFrame?

Post by doublemax »

So what I do is run(from the breakpoint) the debugger and start the stopwatch at the same time, then when breakpoint re-engages I stop the stopwatch exactly at the same time... this method might be ( 0.5 secs -+ ) but its definitely not ( 6.0 secs -+ )
Maybe the behavior is different than i expected. Maybe the timer is running normally in the background. Which means the time you spend in the debugger GUI counts, too. So if you spend 10-15 seconds in the GUI, the next timer event will happen only 5-10 seconds after you continued execution.
Use the source, Luke!
tuk1
Earned some good credits
Earned some good credits
Posts: 114
Joined: Sun Oct 08, 2017 9:36 am

Re: How can a Model class(in a differnt thread) reference wxFrame?

Post by tuk1 »

Thanks DM, very helpful!
wxWidgets(v3.2.2.1) - Vs2022(v143) - Win10(x64) - DialogBlocks(v5.16.5_Unicode)
User avatar
doublemax
Moderator
Moderator
Posts: 19116
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: How can a Model class(in a differnt thread) reference wxFrame?

Post by doublemax »

That was just a theory. Did you confirm it?
Use the source, Luke!
tuk1
Earned some good credits
Earned some good credits
Posts: 114
Joined: Sun Oct 08, 2017 9:36 am

Re: How can a Model class(in a differnt thread) reference wxFrame?

Post by tuk1 »

doublemax wrote:Maybe the behavior is different than i expected. Maybe the timer is running normally in the background. Which means the time you spend in the debugger GUI counts, too. So if you spend 10-15 seconds in the GUI, the next timer event will happen only 5-10 seconds after you continued execution.
You're right..
If I wait longer than 20 secs before running dbugr again...then it breaks straight away.
If run dbugr(without pause) again...then breakpoint re-engages after 20 secs as it should.
If I wait 10 secs to run dbugr again...then breakpoint re-engages after 10 secs...and so on.

So wxtimer must be running in a separate thread, dbugr breakpoint only pauses the main execution thread but not the wxtimer thread which is still running.

Unless there is a setting buried deep in vs for this behaviour then it means even vs gets confused by threads :D
wxWidgets(v3.2.2.1) - Vs2022(v143) - Win10(x64) - DialogBlocks(v5.16.5_Unicode)
User avatar
doublemax
Moderator
Moderator
Posts: 19116
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: How can a Model class(in a differnt thread) reference wxFrame?

Post by doublemax »

So wxtimer must be running in a separate thread, dbugr breakpoint only pauses the main execution thread but not the wxtimer thread which is still running.
There are no threads involved. wxTimer just uses a Windows timer internally which is run by the OS independent of the application. It just sends a message when the timer fires.
Use the source, Luke!
tuk1
Earned some good credits
Earned some good credits
Posts: 114
Joined: Sun Oct 08, 2017 9:36 am

Re: How can a Model class(in a differnt thread) reference wxFrame?

Post by tuk1 »

doublemax wrote:
So wxtimer must be running in a separate thread, db breakpoint only pauses the main execution thread not the wxtimer thread which is still running.
There are no threads involved. wxTimer just uses a Windows timer internally which is run by the OS independent of the application. It just sends a message when the timer fires.
I think we're largely saying the same thing, but to clarify, at os level wxtimer(or the sys timer it relies on) must be running on a different thread from vs db execution, ...otherwise wxtimer(or the sys timer it relies on) would pause on a vs db breakpoint.

Its interesting that wxTimer uses a system timer and not a wxthread type timer
wxWidgets(v3.2.2.1) - Vs2022(v143) - Win10(x64) - DialogBlocks(v5.16.5_Unicode)
Post Reply