25

Is qDebug() thread-safe? By thread-safe I don't just mean not-crashing, but also if I call qDebug() from different threads, is it possible for the output to become mixed-up? I tested it with this code, and it doesn't appear to be so, however, I couldn't find anywhere in the documentation where they talk about this.

This is my test code:

#include <QtConcurrent>
#include <QApplication>
void print_a() {
    for (int ii = 0; ii < 10000; ii++) {
        qDebug("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
    }
}
void print_b()
{
    for (int ii = 0; ii < 10000; ii++) {
        qDebug("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
    }
}
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QtConcurrent::run(print_a);
    QtConcurrent::run(print_b);
    return a.exec();
}

There were no 'a' and 'b' mixed in the same line anywhere, but I'm still not sure if it's 100% thread safe...

mozzbozz
  • 3,052
  • 5
  • 31
  • 44
sashoalm
  • 75,001
  • 122
  • 434
  • 781
  • 2
    The docs say [*If a function is not marked as thread-safe or reentrant, it should not be used from different threads*](http://qt-project.org/doc/qt-5.0/qtcore/threads-reentrancy.html). In case of [`qDebug()`](http://qt-project.org/doc/qt-5.0/qtcore/qtglobal.html#qDebug) it doesn't say it's thread-safe, so it's probably not safe to use from different threads. – thuga Mar 20 '14 at 09:54
  • @thuga That's a valid answer to my question, then, you should post it :) – sashoalm Mar 20 '14 at 11:57
  • I posted my comment as an answer. – thuga Mar 20 '14 at 12:01

7 Answers7

16

The docs say If a function is not marked as thread-safe or reentrant, it should not be used from different threads. In case of qDebug() it says: Note: This function is thread-safe.

(this answer was updated... the docs didn't state that the function was thread-safe before.)

thuga
  • 12,601
  • 42
  • 52
  • Wait, I actually just thought about that some more, and thread-safety is about using the same object, right? But `qDebug() << "foo";` constructs a new `QDebug` object, so we would be talking about reentrancy, really, not thread-safety. Sorry I didn't mention it earlier, but I figured that out now. – sashoalm Mar 20 '14 at 12:16
  • Yes, but `QRect` and `QPoint` are not, either. I posted a new question specifically about the reentrancy issue, you can see it at http://stackoverflow.com/questions/22535094/qt-documentation-and-reentrancy – sashoalm Mar 20 '14 at 13:54
16

Following are my answer and comments:

  1. If the documentation of qDebug() does not mention whether it is thread-safe or not, we should assume it is not. The answer is likely platform-dependent: how qDebug() is implemented at the system level (Linux, Windows, ...).

  2. Instead of the broader question of thread-safety, I think you were asking a more specific question like this: "Will the use of qDebug() in a multi-threaded application lead to interleaved output lines?" The answer is "Yes, occasionally." as demonstrated by the results produced by @dmcontador above. And the probability increases when the strings to be printed out are getting longer, as explained by @quetzalcoatl above.

  3. The answer does not depend on whether you use qDebug("...") or qDebug() << "...", as both will finally call the system-level implementation code.

  4. It is not easy for me to produce interleaved output lines using your original example code. So I have created a new example as shown below:

    #include <QCoreApplication>
    #include <QtConcurrent>
    
    #define MAX_ITERS 10
    #define MAX_LEN   10000
    
    void print_a()
    {
        QString a(MAX_LEN, 'a');
    
        for(int i = 0; i < MAX_ITERS; ++i) {
            qDebug().noquote() << a;
        }
    }
    
    void print_b()
    {
        QString b(MAX_LEN, 'b');
    
        for(int i = 0; i < MAX_ITERS; ++i) {
            qDebug().noquote() << b;
        }
    }
    
    int main(int argc, char * argv[])
    {
        QCoreApplication a(argc, argv);
        QtConcurrent::run(print_a);
        QtConcurrent::run(print_b);
        return 0;
    }
    

The probability increases when you increase MAX_LEN.

  1. A follow-up question would be: "How to use qDebug() to produce non-interleaved output lines?" One solution would be to use QMutex on each and every qDebug() line. Note that I have not tried this solution which is not practical.
jonathanzh
  • 1,346
  • 15
  • 21
  • 1
    qDebug and QDebug itself _is_ thread-safe. What you see as 'interleaved output' stems from the fact that stderr (which Qt logs to by default on most configurations) is not buffered. If you have such long lines, I suggest to use a different logging sink instead (like syslogd on Linux). You can easily check this by replacing qDebug() with fprintf(stderr, ...) calls. – kkoehne Oct 12 '17 at 10:03
8

I'm afraid that it is not thread-safe. Also, I tried your code and had mixed output.

aaaaaaaaaaaabbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbabbbbbbbbbbbbbbbbbb

I had the same luck with qDebug() << "..."

Tested in Qt5.2.1 with mingw48_32 compiler.

dmcontador
  • 660
  • 1
  • 8
  • 18
5

In fact, what QtDebug-related functions (such as qDebug(), qWarning() and so on) do, is call message handler that can be set by calling qInstallMessageHandler(). So it's up to you - will this message handler be thread-safe or not. There is a default implementation which just prints messages to stderr, it doesn't prevent mixed output from different threads, so, if you want to have always non-mixed line-by-line output for your debug messages, warnings and errors from different threads, you should install your own handler with some sort of locking (such as QMutex) like this:

QMutex messageMutex;

void myMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
    QMutexLocker locker(&messageMutex);

    [print msg and context somewhere]
}

int main(int argc, char **argv)
{
    qInstallMessageHandler(myMessageHandler);

    QApplication app(argc, argv);

    [...]
}

NOTE: as Kuba Ober correctly noted, this should be used with caution (like any locking in general, though). For example, you can get a deadlock if QtDebug functions are called from I/O library internals while using same I/O library to output debug messages (this is possible, for example, when QtDebug message handler calls I/O while holding a lock on non-recursive mutex, then underlying I/O machinery calls some callback function, and then this callback function calls QtDebug function, which calls the same handler again).

  • It should be noted that **this is possibly a terrible idea**. Why? Because if you end up invoking the message handler while other mutexes are held, you may end up with a deadlock as there's no guarantee that the mutexes would be acquired in the order necessary to prevent deadlocks. For example, if the standard output is used anywhere else in your code, the C library may hold a mutex of its own, etc. It requires great care to insure that this code won't deadlock. It's possible, but don't get the idea that you can do this and it'll "just work". – Kuba hasn't forgotten Monica Feb 05 '19 at 13:57
  • You can get a deadlock if QDebug functions are called from some I/O library internals while using same I/O library to print debug messages, that's true. So this should be used with caution. I added corresponding note to my answer. – Oleg Derevenetz Feb 05 '19 at 16:18
2

I've found such thing: http://www.qtcentre.org/threads/28879-redirecting-qDebug-to-file-threading-question

Quoting:

To answer the question if qdebug is threadsafe: QDebug uses a QTextstream. A QTextStream is not threadsafe. The documentation is not clear about this, but if you look at the source code of qdebug or qtextstream you see there's no mutex locking at all in the code.

quetzalcoatl
  • 32,194
  • 8
  • 68
  • 107
  • That answer is from 2006, though. Considering my test code runs without mixing the output, things might have changed. I'll have to look at the source code probably. – sashoalm Mar 20 '14 at 09:46
  • 4
    Be careful - output streams may be buffered. Buffers may be internally synced (and often are!), but the whole mechanisms does not have to be. Try doing the same with very large strings so that the buffer will need to grow or chunk the strings and wait. I don't know how large any buffers may be, but your current test strings are quite short. – quetzalcoatl Mar 20 '14 at 10:35
  • 1
    That is about `QDebug` which you get by `qDebug()` not `qDebug(..)`. So it is not safe if you use `qDebug() << "aaaaaaaaaaaaaaa"`. Does not tell us anything about the C-style version – BeniBela Mar 20 '14 at 14:09
  • @BeniBela Actually `qDebug()` is a function that creates a brand new `QDebug` instance. So the threads would not be accessing **the same data**, unless global variables are involved. – sashoalm Mar 20 '14 at 15:18
2

Practically qDebug( ..text.. ) is thread-safe (at least if compiled with gcc).

If you look in the qt (4) source file qglobal.cpp, qDebug calls qt_message_output which calls fprintf(stderr, ...), which is thread-safe in glibc

(qDebug() << .. is another story )

BeniBela
  • 16,412
  • 4
  • 45
  • 52
  • Why would `qDebug()` be another story? Doesn't it call `fprintf` as well? – sashoalm Mar 20 '14 at 15:16
  • I posted a [question about fprintf for MSVCRT](http://stackoverflow.com/questions/22539282/is-msvcrts-implementation-of-fprintf-thread-safe) as well, since I use Qt on Windows. – sashoalm Mar 20 '14 at 16:30
  • 1
    It seems `fprintf()` is thread-safe for MSVCRT also, so using Qt with Visual Studio toolchain, `qDebug()` should still be thread-safe. – sashoalm Mar 21 '14 at 09:41
  • This is **not** true. See this answer: http://stackoverflow.com/a/23517726/1202500 – mozzbozz Feb 17 '15 at 18:12
  • @mozzbozz: Perhaps it was changed in qt5? Btw, `#include ` was correct in the source. `QtConcurrent` does not exist in qt4 – BeniBela Feb 18 '15 at 18:58
  • @BeniBela: Well, that could be the reason for the different answers. However, one could say "never rely on a feature not guaranteed by the docs"... Ok, good point about `#include ` - couldn't find anything about it on google at the point I've edited the post... hmm, the [docs](http://doc.qt.io/qt-5/qtconcurrent.html) say, `QtConcurrent` is at least available since Qt 4.4. So I guess, this is the better testing code, because it's compatible to all "current" Qt versions (I guess no one uses < Qt 4.4 anymore? oO) – mozzbozz Feb 19 '15 at 00:12
1

Both

qDebug("xx")

as well as

qDebug() << "xx"

qInfo, qWarning, qCritical, and the categorized versions like qCDebug, qCInfo, qCWarning, qCritical are safe to be used concurrently from different threads.

However, you have to make sure that the log sink can also handle large data atomically. This is were the confusion comes from, because stderr apparently breaks line that are too long. You can easily verify this by just replacing qDebug() by fprintf(stderr) in the example: It shows exactly the same behavior for me.

You can try other logging sinks, like journald. Anyhow, they might impose restrictions on the maximum length, too. In general I'd suggest to keep the maximum length of a log message reasonable.

kkoehne
  • 1,176
  • 9
  • 17