QElapsedTimer reporting strange values from within QThread
-
I changed the WheelProbeThread to subclass QObject instead of QThread, changed its void run() method to be a void process() slot, and added the folllowing in the main thread:
QThread *t = new QThread(); wheelProbeMonitor->moveToThread(t); connect(t, SIGNAL(started()), wheelProbeMonitor, SLOT(process())); t->start();
following the directions given here, but unfortunately the glitches didn't stop (value to be read should be consistent around 200):
I'm getting even more inclined to think this doesn't concern Qt at all, as much as the underlying scheduler or the sysfs library, although i'm still open to any other suggestion to improve the Qt part.Thanks,
Jack -
@JeKK666
Maybe you already moved the following code into the process function!speedTimer = new QElapsedTimer();
speedTimer->start();If not I'd try
@CP71 If i understand correctly, you are referring to the issue where objects in the QThread object constructor are actually instantiated by the parent thread: i thought this problem would be bypassed by using the worker thread way suggested by @KroMignon
-
@KroMignon yes i do; is that conceptually wrong?
@JeKK666 maybe you should give your processor/process time to actually write into that file, that you're polling?
I'm unsure how the system will handle such a permanent poll!
Add a msleep to that of maybe 1 ms ? (200ms to 1 seems like a decent enough resolution)
Not the kind of advice I give often :P
-
@CP71 If i understand correctly, you are referring to the issue where objects in the QThread object constructor are actually instantiated by the parent thread: i thought this problem would be bypassed by using the worker thread way suggested by @KroMignon
-
@KroMignon yes i do; is that conceptually wrong?
@JeKK666 said in QElapsedTimer reporting strange values from within QThread:
yes i do; is that conceptually wrong?
As written before, using a forever loop will lock the event loop, so no signal/slot are handled for all instances runing in this thread.
I would suggest you to:- create the QEleapsed timer in the rigth thread
- call
processEvents(QEventLoop::AllEvents);
in foreverloop
void WheelProbeThread::run() { char* buf[4]; short len; QElapsedTimer speedTimer; speedTimer.start(); while (1) { pollRetVal = poll(pollingFileDescriptors, 1, -1); if(pollingFileDescriptors[0].revents & POLLPRI) { //probe click len = read(pollingFileDescriptors[0].fd, buf, 4); //mandatory to make system register interrupt as served lseek(pollingFileDescriptors[0].fd, 0, 0); emit probeClick(); emit timeBetweenClicks(speedTimer.restart()); //measure delta t between read pulses } QThread::currentThread()->eventDispatcher()->processEvents(QEventLoop::AllEvents); } }
-
@JeKK666
From my experience is better to allocate any objects into run/process for thread affinity.From maya post:
@CP71
Suggestion from me. (I know nothing about devices/embedded/real-time! Nor about bicycles.)If @KroMignon's latest sorts you out, well and good.
Otherwise, given your use of
poll()
, can't you write a test, standalone, non-thread, non-UI, non-even-Qt command-line program which just sits and returns the times? Having by-passed everything else, at least you then know whether your timings are just they way they are because they are generated like that from whatever lower-level? -
@CP71
Suggestion from me. (I know nothing about devices/embedded/real-time! Nor about bicycles.)If @KroMignon's latest sorts you out, well and good.
Otherwise, given your use of
poll()
, can't you write a test, standalone, non-thread, non-UI, non-even-Qt command-line program which just sits and returns the times? Having by-passed everything else, at least you then know whether your timings are just they way they are because they are generated like that from whatever lower-level? -
@J-Hilk thanks, i tried to add a QThread::msleep(1) after fseek, didn' seem to make a percivable difference; also, please note that no file writing occurs on my side, unless we consider the cursor moving performed by fseek as writing.
@CP71 thanks, i also tried your suggestion by moving all the allocations in the run() method/process() slot, didn't seem to change the overall behavior.
@KroMignon thank you for your interest in my question; i'm sorry i don't fully understand your statement concerning "no signal/slot are handled for all instances runing in this thread": it seems to me that you are implying my code should not work at all because of the infinite loop, but that clearly isn't a problem: the thread is descheduled as soon as its operations are done, i.e. when hitting the next blocking poll(), so that the main thread can resume and carry on execution with the values reported via the timeBetweenClicks signal; could you please elaborate more?
@JonB that is the silliest and most brilliant suggestion i could have dreamed of... >.<'
it occured to me while reading your post that i have already cross compiled at least five CLI c programs that test hardware and its functionalities, so obviously the first thing to do would be to test the whole lower level below Qt...
Excellent point! -
@JeKK666 said in QElapsedTimer reporting strange values from within QThread:
yes i do; is that conceptually wrong?
As written before, using a forever loop will lock the event loop, so no signal/slot are handled for all instances runing in this thread.
I would suggest you to:- create the QEleapsed timer in the rigth thread
- call
processEvents(QEventLoop::AllEvents);
in foreverloop
void WheelProbeThread::run() { char* buf[4]; short len; QElapsedTimer speedTimer; speedTimer.start(); while (1) { pollRetVal = poll(pollingFileDescriptors, 1, -1); if(pollingFileDescriptors[0].revents & POLLPRI) { //probe click len = read(pollingFileDescriptors[0].fd, buf, 4); //mandatory to make system register interrupt as served lseek(pollingFileDescriptors[0].fd, 0, 0); emit probeClick(); emit timeBetweenClicks(speedTimer.restart()); //measure delta t between read pulses } QThread::currentThread()->eventDispatcher()->processEvents(QEventLoop::AllEvents); } }
@KroMignon said in QElapsedTimer reporting strange values from within QThread:
As written before, using a forever loop will lock the event loop, so no signal/slot are handled for all instances runing in this thread.
the OP only emits a signal, that is handled in the parent thread with an even loop running, so that should be no problem here
-
@J-Hilk thanks, i tried to add a QThread::msleep(1) after fseek, didn' seem to make a percivable difference; also, please note that no file writing occurs on my side, unless we consider the cursor moving performed by fseek as writing.
@CP71 thanks, i also tried your suggestion by moving all the allocations in the run() method/process() slot, didn't seem to change the overall behavior.
@KroMignon thank you for your interest in my question; i'm sorry i don't fully understand your statement concerning "no signal/slot are handled for all instances runing in this thread": it seems to me that you are implying my code should not work at all because of the infinite loop, but that clearly isn't a problem: the thread is descheduled as soon as its operations are done, i.e. when hitting the next blocking poll(), so that the main thread can resume and carry on execution with the values reported via the timeBetweenClicks signal; could you please elaborate more?
@JonB that is the silliest and most brilliant suggestion i could have dreamed of... >.<'
it occured to me while reading your post that i have already cross compiled at least five CLI c programs that test hardware and its functionalities, so obviously the first thing to do would be to test the whole lower level below Qt...
Excellent point!@JeKK666 said in QElapsedTimer reporting strange values from within QThread:
thank you for your interest in my question; i'm sorry i don't fully understand your statement concerning "no signal/slot are handled for all instances runing in this thread": it seems to me that you are implying my code should not work at all because of the infinite loop, but that clearly isn't a problem: the thread is descheduled as soon as its operations are done, i.e. when hitting the next blocking poll(), so that the main thread can resume and carry on execution with the values reported via the timeBetweenClicks signal; could you please elaborate more?
Sorry, my failure. The problem is only if you want to handle slots with this thread. By emitting a signal, the event is stored in the receiver thread event loop. So if this loop is not locked, there is no problem.
But as @JonB suggest, you should look with a simple C program if the problem is not at low level (driver and/or system level).
-
So, i'm back with results from the lower levels:
After logging a 100 values or so, results would suggest the problem lies somewhere in the Qt infrastructure; might as well be some unknown (to me) overhead, or some werid stuff going on about QElapsedTimer.
The next logical step, to me, would be to try using the simpler c code i wrote for the CLI program that runs in the screenshot to exclude QElapsedTimer from the equation, and see what results i can get.
Thanks @JonB for reminding me of stuff i made in the last year or so, at work, which i had completely forgot about!
We'll see how it goes from there.Thanks,
Jack -
I've substituted the QElapsedTimer with the C library time.h, using the clock_gettime(...) function in the same way i did in the cli test program: i get the same inconsistent results, nothing changed (implementation as described here).
I can then conclude it's not the fault of the QElapsedTimer class, instead it has likely more to do with QThread, or threads in general, anyways the system i devised does not seem reliable enough to do what i need.
Since the CLI program itself seems quite precise instead, i might as well run something similar in the background, and the use IPC to retreive the value from Qt, or perhaps i could simply write the value to a file on one side and read it from Qt.
-
Update! @JonB
While i was tampering and not making any useful test, i accidentally left the CLI program running in an ssh shell, telling me the time deltas between clicks, and i noticed that everything was going fine, until... i launched the Qt application.
All of a sudden i was getting the same randomly halved times in CLI as i was getting before within Qt, using QElapsedTimer!This begged further investigation, and i was able to determine that the glitches persist even after the Qt app is closed, until the next reboot; this led me to think that there is something that I did during the app initialization that messed up some system timers or whatnot.
So the first test that i performed was to remove my whole application from its cointainer (basically everything is contained within the mainwindow class) and running only this snippet, which prints an image on the framebuffer and then loiters around doing nothing:#include <QSplashScreen> //#include <QApplication> #include "mainwindow.h" int main(int argc, char *argv[]) { QApplication a(argc, argv); QPixmap pixmap(":images/logo.jpg"); QSplashScreen splash(pixmap); splash.show(); // MainWindow w; // w.show(); return a.exec(); }
Surprisingly enough, this was enough to witness the glitches on the interrupt's time reading... so, do i have to conclude that the Qt framework in itself has a way of messing around with my system, rendering it unusable even after the Qt application is closed?
Perhaps it's too heavy for the CPU or stuff? Or maybe i should find a more up to date BSP, which might contain several bugfixes and hopefully concerning this specific use case?I also narrowed down that the exact reason of why the reported times are almost exactly half of the nominal time, the reason being the interrupt is fired twice (or poll() returns twice, who knows), but why would such a thing happen or how is this related to the Qt application being executed is far beyond me...
For now it looks like i'll have to use different hardware, unless some miracle happens.
Thanks,
Jack -
Update! @JonB
While i was tampering and not making any useful test, i accidentally left the CLI program running in an ssh shell, telling me the time deltas between clicks, and i noticed that everything was going fine, until... i launched the Qt application.
All of a sudden i was getting the same randomly halved times in CLI as i was getting before within Qt, using QElapsedTimer!This begged further investigation, and i was able to determine that the glitches persist even after the Qt app is closed, until the next reboot; this led me to think that there is something that I did during the app initialization that messed up some system timers or whatnot.
So the first test that i performed was to remove my whole application from its cointainer (basically everything is contained within the mainwindow class) and running only this snippet, which prints an image on the framebuffer and then loiters around doing nothing:#include <QSplashScreen> //#include <QApplication> #include "mainwindow.h" int main(int argc, char *argv[]) { QApplication a(argc, argv); QPixmap pixmap(":images/logo.jpg"); QSplashScreen splash(pixmap); splash.show(); // MainWindow w; // w.show(); return a.exec(); }
Surprisingly enough, this was enough to witness the glitches on the interrupt's time reading... so, do i have to conclude that the Qt framework in itself has a way of messing around with my system, rendering it unusable even after the Qt application is closed?
Perhaps it's too heavy for the CPU or stuff? Or maybe i should find a more up to date BSP, which might contain several bugfixes and hopefully concerning this specific use case?I also narrowed down that the exact reason of why the reported times are almost exactly half of the nominal time, the reason being the interrupt is fired twice (or poll() returns twice, who knows), but why would such a thing happen or how is this related to the Qt application being executed is far beyond me...
For now it looks like i'll have to use different hardware, unless some miracle happens.
Thanks,
Jack@JeKK666
I don't know. It would be interesting if someone else comments, because they will know more than I. But your claim/finding that having had a Qt program show a splashscreen and then exit, and still affect timings after that, sounds fishy.Qt doesn't somehow do things which are not provided by your OS. It just makes appropriate calls. If there is something wrong in its code that would be a Qt bug, but otherwise it's down to your OS.
Since this has become a discussion, there is one thing I don't get, and I hope an expert will clarify. My lack of understanding is because I know nothing about embedded/Yocto. For a desktop Linux or Windows at least, it seems to me: you are relying on a timer to tell you how far apart some events are. But how do you know when your thread or process will be scheduled for a time slice? In a desktop OS at least, after your
poll()
gets notified of one event, it could take ages and miss events till the next time that piece of code with thepoll()
gets called again. Is that not right? -
@JeKK666
I don't know. It would be interesting if someone else comments, because they will know more than I. But your claim/finding that having had a Qt program show a splashscreen and then exit, and still affect timings after that, sounds fishy.Qt doesn't somehow do things which are not provided by your OS. It just makes appropriate calls. If there is something wrong in its code that would be a Qt bug, but otherwise it's down to your OS.
Since this has become a discussion, there is one thing I don't get, and I hope an expert will clarify. My lack of understanding is because I know nothing about embedded/Yocto. For a desktop Linux or Windows at least, it seems to me: you are relying on a timer to tell you how far apart some events are. But how do you know when your thread or process will be scheduled for a time slice? In a desktop OS at least, after your
poll()
gets notified of one event, it could take ages and miss events till the next time that piece of code with thepoll()
gets called again. Is that not right?@JonB I'll try to explaining as per my understanding.
The sysfs library provides some sort of "abstraction layer removal": the endpoint on which poll() is called represents a hardware resource (in this case a GPIO pin) made accessible in user space; it allows creation of a construct which resembles registering an interrupt service routine to react to an asynchronous event in the most timely manner, much as you would do in a microcontroller.
In Linux, hardware level access is restricted to kernel space applications, so one would need to write his own driver that access memory locations corresponding to, for instance, GPIO registers, register an IRQ number and all other sorts of voodoo I'm not very familiar about, having worked mostly with bare microcontrollers (no FreeRTOS involved).So, when you call poll() on the sysfs endpoint the thread blocks, waiting for a level change on the pin, coherent with the edge selected; once the transition occurs, the interrupt fires and sysfs will notify (somehow) the thread polling the endpoint, allowing it to execute ASAP.
It has its limits compared to a kernel driver, but for my frequency range (10-20hz) headroom should be plenty.
Same rules for basic ISRs apply, so code executed must be minimal, like setting a flag and let the main thread do the heavy lifting, or in this specific case compute the difference between the time now() and the last time this function was called.Sysfs itself is quite dated, i read somewhere that the version I'm using was deprecated some 3 years ago, but with the module i have it's the simplest option available.
I totally agree with your observations about the issue being in the OS, somewhere, for now I'll just move on to the next toy project, but i will regularly check back on this thread to see if more impressions come in, an apparently simple issue revealed itself to be a rather intricate mess :)