4

I ran into a thread/memory problem recently while developing a Qt based appication in c++ and I am looking for a correct explanation. I can't really post a fully functioning example as that would require linking to Qt etc. But the problem is pretty clearly explained in a few short lines.

When I click a button on the gui, something like this happens:

void MainWindow::onClick(){

    std::vector<int> vec;
    vec.push_back(0);

    dev.connect(vec);

    // do some more stuff
}

In this instance, dev is a member of MainWindow and is of class type Device which represents hardware (or more accurately, hardware drivers) that I want to connect with. The code for connect is something like:

void Device::connect(const std::vector<int>& vec){
    // do some stuff with vec that takes a long time
}

The problem that I had was that the device drivers were throwing me exceptions because they were getting bad values out of vec. And indeed, when I would break into connect, the data was gone: in that scope vec was empty memory. I fixed the problem by using shared_ptrs.

My theory is that when I call dev.connect(vec) from the GUI thread, Qt actually puts that call on a separate thread. Then, that function takes a long time and Qt decides that it is time to go on and complete onClick (or something like that, maybe it happens immediately) so that by the time vec gets handled in Device::connect, it was already out of scope. This fits given the fact that shared_ptr saves the day here.

So my questions are, am I right about this? and can someone explain the details of Qt's implicit threading behavior or else point to some such explanation?

dmedine
  • 1,430
  • 8
  • 25
  • I believe it does spawn (private to Qt) threads. You could find out with a debugger (breakpoint on `pthread_create`on Linux). [This](https://stackoverflow.com/q/43141659/841108) is a similar question for GTK – Basile Starynkevitch Nov 30 '17 at 13:56
  • Qt does some implicit threading but it should not affect you in this situtation - the MainWindow's object instance's onClick signal and your slot are on the same object so sending/receiving qt-signals will be done on the same thread (unless you specifically passed Qt::Blocking/QueuedConnection). Is your Device Class derived from QObject? If so, check you're not implicitly calling the superclasses' QObject's connect() function. At which point does your vec go out of scope? I assume the assembly code between your last vec.push() and dev.connect(vec) is short enough to provide the answer. – markus-nm Nov 30 '17 at 14:03
  • Absolutely No, Qt never takes such decisions on your behalf, The documentation [warns many times](https://wiki.qt.io/Threads_Events_QObjects#Blocking_the_event_loop) about doing heavy work in the GUI thread. Instead, they encourage you (the developer using Qt) to [offload heavy job to worker threads](https://doc.qt.io/qt-5/thread-basics.html#using-threads). Something like that can **never** be done *implicitly* as it will always have very bad side effects (as you mentioned in your question)... – Mike Nov 30 '17 at 15:57
  • " I can't really post a fully functioning example as that would require linking to Qt etc." <-- Well, you don't have to link to Qt in your answer. Most people here have Qt installed, and they can try to run your code. The less effort it takes the person trying to run your code, the better it is. Many of my answers here are MCVEs ([1](https://stackoverflow.com/a/39521698/2666212), [2](https://stackoverflow.com/a/42733747/2666212), [3](https://stackoverflow.com/a/42581405/2666212), ...). You just have to copy and paste the code in my answer into a `main.cpp` file in order to run it... – Mike Nov 30 '17 at 16:05

2 Answers2

1

What you're asking is, is it possible for the QT ui thread to take so long in some task, that it gets interrupted, return from a function, and then attempts to resume from where it left off.

The answer is no. If a thread gets interrupted then it will either return to exactly where it was, or it's about to terminate. Attempting to do otherwise will likely get flagged by virus scanners & introduce nasty nasty bugs.

If you try to do something that takes a long time in the UI thread, then what can happen is that your UI becomes unresponsive, and the OS will complain that the application has become unresponsive (because the application is no longer able to react to events sent to it by the OS).

More likely is when a signal is raised in QT, there is no guarantee that it will be passed to the slots immediately, thus you will end up in the situation that you're describing.

The fact that shared_ptr 'saves the day' means that you're not looking at a situation where by you're corrupting your stack (a good thing, they're hard to debug); and a simple stack trace should answer your question.

UKMonkey
  • 6,941
  • 3
  • 21
  • 30
0

Plus one to UKMonkey, because the answer is indeed 'no'. Thanks for the details.

Investigating further, I found out that there was in fact another problem that was at the heart of the device driver complaint all along. I thought that it was the vector thing because (in this particular case) the empty vector could conceivably have been the root of the exception and the debugger was showing it as empty. Using shard_ptrs, I saw values in the vector, but still had exceptions. Then, I found the (unrelated and true) reason for the exceptions and fixed it. Going back to the non-shared_ptr version (but with the unrelated exception throwing bug squashed) the code ran as expected.

So, this was all just a red herring. But, I am happy that I know that the answer is fact 'no'. Qt doesn't do any funny business with threading when dealing with a straight forward scenario such as this. The code I showed does indeed block on the call to connect.

dmedine
  • 1,430
  • 8
  • 25