Reputation: 581
I have a problem, that remaining sent signals are not received after calling quit on QThread
object.
The scenario contains 2 additional threads (QThread
and std::thread
) and the main execution thread. Let's call the QThread
Q
, the std::thread
T
and the main thread M
.
In M
I create Q
, the Receiver-object R
"living" in Q
and the Sender-object S
. Also a std::thread
T
is created executing a bunch if emits with S
.
class Sender : public QObject
{
Q_OBJECT;
public:
std::vector<int> m_Sent;
Sender()
{
}
public slots:
signals:
void signal(int i);
public:
void send(int i)
{
m_Sent.emplace_back(i);
emit signal(i);
}
};
class Receiver : public QObject
{
Q_OBJECT;
public:
std::vector<int> m_Received;
Receiver()
{
}
void Connect(Sender* s)
{
connect(s, &Sender::signal, this, &Receiver::slot, Qt::QueuedConnection);
}
void Disconnect(Sender* s)
{
disconnect(s, &Sender::signal, this, &Receiver::slot);
}
public slots:
void slot(int i)
{
m_Received.emplace_back(i);
}
};
void main(int argc, char** argv)
{
QApplication app(argc, argv);
qint64 random_seed = QDateTime::currentMSecsSinceEpoch();
std::cout << "Setting random seed " << random_seed << "\n";
std::srand(random_seed);
std::unique_ptr<Receiver> R(new Receiver);
std::unique_ptr<Sender> S(new Sender);
auto actions = [&S]() {
int i = 0;
std::chrono::steady_clock::time_point current =
std::chrono::steady_clock::now();
std::chrono::steady_clock::time_point finish =
current + std::chrono::milliseconds(100);
while (current < finish)
{
std::this_thread::sleep_for(std::chrono::microseconds(std::rand()%1000));
S->send(i++);
std::this_thread::sleep_for(std::chrono::microseconds(std::rand()%1000));
S->send(i++);
std::this_thread::sleep_for(std::chrono::microseconds(std::rand()%1000));
S->send(i++);
std::this_thread::sleep_for(std::chrono::microseconds(std::rand()%1000));
S->send(i++);
std::this_thread::sleep_until(current + std::chrono::milliseconds(17));
current = std::chrono::steady_clock::now();
}
};
std::unique_ptr<QThread> Q(new QThread());
R->moveToThread(Q.get());
R->Connect(S.get());
Q->start();
std::thread T(actions);
T.join();
// approach 1:
QMetaObject::invokeMethod(Q.get(), "quit", Qt::QueuedConnection);
Q->wait(); // never returns
// approach 2:
Q->quit();
Q->wait(); // missing events
std::cout << "Sent: ";
for(auto v : S->m_Sent)
{
std::cout << v << " ";
}
std::cout << std::endl;
std::cout << "Received: ";
for(auto v : R->m_Received)
{
std::cout << v << " ";
}
std::cout << std::endl;
}
I'm working on Windows with VS2013 and Qt 5.5.1. I tested it with kind of counter in R
to track received signals. While debugging I went through all emits so all should be inserted to event loop in Q
. After Q.wait()
the counter for the slots do not correspond to the emitted signals. I would have expected the event loop with remaining input events was handled by Q.quit()
or Q.wait()
but seems not so, It's always that there is a cut of "event-stream" from a certain point onward. I tried now for 4 days going through Qt-Docu and several other stuff found by google, but no proposal worked so far.
Upvotes: 3
Views: 880
Reputation: 923
I am not 100% sure since the documentation is not crystal clear, but what makes you think that the even loop is processing all pending events before exiting ? My assumption would be that there is a check "should I exit" at every loop and that it can discard some pending events when the exit flag is set.
In order to summarize the discussion below, I would suggest to add a new signal that you emit from wherever you want (say for instance from the std::thread once you have emitted everything you wanted) that would get into the QThread event loop queue and be connected to the QThread quit method so that the thread exits when being processed. You can also avoid defining a new signal if you want.
Your code would look like (not tested):
Sender S = new Sender();
QThread Q = new QThread();
Receiver R = new Receiver();
R->moveToThread(Q);
connect(S, &Sender::signal, R, &Receiver::slot, Qt::QueuedConnection);
Q->start();
while(!Q.isRunning())
{
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
std::thread T([&S](){
emit S->signal(); // only an example, several other connects are used too
})
T.join();
QMetaObject::invokeMethod(Q, "quit",
Qt::QueuedConnection);
Q.wait();
Upvotes: 2