Is qDebug() thread-safe?
Asked Answered
I

7

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...

Isleen answered 20/3, 2014 at 8:25 Comment(3)
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 doesn't say it's thread-safe, so it's probably not safe to use from different threads.Bigler
@Bigler That's a valid answer to my question, then, you should post it :)Isleen
I posted my comment as an answer.Bigler
B
15

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.
Bonaventura answered 30/4, 2015 at 0:5 Comment(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.Albatross
B
17

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.)

Bigler answered 20/3, 2014 at 12:0 Comment(2)
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.Isleen
Yes, but QRect and QPoint are not, either. I posted a new question specifically about the reentrancy issue, you can see it at #22535594Isleen
B
15

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.
Bonaventura answered 30/4, 2015 at 0:5 Comment(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.Albatross
P
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.

Photoactinic answered 7/5, 2014 at 12:23 Comment(1)
upvoted because I could confirm this with Qt 5.3.1 (MinGW 4.8.2 32bit)Bonnet
P
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).

Pratte answered 5/2, 2019 at 13:21 Comment(3)
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".Microelectronics
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.Pratte
@Kubahasn'tforgottenMonica if you have your own mutex then how it can interfere with mutexes held by C library? The deadlock could happen only when myMessageHandler() would call itself.Spider
H
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.

Harewood answered 20/3, 2014 at 9:27 Comment(4)
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.Isleen
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.Harewood
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 versionOchs
@Ochs 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.Isleen
O
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 )

Ochs answered 20/3, 2014 at 15:12 Comment(6)
Why would qDebug() be another story? Doesn't it call fprintf as well?Isleen
I posted a question about fprintf for MSVCRT as well, since I use Qt on Windows.Isleen
It seems fprintf() is thread-safe for MSVCRT also, so using Qt with Visual Studio toolchain, qDebug() should still be thread-safe.Isleen
This is not true. See this answer: https://mcmap.net/q/527552/-is-qdebug-thread-safeBonnet
@mozzbozz: Perhaps it was changed in qt5? Btw, #include <QtConcurrentRun> was correct in the source. QtConcurrent does not exist in qt4Ochs
@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 <QtConcurrentRun> - couldn't find anything about it on google at the point I've edited the post... hmm, the docs 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)Bonnet
A
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.

Albatross answered 12/10, 2017 at 11:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.