Qt Threading Issues in Linux

Non-blocking reading in background Qt thread on Linux caused EAGAIN

Main answer

Ok, I finally got it. The errno is set internally when locking a mutex by Qt either when calling semaphore->acquire (); or when emitting a signal with emit ready (b); (Qt uses synchronisation objects for queued connections, obviously). Here's how to debug where errno change happens. I added the following line at the beginning of Worker::onStart:

qDebug() << QString("0x%1").arg(reinterpret_cast<quint64>(&errno), 0, 16);

and set a breakpoint on the next line in debugger. Having this address (e.g. 0x7fffda6ce668) I added a memory breakpoint in gdb with watch *0x7fffda6ce668 in the gdb console (if you use Qt Creator, enable Window -> Views -> Debugger Log). I immediately got the backtrace for the errno change:

#0  0x00007ffff63964ae in syscall () at ../sysdeps/unix/sysv/linux/x86_64/syscall.S:42
#1 0x00007ffff6eb0610 in QBasicMutex::lockInternal() () from /home/(my-user-name)/apps/Qt5.5.0/5.5/gcc_64/lib/libQt5Core.so.5
#2 0x00007ffff70a7199 in QCoreApplication::postEvent(QObject*, QEvent*, int) () from /home/(my-user-name)/apps/Qt5.5.0/5.5/gcc_64/lib/libQt5Core.so.5
#3 0x00007ffff70d3286 in QMetaObject::activate(QObject*, int, int, void**) () from /home/(my-user-name)/apps/Qt5.5.0/5.5/gcc_64/lib/libQt5Core.so.5
#4 0x000000000040494f in Worker::ready (this=0x8d1550, _t1=0 '\\000') at moc_Worker.cpp:142
#5 0x0000000000403dee in Worker::onStart (this=0x8d1550) at ../qt/Worker.cpp:63

Now, QMutex is implemented in corelib/thread/qmutex_linux.cpp and uses a futex which causes errno == 11 sometimes. I've no idea why this happens, sorry, might be someone's bug ;) You can check the qmutex_linux.cpp code and try to find relevant info on the net for yourself. If you are interested if a specific API call produces an error you can set errno=0 before this call and check it after the call. Btw, I tested it without any file io just sending a dummy character with ready(0), the result was the same. So the problem is not file io.

Previous answer (mostly irrelevant after your code changes)

I think what you're trying to achieve with a QMutex alone is classically done with QMutex and QWaitCondition:

void Worker::onStart ()
{
// ...
while (true) {
QMutexLocker locker(&mutex);
if(read (file, & b, 1) <= 0)
break;
emit ready (b);
// waitCondition unlocks the mutex and
// waits till waitCondition wakeAll/wakeOne is called
// signalling that Widget has finished processing
waitCondition.wait(&mutex);
}
// ...
}

void Worker::onRequest ()
{
// re-locks the mutex and continues the while cycle
waitCondition.wakeAll();
}

Here waitCondition is a member variable like mutex. I haven't checked this code. It's just for illustration of the idea, you may need to change it a little. Links for reference: QWaitCondition description and usage example.

Qt Gui not updated because of threading issue

Every GUI is executed in an infinite loop, so Qt also uses it, but the blocking tasks generate that the loop is not executed correctly, showing inadequate behaviors like the one you observe.

If one wants to execute blocking tasks it is advisable to execute it in another thread for it Qt provides several possibilities:

  • QThread

  • QThreadPool and QRunnable

  • QtConcurrent

I recommend the following link for you to choose the right option for your case.

If you want to update the GUI view with the information generated in another thread it is advisable to use signals and slots, or use QtConcurrent.

GUI Thread and Worker Thread

As mentioned, each program has one thread when it is started. This
thread is called the "main thread" (also known as the "GUI thread" in
Qt applications). The Qt GUI must run in this thread. All widgets and
several related classes, for example QPixmap, don't work in secondary
threads. A secondary thread is commonly referred to as a "worker
thread" because it is used to offload processing work from the main
thread.

Another way is to force the GUI to update for this we can use qApp->processEvents().

References:

  • http://doc.qt.io/qt-5/thread-basics.html
  • http://doc.qt.io/qt-5/threads-technologies.html

does qt's gui thread spawn threads under the hood when calling methods from outside objects?

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.

Qt - Close QThreads that are blocked by system-level calls

For Linux:

Sending a signal to the thread with pthread_kill() interrupted read() with failure code EINTR. sigaction() was used to register the signal, and the signal thrown was SIGUSR1.

// Global scope
void nothing(int signum) {}

...

// Within the start of the thread
pthread_t myThreadID = pthread_self(); // Get the thread ID
struct sigaction action;
action.sa_flags = 0;
sigemptyset(&action.sa_mask);
action.sa_handler = nothing;
sigaction(SIGUSR1, &action, NULL);

...

// When it's time to close the thread
pthread_kill(myThreadID, SIGUSR1);

For Windows:

Signaling the OVERLAPPED's hEvent with SetEvent() was used to unblock GetOverlappedResult().

// Store a reference to the event
HANDLE myEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

...

// Within the start of the thread
OVERLAPPED overlapped;
memset(&overlapped, 0, sizeof(overlapped));
overlapped.hEvent = myEvent;

...

// When it's time to close the thread
SetEvent(myEvent);


Related Topics



Leave a reply



Submit