Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 671
Ryan Avatar asked Jun 08 '17 16:06

Ryan


1 Answers

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
like image 100
Kuba hasn't forgotten Monica Avatar answered Sep 27 '22 01:09

Kuba hasn't forgotten Monica