"Main thread" vs. other threads 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.
User avatar
doublemax
Moderator
Moderator
Posts: 19114
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: "Main thread" vs. other threads

Post by doublemax »

I think I will need to trigger a maximum of 4 events in the main thread per second, so I should be ok?
Yes.
if I add delays to my threads, will they still occupy 100% of a CPU core each?
No. During wxThread::Sleep() the thread will not take any CPU time.
Should I limit the number of "infinite" threads to as few as possible?
Important is to take only as much CPU time as you need and not have an infinite loop that does nothing. Even calling wxThread::Sleep(0) is often enough as it will return the rest of the time slice of this thread to the OS (and therefore to other threads).
Use the source, Luke!
alys666
Super wx Problem Solver
Super wx Problem Solver
Posts: 329
Joined: Tue Oct 18, 2016 2:31 pm

Re: "Main thread" vs. other threads

Post by alys666 »

how you're reading data from your bus?
is there some blocking function to read data?
ubuntu 20.04, wxWidgets 3.2.1
Ksawery
Experienced Solver
Experienced Solver
Posts: 83
Joined: Thu Jul 25, 2019 12:31 pm

Re: "Main thread" vs. other threads

Post by Ksawery »

So i'm trying to implement the sending of custom events to my application windows from wxThreads. I'm using the following example for this purpose:

https://wiki.wxwidgets.org/Inter-Thread ... ain_thread

After implementing this, I keep seeing the following error:

Code: Select all

undefined reference to `vtable for cMain'
cMain is a wxFrame, which contains an event handler for the thread events. I'm not sure what's causing this error. Does it have something to do with casting a wxFrame pointer to a wxEvtHandler pointer when creating the threads (as in the example)?
how you're reading data from your bus?
is there some blocking function to read data?
One of my threads contains (what I believe is) a blocking function for incoming TCP/IP traffic. The two other threads run in a loop, calling various Modbus RTU functions to retrieve data from slave devices. There are no blocking functions there.

Regards,
Ksawery
User avatar
doublemax
Moderator
Moderator
Posts: 19114
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: "Main thread" vs. other threads

Post by doublemax »

undefined reference to `vtable for cMain'
Usually that means that you're defining a static event table (BEGIN_EVENT_TABLE/END_EVENT_TABLE), but forgot to add the DECLARE_EVENT_TABLE() to the class header.

If that's not it, show more code.
Use the source, Luke!
Ksawery
Experienced Solver
Experienced Solver
Posts: 83
Joined: Thu Jul 25, 2019 12:31 pm

Re: "Main thread" vs. other threads

Post by Ksawery »

Here is the event table in cMain:

Code: Select all

wxBEGIN_EVENT_TABLE(cMain, wxFrame)
    	EVT_COMMAND(wxID_ANY, wxEVT_RTU_UPDATE, cMain::OnModbusUpdate)
	EVT_BUTTON(10001, cMain::OnButtonClickedWsun)
	EVT_BUTTON(10002, cMain::OnButtonClickedWysun)
	EVT_BUTTON(10003, cMain::OnButtonClickedCalibrate)
	EVT_MENU(wxID_PREFERENCES, cMain::OnMenuClickedDiagnostics)
	EVT_MENU(wxID_INFO, cMain::OnMenuClickedInformation)
	EVT_MENU(wxID_EXIT, cMain::OnMenuClickedExit)
	EVT_TIMER(10004, cMain::OnWriteTimer)
	EVT_TIMER(10005, cMain::OnWriteErrorTimer)
	EVT_CLOSE(cMain::OnClose)
wxEND_EVENT_TABLE()
It is declared in the header file (cMain.h) using wxDECLARE_EVENT_TABLE(). It worked fine up until now, however now I added the following line to the table:

Code: Select all

EVT_COMMAND(wxID_ANY, wxEVT_RTU_UPDATE, cMain::OnModbusUpdate)
wxEVT_RTU_UPDATE is declared in another class header file (cModbusGateway.h), which is included in the cMain.h header file. The declaration is as follows (above the class declaration):

Code: Select all

DECLARE_EVENT_TYPE(wxEVT_RTU_UPDATE, -1)
The event is defined in the cModbusGateway.cpp file (above the class definition), as follows:

Code: Select all

DEFINE_EVENT_TYPE(wxEVT_RTU_UPDATE)
I guess something must be wrong with the declarations/definitions of the event? I tried to follow the example as best I could.

Regards,
Ksawery
alys666
Super wx Problem Solver
Super wx Problem Solver
Posts: 329
Joined: Tue Oct 18, 2016 2:31 pm

Re: "Main thread" vs. other threads

Post by alys666 »

