start a new `wxThread` only after previous thread returns - c++

CASE 1:
I am using wxThreads, I am creating threads using 2 for loops. I have a MyThreads class which is inherited from wxThread class. Also, every thread creates a wxThreadEvent before exit and sends data to main program. Main program executes DoThisWorkAfterThreadReturns() after every thread is finished. what I want to do is, all threads with level=0 can execute simultaneously. But before creating threads with level = 1, all level 0 threads should have finished their execution and DoThisWorkAfterThreadReturns() execution for all level 0 threads should also be finished. How should I do this using wxWidgets ?
for(level=0;level<n;level++)
{
for(int i=0;i<no;i++)
{
//threads in this loop can execute simultaneously.
MyThread *thread = new MyThread(this);
thread->create();
thread->run();
}
//wait till all threads for given level finish execution and execute
DoThisWorkAfterThreadReturns()
}
CASE 2:
If CASE 1 is not possible then can I do following ?
for(i=0;i<n;i++)
{
MyThread *thread = new MyThread(this);
thread->create();
thread->run();
// wait till this thread finishes its execution, returns data to main program and main program finishes execution of DoThisWorkAfterThreadReturns()
// after this only execute i++(i.e. next thread)
}
can I wait for every thread to finish before creating a new thread from for loop ? It is necessary to create threads as I am sending backend requests which takes a long time sometimes.

I don't think this can be done simply. Here's one example of a way to do it. I'm using a simple application with a text control and a button. To accomplish what you have described above, I've split the work between 3 functions.
The first function starts the work of the outer loop. In this example, it's an event handler for the button.
The second function spawns a number of threads and basically corresponds to the inner loop described above. The threads in this example are dumb. They simply wait for a random amount of time between 0 and 5 seconds, throw an event to announce they are done, and then delete themselves.
The third function is a thread event handler called when each thread is finished. It basically finishes the work of the outer loop. It checks the number of threads running, and if that is zero it will either start a new iteration of the inner loop or finish.
// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
// for all others, include the necessary headers (this file is usually all you
// need because it includes almost all "standard" wxWidgets headers)
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
#include <wx/thread.h>
#include <stdlib.h> /* srand, rand */
#include <time.h> /* time */
class MyThread : public wxThread
{
public:
MyThread(wxEvtHandler *handler,int sleeptime)
: wxThread(wxTHREAD_DETACHED)
{ m_pHandler = handler;m_sleepTime= sleeptime;}
protected:
virtual ExitCode Entry();
wxEvtHandler *m_pHandler;
int m_sleepTime;
};
wxThread::ExitCode MyThread::Entry()
{
// A real application would do something here,
// but for this example the only thing done is sleeping
Sleep(m_sleepTime);
// The work is done. Throw a thread event to announce this to the frame.
wxThreadEvent* exitEvent = new wxThreadEvent();
exitEvent->SetInt(m_sleepTime);
wxQueueEvent(m_pHandler, exitEvent);
return (wxThread::ExitCode)0;
}
class MyFrame : public wxFrame
{
public:
MyFrame( wxWindow* parent, int id = wxID_ANY, wxString title = "Demo",
wxPoint pos = wxDefaultPosition, wxSize size = wxSize(481,466),
int style = wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL );
private:
void OnButton(wxCommandEvent& event);
void OnThreadComplete(wxThreadEvent& event);
void SpawnThreads();
int m_outerLoopCounter;
int m_outerLoopLimit;
int m_threadsToUse;
wxCriticalSection m_threadsRunningCS;
int m_threadsRunning;
wxTextCtrl* m_textCtrl;
wxButton* m_button;
};
MyFrame::MyFrame( wxWindow* parent, int id, wxString title, wxPoint pos
, wxSize size, int style )
:wxFrame( parent, id, title, pos, size, style )
{
wxPanel* panel = new wxPanel(this);
wxBoxSizer* szr = new wxBoxSizer( wxVERTICAL );
m_textCtrl = new wxTextCtrl( panel, wxID_ANY, wxEmptyString,
wxDefaultPosition, wxDefaultSize,
wxTE_DONTWRAP|wxTE_MULTILINE );
szr->Add( m_textCtrl, 1, wxALL|wxEXPAND, 5 );
m_button = new wxButton( panel, wxID_ANY, "Spawn");
szr->Add( m_button, 0, wxALL, 5 );
panel->SetSizer( szr );
Layout();
srand(time(NULL));
m_outerLoopLimit = 3;
m_threadsToUse = 4;
Bind( wxEVT_THREAD, &MyFrame::OnThreadComplete, this);
m_button->Bind( wxEVT_BUTTON, &MyFrame::OnButton, this );
}
void MyFrame::OnButton(wxCommandEvent& event)
{
m_button->Disable();
m_outerLoopCounter=0;
SpawnThreads();
}
void MyFrame::SpawnThreads()
{
(*m_textCtrl) << "spawning threads for loop " << m_outerLoopCounter+1;
(*m_textCtrl) << " of " << m_outerLoopLimit <<"\n";
m_threadsRunning=0;
for ( int i=0; i<m_threadsToUse; ++i )
{
int sleeptime = rand()%5000;
(*m_textCtrl) << "\tthread " << i << " will sleep for ";
(*m_textCtrl) << sleeptime << " ms.\n";
MyThread* thread = new MyThread(this,sleeptime);
wxCriticalSectionLocker enter(m_threadsRunningCS);
++m_threadsRunning;
if ( thread->Run() != wxTHREAD_NO_ERROR )
{
wxLogError("Can't create the thread!");
delete thread;
--m_threadsRunning;
}
}
}
void MyFrame::OnThreadComplete(wxThreadEvent& event)
{
(*m_textCtrl) << "\tThe thread that slept for ";
(*m_textCtrl) << event.GetInt() << " ms has finished.\n";
// Check the number of threads that are still running
bool canStop = false;
{
wxCriticalSectionLocker enter(m_threadsRunningCS);
--m_threadsRunning;
if ( m_threadsRunning == 0 )
{
canStop=true;
}
}
// If there are zero threads still running, either enter a new iteration
// of the outer loop or stop if the outer loop is complete.
if(canStop)
{
++m_outerLoopCounter;
if ( m_outerLoopCounter<m_outerLoopLimit )
{
SpawnThreads();
}
else
{
(*m_textCtrl) << "All Done.\n";
m_button->Enable();
}
}
}
class MyApp : public wxApp
{
public:
virtual bool OnInit()
{
MyFrame* frame = new MyFrame(NULL);
frame->Show();
return true;
}
};
wxIMPLEMENT_APP(MyApp);
There are some downsides here. The outer loop counter and the number of threads running need to be tracked with variables available in multiple functions. So they need to be either global variables or members of the frame or app class. Also, I think the variable for the number of running threads should be guarded with a critical section. (I could be wrong, but in the example above, I decided to be safe and used the critical section.)
There may be a simpler way to do this. It's just the first thing I've tried.