undefined reference to `vtable for cMain'
usually it means that virtual method of a class declared, but not implemented.
if class B inherits from A, and method X is declared as overriden in B, then compiler calls X via vtable of B, not A. but method is not really implemented, and there is no other virtual or overriden methods in B - so compiler cannot generate vtable of B.
ubuntu 20.04, wxWidgets 3.2.1
alys666
Super wx Problem Solver
Super wx Problem Solver
Posts: 329
Joined: Tue Oct 18, 2016 2:31 pm

Re: "Main thread" vs. other threads

Post by alys666 »

in simple words cMain has declared virtual method, but not implemented it. and there are no other implemented virtual methods in cMain.
ubuntu 20.04, wxWidgets 3.2.1
alys666
Super wx Problem Solver
Super wx Problem Solver
Posts: 329
Joined: Tue Oct 18, 2016 2:31 pm

Re: "Main thread" vs. other threads

Post by alys666 »

Ksawery wrote: Thu Oct 17, 2019 11:11 am One of my threads contains (what I believe is) a blocking function for incoming TCP/IP traffic. The two other threads run in a loop, calling various Modbus RTU functions to retrieve data from slave devices. There are no blocking functions there.
to make clean multithreading design you need blocking functions, and better blocking with timeout.
do this Modbus library has any to read the data?
or you are just polling modbus devices kinda to control their state or something?
which lib are you using for modbus?
ubuntu 20.04, wxWidgets 3.2.1
Ksawery
Experienced Solver
Experienced Solver
Posts: 83
Joined: Thu Jul 25, 2019 12:31 pm

Re: "Main thread" vs. other threads

Post by Ksawery »

in simple words cMain has declared virtual method, but not implemented it. and there are no other implemented virtual methods in cMain.
Thank you, this was it! I declared a virtual method that was meant to override a method in the wxThreadHelper class, but I removed all helper thread code from the app. Well spotted, I wish the compiler/linker error was a bit more clear.

Ksawery
Ksawery
Experienced Solver
Experienced Solver
Posts: 83
Joined: Thu Jul 25, 2019 12:31 pm

Re: "Main thread" vs. other threads

Post by Ksawery »

to make clean multithreading design you need blocking functions, and better blocking with timeout.
do this Modbus library has any to read the data?
or you are just polling modbus devices kinda to control their state or something?
which lib are you using for modbus?
I'm using the latest version of the libmodbus library, compiled from source:

https://github.com/stephane/libmodbus
https://libmodbus.org/docs/v3.1.4/

The TCP/IP Server and RTU Slave functions are blocking, since they wait for incoming messages. The TCP/IP Client and RTU Master functions are non-blocking, they send messages when required to - I hope my understanding of "blocking" and "non-blocking" functions is correct in this case.

Regards,
Ksawery
alys666
Super wx Problem Solver
Super wx Problem Solver
Posts: 329
Joined: Tue Oct 18, 2016 2:31 pm

Re: "Main thread" vs. other threads

Post by alys666 »

Well spotted, I wish the compiler/linker error was a bit more clear.
it's because C++ is powerful, but weird.
compiler cannot detect this error, because he do not know where you implemented a method of a class(you can implement different class methods in different files), and only linker cannot find vtable of this class. but it also could be, if you forgot to add needed object file to linkage list.
ubuntu 20.04, wxWidgets 3.2.1
alys666
Super wx Problem Solver
Super wx Problem Solver
Posts: 329
Joined: Tue Oct 18, 2016 2:31 pm

Re: "Main thread" vs. other threads

Post by alys666 »

Ksawery wrote: Thu Oct 17, 2019 12:10 pm The TCP/IP Server and RTU Slave functions are blocking, since they wait for incoming messages. The TCP/IP Client and RTU Master functions are non-blocking, they send messages when required to - I hope my understanding of "blocking" and "non-blocking" functions is correct in this case.
Regards,
Ksawery
blocking means that thread will be resumed(not active, and not occupy processor) if there is no data or event you're waiting for.
and this behaviour is the best for multithreading.
now you must decide how often you want to issue non blocking calls to modbus.
ubuntu 20.04, wxWidgets 3.2.1
Ksawery
Experienced Solver
Experienced Solver
Posts: 83
Joined: Thu Jul 25, 2019 12:31 pm

Re: "Main thread" vs. other threads

Post by Ksawery »

The application now successfully compiles, but unfortunately crashes after 20 seconds or so, for some unknown reason. I seem to be getting different errors reported in the console each time. For example:

Code: Select all

corrupted double-linked list
or

Code: Select all

(PomiarWiazki_Gateway:18974): GLib-GObject-[1;35mCRITICAL[0m **: [34m15:23:13.628[0m: g_closure_ref: assertion 'closure->ref_count > 0' failed
Here is the debugger console:

Code: Select all

New UI allocated
GNU gdb (Ubuntu 8.1-0ubuntu3) 8.1.0.20180409-git
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word".
(gdb) [Thread debugging using libthread_db enabled]                                                                                                                                                                                          
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Temporary breakpoint 1, main (argc=1, argv=0x7fffffffdec8) at ../cPomiarWiazki.cpp:3
3       wxIMPLEMENT_APP(cPomiarWiazki);
[New Thread 0x7fffeb904700 (LWP 18778)]
[New Thread 0x7fffeb103700 (LWP 18779)]
[New Thread 0x7fffea3f1700 (LWP 18780)]
[New Thread 0x7fffe9bf0700 (LWP 18781)]
[New Thread 0x7fffe93ef700 (LWP 18782)]
[New Thread 0x7fffe8a70700 (LWP 18783)]

Thread 1 "PomiarWiazki_Ga" received signal SIGABRT, Aborted.
__GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51
51      ../sysdeps/unix/sysv/linux/raise.c: No such file or directory.
#9  0x00007ffff4530d45 in g_type_register_static_simple () from /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0
#10 0x00007ffff47a8d64 in g_file_info_get_type () from /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0
#20 0x00007ffff4514778 in g_object_new_valist () from /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0
#40 0x00007ffff6d2039b in wxEntry (argc=@0x7ffff7095090: 1, argv=0x55555590b450) at ../src/common/init.cpp:507
507             return wxTheApp->OnRun();
#0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51
51      ../sysdeps/unix/sysv/linux/raise.c: No such file or directory.
Here is the debugger output:

Code: Select all

PomiarWiazki_Gateway Debug [C/C++ Application]	
	PomiarWiazki_Gateway [18766] [cores: 0,1,3]	
		Thread #1 [PomiarWiazki_Ga] 18766 [core: 3] (Suspended : Signal : SIGABRT:Aborted)	
			__GI_raise() at raise.c:51 0x7ffff60c7e97	
			__GI_abort() at abort.c:79 0x7ffff60c9801	
			__libc_message() at libc_fatal.c:181 0x7ffff6112897	
			malloc_printerr() at malloc.c:5,350 0x7ffff611990a	
			_int_malloc() at malloc.c:3,926 0x7ffff611e0a9	
			__libc_calloc() at malloc.c:3,436 0x7ffff61230b1	
			g_malloc0() at 0x7ffff4237b11	
			0x7ffff452b31f	
			g_type_register_static() at 0x7ffff4530a80	
			g_type_register_static_simple() at 0x7ffff4530d45	
			g_file_info_get_type() at 0x7ffff47a8d64	
			0x7ffff47edbde	
			g_type_class_ref() at 0x7ffff452e419	
			g_object_new_valist() at 0x7ffff4514778	
			g_object_new() at 0x7ffff4514939	
			g_resource_lookup_data() at 0x7ffff47c9416	
			g_resources_lookup_data() at 0x7ffff47c998e	
			gtk_widget_class_set_template_from_resource() at 0x7ffff5b0b1cd	
			0x7ffff5abab45	
			g_type_class_ref() at 0x7ffff452e419	
			g_object_new_valist() at 0x7ffff4514778	
			g_object_new() at 0x7ffff4514939	
			0x7ffff5ab88b8	
			g_type_create_instance() at 0x7ffff45319c5	
			0x7ffff4512748	
			g_object_new_with_properties() at 0x7ffff4513ee5	
			g_object_new() at 0x7ffff4514961	
			0x7ffff5ab9a31	
			gtk_main_do_event() at 0x7ffff59b1825	
			0x7ffff54c2765	
			0x7ffff54f2f92	
			g_main_context_dispatch() at 0x7ffff4232417	
			0x7ffff4232650	
			g_main_loop_run() at 0x7ffff4232962	
			gtk_main() at 0x7ffff59b0a25	
			wxGUIEventLoop::DoRun() at evtloop.cpp:64 0x7ffff73c07dd	
			wxEventLoopBase::Run() at evtloopcmn.cpp:90 0x7ffff6ce8c29	
			wxAppConsoleBase::MainLoop() at appbase.cpp:380 0x7ffff6cad080	
			wxAppConsoleBase::OnRun() at appbase.cpp:301 0x7ffff6cacda7	
			wxAppBase::OnRun() at appcmn.cpp:335 0x7ffff749e8b1	
			wxEntry() at init.cpp:507 0x7ffff6d2039b	
			wxEntry() at init.cpp:519 0x7ffff6d20471	
			main() at cPomiarWiazki.cpp:3 0x555555588b94	
		Thread #2 [gmain] 18778 [core: 0] (Suspended : Container)	
		Thread #3 [gdbus] 18779 [core: 3] (Suspended : Container)	
		Thread #4 [PomiarWiazki_Ga] 18780 [core: 1] (Suspended : Container)	
		Thread #5 [PomiarWiazki_Ga] 18781 [core: 0] (Suspended : Container)	
		Thread #6 [PomiarWiazki_Ga] 18782 [core: 1] (Suspended : Container)	
		Thread #7 [pool] 18783 [core: 3] (Suspended : Container)	
	gdb (8.1.0.20180409)	
The crash seems to occur after the following event:

Code: Select all

Thread 1 "PomiarWiazki_Ga" received signal SIGABRT, Aborted.
Im not sure why the last thread aborts. The problem also disappears if I remove calls to the main thread custom event, from the Modbus threads.

Any help would be appreciated. Meanwhile I'll try to figure this out.

Regards,
Ksawery
Ksawery
Experienced Solver
Experienced Solver
Posts: 83
Joined: Thu Jul 25, 2019 12:31 pm

Re: "Main thread" vs. other threads

Post by Ksawery »

Sorry, turns out it was a simple error in not resetting a counter, and trying to access an invalid memory location. I will continue to test the application, hopefully it will work well now :)

Regards,
Ksawery
Ksawery
Experienced Solver
Experienced Solver
Posts: 83
Joined: Thu Jul 25, 2019 12:31 pm

Re: "Main thread" vs. other threads

Post by Ksawery »

I have a new issue, where my App crashes on triggering one of the events.

On each itteration of my worker thread, I send events to one of the "main" windows, as well as an additional "diagnostics" window (which is tied to the same main window), under the condition that it is opened by the user:

Code: Select all

        //Send event to main window
        wxCommandEvent evt(wxEVT_RTU_UPDATE, wxID_ANY);
        pMainEvtHandler->AddPendingEvent(evt);

        //Send event to diagnostics window
        if (mbGateway->pMain[nRTU]->mbDiagnosticsWindowIsOpen)
        {
            wxCommandEvent evt(wxEVT_DIAG_UPDATE, wxID_ANY);
            pDiagEvtHandler->AddPendingEvent(evt);
        }
Sending events to the main window works well, however as soon as the diagnostics window is opened, the app crashes when trying to send an event to it. The events are declared and defined in the same way in both windows. I tried using QueueEvent instead of AddPendingEvent, according to the recommendation found in AddPendingEvent, however it didn't help:

Code: Select all

        //Send event to main window
        wxCommandEvent* evt = new wxCommandEvent(wxEVT_RTU_UPDATE, wxID_ANY);
        pMainEvtHandler->QueueEvent(evt);

        //Send event to diagnostics window
        if (mbGateway->pMain[nRTU]->mbDiagnosticsWindowIsOpen)
        {
            wxCommandEvent* evt = new wxCommandEvent(wxEVT_DIAG_UPDATE, wxID_ANY);
            pDiagEvtHandler->QueueEvent(evt);
        }
I'm not sure whether the problem might be with my App, or with the way I trigger the events.

Here is how I declare the events in cModbusGateway.h:

Code: Select all

DECLARE_EVENT_TYPE(wxEVT_RTU_UPDATE, -1)
DECLARE_EVENT_TYPE(wxEVT_DIAG_UPDATE, -1)
I define them in cModbusGateway.cpp:

Code: Select all

DEFINE_EVENT_TYPE(wxEVT_RTU_UPDATE)
DEFINE_EVENT_TYPE(wxEVT_DIAG_UPDATE)
Here is the event table in cMain.cpp:

Code: Select all

wxBEGIN_EVENT_TABLE(cMain, wxFrame)
    EVT_COMMAND(wxID_ANY, wxEVT_RTU_UPDATE, cMain::OnModbusUpdate)
    EVT_BUTTON(10001, cMain::OnButtonClickedWsun)
    EVT_BUTTON(10002, cMain::OnButtonClickedWysun)
    EVT_BUTTON(10003, cMain::OnButtonClickedCalibrate)
    EVT_MENU(wxID_PREFERENCES, cMain::OnMenuClickedDiagnostics)
    EVT_MENU(wxID_INFO, cMain::OnMenuClickedInformation)
    EVT_MENU(wxID_EXIT, cMain::OnMenuClickedExit)
    EVT_TIMER(10004, cMain::OnWriteTimer)
    EVT_TIMER(10005, cMain::OnWriteErrorTimer)
    EVT_CLOSE(cMain::OnClose)
wxEND_EVENT_TABLE()
Here is the event table in cDiagnostics.cpp:

Code: Select all

wxBEGIN_EVENT_TABLE(cDiagnostics, wxFrame)
    EVT_COMMAND(wxID_ANY, wxEVT_DIAG_UPDATE, cDiagnostics::OnDiagnosticsUpdate)
    EVT_TIMER(10006, cDiagnostics::OnDiagnosticsResetTimer)
    EVT_BUTTON(10007, cDiagnostics::OnButtonClickedReset)
    EVT_CLOSE(cDiagnostics::OnClose)
wxEND_EVENT_TABLE()
Regards,
Ksawery
Post Reply