wxSemaphore is a counter. wxSemaphore::Wait() waits if the internal counter is zero, otherwise it decrements the counter and returns.
You need the opposite to wxSemaphore, something that waits until the counter is zero.
Set a var (let's call it tVar) that increments when the thread starts and decrements when the thread ends.
class MyThread : public wxThread
{
....
MyThread(someObject *obj, wxEvtHandler *evtH) //someObject is where tVar lives, evtH is where we will post an event
{ m_obj = obj; m_evtH = evtH;}
....
someObject *m_obj;
wxEvtHandler *m_evtH;
};
wxThread::ExitCode MyThread::Entry()
{
//Increment the var
wxCriticalSection locker(m_obj->someCritSec) //someCritSec must be accessible in someObject
locker.Enter();
m_obj->IncrementVar();
locker.Leave() //Allow other threads to increment the var while this thread is still working
//do the task
.....
//and decrement through a message
wxQueueEvent(m_evtH, new wxThreadEvent(wxEVT_COMMAND_MYTHREAD_COMPLETED));
return 0;
}
Class someObject is derived from wxEvtHandler (for example, a wxWindow) so it can receive the message. By an event-table or better by Bind() you have a handler for the thread completion event:
void someObject::OnThreadCompleted(wxThreadEvent&)
{
//decrement the var
--tVar;
//Do something when it reaches 0
if ( tVar == 0 )
DoThisWorkAfterThreadReturns();
}
This solution allows the GUI to be responsive while threads are working. Really no body is waiting. It's that DoThisWorkAfterThreadReturns is only executed when all threads have finished. The logic on which "levels" must "wait" or not is your decision.
There is a small caveat in this solution: if the first created thread finishes before another thread begins running the message will be posted and there's a chance that its handler is called before another thread increments the var.
You can avoid it by having tVar = 1 before any thread is created, and decrementing it (post an event) right after the last thread is created.

Related

Passing a function to wxWidgets thread-pool with inter-thread communication

I am a hobby programmer learning C++ and multi-threading, and getting started on my first thread-pool attempt.
I use Code::Blocks 20.3, wxWidgets 3.1.4, and MinGW 17.1 on a Windows 10 Pro computer.
I have tried several thread-pool examples, but all blocked the GUI.
I found an example shown in https://wiki.wxwidgets.org/Inter-Thread_and_Inter-Process_communication
that uses detached threads in a pool. This should not block the GUI.
I have "restructured" the 1 file example to work in a test project (gui, app, main, thread-pool modules).
I placed the classes in their own file, and moved the "main" part to the Main.cpp in my test project and replaced the gui code with a separate class file.
The standard example works as expected.
In the example, strings are passed to the thread-pool and other strings back to the main thread.
I have been searching for main thread AddToQueue() to pass any function like e.g. aTask() (void, or returning something to the main thread) that executes in the thread-pool. My search was not successful :-(.
=== Simple Task to be executed in a thread ===
std::vector<wxString> wxThreadCom2Frame::aTask(wxString wsSomeString, int x)
{
std::vector<wxString> vTest{};
for(int i = 0; i < x; i++)
{
wxString wsTest{};
wsTest << wsSomeString << " [" << i << "]";
vTest.push_back(wsTest);
}
return vTest;
}
=== Or as alternative, pass the vector by reference
aTask(_T("Just some text"), 5, &vTest); // to be queued with AddJob
===
void wxThreadCom2Frame::aTask(wxString wsSomeString, int x, std::vector<wxString> *vTest)
{
for(int i = 0; i < x; i++)
{
wxString wsTest{};
wsTest << wsSomeString << " [" << i << "]\n";
vTest->push_back(wsTest);
}
}
===
I hope someone can help me understand how to do this.
This is the first step to what I actually like to achieve.
An 'extraction' function returns a structure of 20 tags from a music file (mp3, flac, etc).
The main 'collecting' function will call the 'extraction' function for each file (up to 7000) in a list and place it in the queue of the thread-pool.
The 'collecting' function returns a vector of structures to the main thread for further processing.
Regards, Ruud.
=== ThreadCom.cpp ===
/////////////////////////////////////////////////////////////////////////////
// https://wiki.wxwidgets.org/Inter-Thread_and_Inter-Process_communication //
/////////////////////////////////////////////////////////////////////////////
// Standard
#include <stdlib.h>
#include <assert.h>
#include <map>
#include <list>
// wxWidgets
#include <wx/frame.h>
#include <wx/thread.h>
#include <wx/menu.h>
#include <wx/app.h>
class tJOB
{
public:
enum tCOMMANDS // list of commands that are currently implemented
{
eID_THREAD_EXIT=wxID_EXIT, // thread should exit or wants to exit
eID_THREAD_NULL=wxID_HIGHEST+1, // dummy command
eID_THREAD_STARTED, // worker thread has started OK
eID_THREAD_JOB = ID_THREAD_JOB, // process normal job
eID_THREAD_JOBERR = ID_THREAD_JOBERR // process erroneous job after which thread likes to exit
}; // enum tCOMMANDS
tJOB() : m_cmd(eID_THREAD_NULL) {}
tJOB(tCOMMANDS cmd, const wxString& arg) : m_cmd(cmd), m_Arg(arg) {}
tCOMMANDS m_cmd; wxString m_Arg;
}; // class tJOB
class QUEUE
{
public:
enum tPRIORITY { eHIGHEST, eHIGHER, eNORMAL, eBELOW_NORMAL, eLOW, eIDLE }; // priority classes
QUEUE(wxEvtHandler* pParent) : m_pParent(pParent) {}
void AddJob(const tJOB& job, const tPRIORITY& priority=eNORMAL) // push a job with given priority class onto the FIFO
{
wxMutexLocker lock(m_MutexQueue); // lock the queue
m_Jobs.insert(std::make_pair(priority, job)); // insert the prioritized entry into the multimap
m_QueueCount.Post(); // new job has arrived: increment semaphore counter
} // void AddJob(const tJOB& job, const tPRIORITY& priority=eNORMAL)
tJOB Pop()
{
tJOB element;
m_QueueCount.Wait(); // wait for semaphore (=queue count to become positive)
m_MutexQueue.Lock(); // lock queue
element=(m_Jobs.begin())->second; // get the first entry from queue (higher priority classes come first)
m_Jobs.erase(m_Jobs.begin()); // erase it
m_MutexQueue.Unlock(); // unlock queue
return element; // return job entry
} // tJOB Pop()
void Report(const tJOB::tCOMMANDS& cmd, const wxString& sArg=wxEmptyString, int iArg=0) // report back to parent
{
wxCommandEvent evt(wxEVT_THREAD, cmd); // create command event object
evt.SetString(sArg); // associate string with it
evt.SetInt(iArg);
m_pParent->AddPendingEvent(evt); // and add it to parent's event queue
} // void Report(const tJOB::tCOMMANDS& cmd, const wxString& arg=wxEmptyString)
size_t Stacksize() // helper function to return no of pending jobs
{
wxMutexLocker lock(m_MutexQueue); // lock queue until the size has been read
return m_Jobs.size();
}
private:
wxEvtHandler* m_pParent;
std::multimap<tPRIORITY, tJOB> m_Jobs; // multimap to reflect prioritization: values with lower keys come first, newer values with same key are appended
wxMutex m_MutexQueue; // protects queue access
wxSemaphore m_QueueCount; // semaphore count reflects number of queued jobs
};
class WorkerThread : public wxThread
{
public:
WorkerThread(QUEUE* pQueue, int id=0) : m_pQueue(pQueue), m_ID(id) { assert(pQueue); wxThread::Create(); }
private:
QUEUE* m_pQueue;
int m_ID;
virtual wxThread::ExitCode Entry()
{
Sleep(1000); // sleep a while to simulate some time-consuming init procedure
tJOB::tCOMMANDS iErr;
m_pQueue->Report(tJOB::eID_THREAD_STARTED, wxEmptyString, m_ID); // tell main thread that worker thread has successfully started
try { while(true) OnJob(); } // this is the main loop: process jobs until a job handler throws
catch(tJOB::tCOMMANDS& i) { m_pQueue->Report(iErr=i, wxEmptyString, m_ID); } // catch return value from error condition
return (wxThread::ExitCode)iErr; // and return exit code
} // virtual wxThread::ExitCode Entry()
virtual void OnJob()
{
tJOB job=m_pQueue->Pop(); // pop a job from the queue. this will block the worker thread if queue is empty
switch(job.m_cmd)
{
case tJOB::eID_THREAD_EXIT: // thread should exit
Sleep(1000); // wait a while
throw tJOB::eID_THREAD_EXIT; // confirm exit command
case tJOB::eID_THREAD_JOB: // process a standard job
Sleep(2000);
m_pQueue->Report(tJOB::eID_THREAD_JOB, wxString::Format(wxT("Job #%s done."), job.m_Arg.c_str()), m_ID); // report successful completion
break;
case tJOB::eID_THREAD_JOBERR: // process a job that terminates with an error
m_pQueue->Report(tJOB::eID_THREAD_JOB, wxString::Format(wxT("Job #%s erroneous."), job.m_Arg.c_str()), m_ID);
Sleep(1000);
throw tJOB::eID_THREAD_EXIT; // report exit of worker thread
break;
case tJOB::eID_THREAD_NULL: // dummy command
default:
break; // default
} // switch(job.m_cmd)
} // virtual void OnJob()
}; // class WorkerThread : public wxThread
=== partial wxThreadCom2Main.cpp ===
void wxThreadCom2Frame::AddToQueue( wxCommandEvent& event )
{
int iJob=rand();
m_pQueue->AddJob(tJOB((tJOB::tCOMMANDS)event.GetId(), wxString::Format(wxT("%u"), iJob)));
SetStatusText(wxString::Format(wxT("Job #%i started."), iJob)); // just set the status text
}
void wxThreadCom2Frame::OnThread(wxCommandEvent& event) // handler for thread notifications
{
switch(event.GetId())
{
case tJOB::eID_THREAD_JOB:
// Get the returned vector and do something with it
SetStatusText(wxString::Format(wxT("[%i]: %s"), event.GetInt(), event.GetString().c_str())); // progress display
break;
case tJOB::eID_THREAD_EXIT:
SetStatusText(wxString::Format(wxT("[%i]: Stopped."), event.GetInt()));
m_Threads.remove(event.GetInt()); // thread has exited: remove thread ID from list
if(m_Threads.empty()) { EnableControls(false); } // disable some menu items if no more threads
break;
case tJOB::eID_THREAD_STARTED:
SetStatusText(wxString::Format(wxT("[%i]: Ready."), event.GetInt()));
EnableControls(true); // at least one thread successfully started: enable controls
break;
default:
event.Skip();
}
}
void wxThreadCom2Frame::EnableControls(bool bEnable) // en/dis-able Stop, Add Job, Add JobErr
{
wxMenu* pMenu=GetMenuBar()->GetMenu(0);
static const int MENUIDS[]={/*ID_START_THREAD, */ID_THREAD_EXIT, ID_THREAD_JOB, ID_THREAD_JOBERR};
for(unsigned int i=0; i<WXSIZEOF(MENUIDS); pMenu->Enable(MENUIDS[i++], bEnable));
}
===

The simplest possible example for a multi-threaded gtkmm application

I'm developing a stopwatch application in c++ and gtkmm.The first approach was to integrate the stopwatch loop with main event loop,but that caused delays in the stopwatch loop,So I decided to use two threads , one for GUI the other is for the stopwatch counter.
the GUI thread and and stopwatch thread -which counts the number of seconds passed- , the stopwatch thread updates the label after each second. I've read that I've to use specific techniques to use multi threaded gtkmm program. I also checked this example which i didn't understand. Can any one tell me what shall I do in the stopwatch thread to make it update the GUI without crashing.
Here's the code,
#include <ctime>
#include <thread>
#include <iostream>
#include <sstream>
#include <gtkmm.h>
using namespace std;
using namespace Gtk;
using namespace std::chrono;
class Timer
{
bool condition;
Label *label_ptr;
private:
void startTimer()
{
condition = true;
auto t0 = high_resolution_clock::now();
int x = 0;
while (condition)
{
cout << condition << endl;
auto t1 = high_resolution_clock::now();
int duration = duration_cast<seconds>(t1 - t0).count();
if (duration - x == 1)
{
x = duration;
//cout << duration_cast<seconds>(t1 - t0).count() << "sec" << endl;
stringstream moment;
moment << to_string(duration_cast<seconds>(t1 - t0).count());
moment << ": 0 s";
label_ptr->set_label(moment.str());
}
if (g_main_context_pending(NULL))
{
g_main_context_iteration(NULL, true);
}
}
}
public:
//This is the function which start the thread
void start_timer(){
thread t0(sigc::mem_fun(this , &Timer::startTimer));
}
Timer(Label &label)
{
condition = 0;
label_ptr = &label;
}
void stop_timer()
{
label_ptr->set_text("0 : 0");
condition = false;
}
};
int main(int argc, char *argv[])
{
auto app = Application::create(argc, argv, "Timer");
if (!g_thread_supported())
g_thread_init(NULL);
gdk_threads_init();
gdk_threads_enter();
Window window;
HBox mainBox;
window.add(mainBox);
window.set_title("Timer & Stopwatch");
window.set_default_size(240, 100);
window.set_border_width(10);
Label label;
VButtonBox buttons;
Separator sep0;
mainBox.pack_start(buttons, PACK_EXPAND_PADDING, 20);
mainBox.pack_start(sep0, PACK_SHRINK);
mainBox.pack_end(label, PACK_EXPAND_PADDING);
label.set_size_request(120, 100);
Button start, stop;
buttons.add(start);
buttons.add(stop);
start.set_border_width(5);
stop.set_border_width(5);
start.set_label("Start");
stop.set_label("Stop");
label.set_text("0 : 0");
Timer timer(label);
start.signal_clicked().connect(sigc::mem_fun(timer, &Timer::start_timer));
window.show_all_children();
gdk_threads_leave();
return app->run(window);
}
if your requirement is just to count number of seconds elapsed, you do not need a worker thread. Also you cannot modify any GUI element (in your case the 'label') from a worker thread since Gdk is not thread safe. You will then need to use Glib::Dispatcher to notify the main thread of a 'tick' event and then increment a counter from the main loop.
Instead of all this you can simply use Glib::SignalTimeout and connect a function to it like below.
In the function that handles starting of the stop-watch
//Connect to timeout
m_connectionStopWatch = Glib::SignalTimeout::connect(sigc::mem_fun(*this, &CStopWatchClass::OneSecondElapsed), 1000);
//Reset counter
m_nCounter = 0;
The function that is called every second
bool CStopWatchClass::OneSecondElapsed(void)
{
//Increment counter
m_nCounter++;
//TODO Update label
return true;
}
In the function that stops the stop watch
m_connectionStopWatch.disconnect();
m_connectionStopWatch and m_nCounter shall be class private (or protected or even public though not preferred) members.
If you are not particular about having a delay of up to 0.99... seconds in starting your stop watch you can even use Glib::SignalTimeout::connect_seconds which loads your application even less. In any case a 1 second timeout is no problem at all, I myself am using a 20 millisecond timeout for a similar graphical application.

Modern C++. Return data structure from working thread continuing its execution

I need to launch working thread, perform some initialization, return data structure as initialization result and continue thread execution. What is the best (or possible) code to achieve this using modern c++ features only? Note, launched thread should continue its execution (thread does not terminated as usual). Unfortunately, most solutions assume worker thread termination.
Pseudo code:
// Executes in WorkerThread context
void SomeClass::Worker_treadfun_with_init()
{
// 1. Initialization calls...
// 2. Pass/signal initialization results to caller
// 3. Continue execution of WorkerThread
}
// Executes in CallerThread context
void SomeClass::Caller()
{
// 1. Create WorkerThread with SomeClass::Worker_treadfun_with_init()" thread function
// 2. Sleep thread for some initialization results
// 3. Grab results
// 3. Continue execution of CallerThread
}
I think std::future meets your requirements.
// Executes in WorkerThread context
void SomeClass::Worker_treadfun_with_init(std::promise<Result> &pro)
{
// 1. Initialization calls...
// 2. Pass/signal initialization results to caller
pro.set_value(yourInitResult);
// 3. Continue execution of WorkerThread
}
// Executes in CallerThread context
void SomeClass::Caller()
{
// 1. Create WorkerThread with SomeClass::Worker_treadfun_with_init()" thread function
std::promise<Result> pro;
auto f=pro.get_future();
std::thread([this,&pro](){Worker_treadfun_with_init(pro);}).detach();
auto result=f.get();
// 3. Grab results
// 3. Continue execution of CallerThread
}
Try using a pointer or reference to the data structure with the answer in it, and std::condition_variable to let you know when the answer has been computed:
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <chrono>
#include <vector>
std::vector<double> g_my_answer;
std::mutex g_mtx;
std::condition_variable g_cv;
bool g_ready = false;
void Worker_treadfun_with_init()
{
//Do your initialization here
{
std::unique_lock<std::mutex> lck( g_mtx );
for( double val = 0; val < 10; val += 0.3 )
g_my_answer.push_back( val );
g_ready = true;
lck.unlock();
g_cv.notify_one();
}
//Keep doing your other work..., here we'll just sleep
for( int i = 0; i < 100; ++i )
{
std::this_thread::sleep_for( std::chrono::seconds(1) );
}
}
void Caller()
{
std::unique_lock<std::mutex> lck(g_mtx);
std::thread worker_thread = std::thread( Worker_treadfun_with_init );
//Calling wait will cause current thread to sleep until g_cv.notify_one() is called.
g_cv.wait( lck, [&g_ready](){ return g_ready; } );
//Print out the answer as the worker thread continues doing its work
for( auto val : g_my_answer )
std::cout << val << std::endl;
//Unlock mutex (or better yet have unique_lock go out of scope)
// incase worker thread needs to lock again to finish
lck.unlock();
//...
//Make sure to join the worker thread some time later on.
worker_thread.join();
}
Of course in actual code you wouldnt use global variables, and instead pass them by pointer or reference (or as member variables of SomeClass) to the worker function, but you get the point.

How to Monitor Qt Signal Event Queue Depth

There are two objects in my program. One object is emitting a signal. The other one receives the signal in a slot and processes the incoming signals one by one. Both objects are running in different threads. Now I need to measure and monitor the workload for my receiving object.
The problem is I do not know how many signals are waiting for my second object to process in the Qt signal queue. Is there a way to get the size of this queue? Or is there a work around to know how many signals have to be still proecessed?
The qGlobalPostedEventsCount() is a starting point, although it only works for the current thread.
To poll an arbitrary thread, we can to use Qt's internals. The implementation is then very simple. It works even when the thread is blocked and doesn't process events.
// https://github.com/KubaO/stackoverflown/tree/master/questions/queue-poll-44440584
#include <QtCore>
#include <private/qthread_p.h>
#include <climits>
uint postedEventsCountForThread(QThread * thread) {
if (!thread)
return -1;
auto threadData = QThreadData::get2(thread);
QMutexLocker lock(&threadData->postEventList.mutex);
return threadData->postEventList.size() - threadData->postEventList.startOffset;
}
uint postedEventsCountFor(QObject * target) {
return postedEventsCountForThread(target->thread());
}
If one really wishes not to use private APIs, we can have a less straightforward solution with more overhead. First, let's recall that the lowest overhead means of "doing stuff in some object's thread" is to do said "stuff" in an event's destructor - see this answer for more details. We can post the highest priority event to the target object's event queue. The event wraps a task that invokes qGlobalPostedEventsCount, updates the count variable, and releases a mutex that we then acquire. At the time of mutex acquisition, the count has a valid value that is returned. If the target thread is unresponsive and the request times out, -1 is returned.
uint qGlobalPostedEventsCount(); // exported in Qt but not declared
uint postedEventsCountForPublic(QObject * target, int timeout = 1000) {
uint count = -1;
QMutex mutex;
struct Event : QEvent {
QMutex & mutex;
QMutexLocker lock;
uint & count;
Event(QMutex & mutex, uint & count) :
QEvent(QEvent::None), mutex(mutex), lock(&mutex), count(count) {}
~Event() {
count = qGlobalPostedEventsCount();
}
};
QCoreApplication::postEvent(target, new Event(mutex, count), INT_MAX);
if (mutex.tryLock(timeout)) {
mutex.unlock();
return count;
}
return -1;
}
And a test harness:
int main(int argc, char ** argv) {
QCoreApplication app(argc, argv);
struct Receiver : QObject {
bool event(QEvent *event) override {
if (event->type() == QEvent::User)
QThread::currentThread()->quit();
return QObject::event(event);
}
} obj;
struct Thread : QThread {
QMutex mutex;
Thread() { mutex.lock(); }
void run() override {
QMutexLocker lock(&mutex);
QThread::run();
}
} thread;
thread.start();
obj.moveToThread(&thread);
QCoreApplication::postEvent(&obj, new QEvent(QEvent::None));
QCoreApplication::postEvent(&obj, new QEvent(QEvent::None));
QCoreApplication::postEvent(&obj, new QEvent(QEvent::None));
QCoreApplication::postEvent(&obj, new QEvent(QEvent::User));
auto count1 = postedEventsCountFor(&obj);
thread.mutex.unlock();
auto count2 = postedEventsCountForPublic(&obj);
thread.wait();
auto count3 = postedEventsCountFor(&obj);
Q_ASSERT(count1 == 4);
Q_ASSERT(count2 == 4);
Q_ASSERT(count3 == 0);
}
QT = core-private
CONFIG += console c++11
CONFIG -= app_bundle
TARGET = queue-poll-44440584
TEMPLATE = app
SOURCES += main.cpp

Qt timers cannot be stopped from another thread

Hy,
I'm writing my first Qt program and getting now in troubles with:
QObject::killTimer: timers cannot be stopped from another thread
QObject::startTimer: timers cannot be started from another thread
My program will communicate to a CANOpen bus for that I'm using the Canfestival Stack. The Canfestival will work with callback methods. To detects timeout in communication I setup a timer function (somehow like a watchdog). My timer package consist out of a "tmr" module, a "TimerForFWUpgrade" module and a "SingleTimer" module. The "tmr" module was originally C programmed so the static "TimerForFWUpgrade" methods will interface it. The "tmr" module will be part of a C programed Firmware update package.
The timer will work as follows. Before a message is sent I will call TMR_Set method. An then in my idle program loop with TMR_IsElapsed we check for a timer underflow. If TMR_IsElapsed I will do the errorhandling. As you see the TMR_Set method will be called continuously and restart the QTimer again and again.
The above noted errors are appearing if I start my program. Can you tell me if my concept could work? Why does this errors appear? Do I have to use additional threads (QThread) to the main thread?
Thank you
Matt
Run and Idle loop:
void run
{
// start communicate with callbacks where TMR_Set is set continously
...
while(TMR_IsElapsed(TMR_NBR_CFU) != 1);
// if TMR_IsElapsed check for errorhandling
....
}
Module tmr (interface to C program):
extern "C"
{
void TMR_Set(UINT8 tmrnbr, UINT32 time)
{
TimerForFWUpgrade::set(tmrnbr, time);
}
INT8 TMR_IsElapsed(UINT8 tmrnbr)
{
return TimerForFWUpgrade::isElapsed(tmrnbr);
}
}
Module TimerForFWUpgrade:
SingleTimer* TimerForFWUpgrade::singleTimer[NR_OF_TIMERS];
TimerForFWUpgrade::TimerForFWUpgrade(QObject* parent)
{
for(unsigned char i = 0; i < NR_OF_TIMERS; i++)
{
singleTimer[i] = new SingleTimer(parent);
}
}
//static
void TimerForFWUpgrade::set(unsigned char tmrnbr, unsigned int time)
{
if(tmrnbr < NR_OF_TIMERS)
{
time *= TimerForFWUpgrade::timeBase;
singleTimer[tmrnbr]->set(time);
}
}
//static
char TimerForFWUpgrade::isElapsed(unsigned char tmrnbr)
{
if(true == singleTimer[tmrnbr]->isElapsed())
{
return 1;
}
else
{
return 0;
}
}
Module SingleTimer:
SingleTimer::SingleTimer(QObject* parent) : QObject(parent),
pTime(new QTimer(this)),
myElapsed(true)
{
connect(pTime, SIGNAL(timeout()), this, SLOT(slot_setElapsed()));
pTime->setTimerType(Qt::PreciseTimer);
pTime->setSingleShot(true);
}
void SingleTimer::set(unsigned int time)
{
myElapsed = false;
pTime->start(time);
}
bool SingleTimer::isElapsed()
{
QCoreApplication::processEvents();
return myElapsed;
}
void SingleTimer::slot_setElapsed()
{
myElapsed = true;
}
Use QTimer for this purpose and make use of SIGNALS and SLOT for the purpose of starting and stopping the timer/s from different threads. You can emit the signal from any thread and catch it in the thread which created the timer to act on it.
Since you say you are new to Qt, I suggest you go through some tutorials before proceeding so that you will know what Qt has to offer and don't end up trying to reinvent the wheel. :)
VoidRealms is a good starting point.
You have this problem because the timers in the static array is created in Thread X, but started and stopped in Thread Y. This is not allowed, because Qt rely on thread affinity to timeout timers.
You can either create, start stop in the same thread or use signal and slots to trigger start and stop operations for timers. The signal and slot solution is a bit problematic Because you have n QTimer objects (Hint: how do you start the timer at position i?)
What you can do instead is create and initialize the timer at position tmrnbr in
TimerForFWUpgrade::set(unsigned char tmrnbr, unsigned int time)
{
singleTimer[tmrnbr] = new SingleTimer(0);
singleTimer[tmrnbr]->set(time);
}
which is executed by the same thread.
Futhermore, you don't need a SingleTimer class. You are using Qt5, and you already have all you need at your disposal:
SingleTimer::isElapsed is really QTimer::remainingTime() == 0;
SingleTimer::set is really QTimer::setSingleShot(true); QTimer::start(time);
SingleTimer::slot_setElapsed becomes useless
ThusSingleTimer::SingleTimer becomes useless and you dont need a SingleTimer class anymore
I got the errors away after changing my timer concept. I'dont use anymore my SingleTimer module. Before the QTimer I won't let timeout and maybe because of that I run into problems. Now I have a cyclic QTimer that times out every 100ms in slot function I will then count the events. Below my working code:
TimerForFWUpgrade::TimerForFWUpgrade(QObject* parent) : QObject(parent),
pTime(new QTimer(this))
{
connect(pTime, SIGNAL(timeout()), this, SLOT(slot_handleTimer()));
pTime->setTimerType(Qt::PreciseTimer);
pTime->start(100);
}
void TimerForFWUpgrade::set(unsigned char tmrnbr, unsigned int time)
{
if(tmrnbr < NR_OF_TIMERS)
{
if(timeBase != 0)
{
myTimeout[tmrnbr] = time / timeBase;
}
else
{
myTimeout[tmrnbr] = 0;
}
myTimer[tmrnbr] = 0;
myElapsed[tmrnbr] = false;
myActive[tmrnbr] = true;
}
}
char TimerForFWUpgrade::isElapsed(unsigned char tmrnbr)
{
QCoreApplication::processEvents();
if(tmrnbr < NR_OF_TIMERS)
{
if(true == myElapsed[tmrnbr])
{
return 1;
}
else
{
return 0;
}
}
else
{
return 0; // NOK
}
}
void TimerForFWUpgrade::slot_handleTimer()
{
for(UINT8 i = 0; i < NR_OF_TIMERS; i++)
{
if(myActive[i] == true)
{
myTimer[i]++;
if(myTimeout[i] < myTimer[i])
{
myTimer[i] = 0;
myElapsed[i] = true;
myActive[i] = false;
}
}
}
}