Search code examples
c++qtx11sigpipe

How to avoid SIGPIPE (due to a timeout?) when debugging an X11 program?


Sometimes, when I'm debugging my Qt program on X11, I need to have a breakpoint at a point when the program has created a window (QWidget), but no window has yet been shown. In this case, when I resume the program from this breakpoint after some time has passed, the program gets SIGPIPE. This also happens when the program does some things before showing a window, and Valgrind slows it down to such a low speed that X11 again times out. At least it appears to be related to some kind of timeout.

I've managed to make a small test case which reproduces the issue exactly without the need to use a debugger or Valgrind:

#include <QThread>
#include <QWidget>
#include <QApplication>

int main(int argc, char** argv)
{
    QApplication app(argc, argv);
    QWidget widget;
    QThread::currentThread()->sleep(60);
    widget.show();
    return app.exec();
}

When this program gets SIGPIPE, its stack trace looks like follows:

Thread 1 "test" received signal SIGPIPE, Broken pipe.
0x00007ffff6385187 in __GI___libc_write (fd=6, buf=buf@entry=0x5555557be990, nbytes=nbytes@entry=80) at ../sysdeps/unix/sysv/linux/write.c:27
27      ../sysdeps/unix/sysv/linux/write.c: No such file or directory.
(gdb) bt
#0  0x00007ffff6385187 in __GI___libc_write (fd=6, buf=buf@entry=0x5555557be990, nbytes=nbytes@entry=80) at ../sysdeps/unix/sysv/linux/write.c:27
#1  0x00007fffed6e5cd8 in _IceTransSocketWrite (ciptr=0x5555557be4b0, buf=0x5555557be990 "\001\f\001", size=80) at /usr/include/X11/Xtrans/Xtranssock.c:2396
#2  0x00007fffed6ea558 in _IceWrite (iceConn=0x5555557c3d80, nbytes=<optimized out>, ptr=0x5555557be990 "\001\f\001") at ../../src/misc.c:350
#3  0x00007fffed6ea644 in IceFlush (iceConn=0x5555557c3d80) at ../../src/misc.c:78
#4  0x00007fffefa56c24 in sm_setProperty (name=0x5555557daa08 "Program", type=0x7fffefb0667a "ARRAY8", num_vals=1, vals=0x7fffffffccc0) at qxcbsessionmanager.cpp:123
#5  0x00007fffefa56ce8 in sm_setProperty (name="Program", value="/tmp/test/test") at qxcbsessionmanager.cpp:137
#6  0x00007fffefa57188 in sm_performSaveYourself (sm=0x5555557753e0) at qxcbsessionmanager.cpp:202
#7  0x00007fffefa56f93 in sm_saveYourselfCallback (smcConn=0x5555557bc9e0, clientData=0x5555557753e0, saveType=1, shutdown=0, interactStyle=0) at qxcbsessionmanager.cpp:180
#8  0x00007fffed8fdb28 in _SmcProcessMessage (iceConn=0x5555557c3d80, clientData=0x5555557bc9e0, opcode=<optimized out>, length=<optimized out>, swap=0, replyWait=<optimized out>, replyReadyRet=0x7fffffffd2d0) at ../../src/sm_process.c:241
#9  0x00007fffed6ee978 in IceProcessMessages (iceConn=0x5555557c3d80, replyWait=0x0, replyReadyRet=0x0) at ../../src/process.c:386
#10 0x00007fffefa57ae6 in QSmSocketReceiver::socketActivated (this=0x5555557be520) at qxcbsessionmanager.cpp:331
#11 0x00007fffefa58298 in QSmSocketReceiver::qt_static_metacall (_o=0x5555557be520, _c=QMetaObject::InvokeMetaMethod, _id=0, _a=0x7fffffffd4a0) at .moc/qxcbsessionmanager.moc:70
#12 0x00007ffff6f6521d in QMetaObject::activate (sender=0x5555557be450, signalOffset=3, local_signal_index=0, argv=0x7fffffffd4a0) at kernel/qobject.cpp:3795
#13 0x00007ffff6f649e6 in QMetaObject::activate (sender=0x5555557be450, m=0x7ffff733bda0 <QSocketNotifier::staticMetaObject>, local_signal_index=0, argv=0x7fffffffd4a0) at kernel/qobject.cpp:3648
#14 0x00007ffff6f72321 in QSocketNotifier::activated (this=0x5555557be450, _t1=6, _t2=...) at .moc/moc_qsocketnotifier.cpp:140
#15 0x00007ffff6f72081 in QSocketNotifier::event (this=0x5555557be450, e=0x7fffffffda20) at kernel/qsocketnotifier.cpp:266
#16 0x00007ffff74c1ce4 in QApplicationPrivate::notify_helper (this=0x5555557701d0, receiver=0x5555557be450, e=0x7fffffffda20) at kernel/qapplication.cpp:3736
#17 0x00007ffff74bf0a2 in QApplication::notify (this=0x7fffffffdc60, receiver=0x5555557be450, e=0x7fffffffda20) at kernel/qapplication.cpp:3093
#18 0x00007ffff6f1c919 in QCoreApplication::notifyInternal2 (receiver=0x5555557be450, event=0x7fffffffda20) at kernel/qcoreapplication.cpp:1060
#19 0x00007ffff6f1d316 in QCoreApplication::sendEvent (receiver=0x5555557be450, event=0x7fffffffda20) at kernel/qcoreapplication.cpp:1450
#20 0x00007ffff6fa5a27 in QEventDispatcherUNIXPrivate::activateSocketNotifiers (this=0x555555774f30) at kernel/qeventdispatcher_unix.cpp:304
#21 0x00007ffff6fa686e in QEventDispatcherUNIX::processEvents (this=0x7fffe4001460, flags=...) at kernel/qeventdispatcher_unix.cpp:509
#22 0x00007fffefa4715a in QXcbUnixEventDispatcher::processEvents (this=0x7fffe4001460, flags=...) at qxcbeventdispatcher.cpp:60
#23 0x00007ffff6f19169 in QEventLoop::processEvents (this=0x7fffffffdbd0, flags=...) at kernel/qeventloop.cpp:138
#24 0x00007ffff6f194a3 in QEventLoop::exec (this=0x7fffffffdbd0, flags=...) at kernel/qeventloop.cpp:225
#25 0x00007ffff6f1d15a in QCoreApplication::exec () at kernel/qcoreapplication.cpp:1363
#26 0x00007ffff584f1c4 in QGuiApplication::exec () at kernel/qguiapplication.cpp:1779
#27 0x00007ffff74be931 in QApplication::exec () at kernel/qapplication.cpp:2893
#28 0x0000555555554c34 in main (argc=1, argv=0x7fffffffdda8) at test.cpp:11

If I ignore this signal (e.g. say signal 0 in GDB), then I get the error

ICE default IO error handler doing an exit(), pid = 3850, errno = 32

so simply doing sigaction-like workaround isn't going to work.

My question is: if this is indeed some X11 timeout, how can I either disable this timeout, or increase it? If it's not a timeout, then what is it and how to avoid this SIGPIPE/ICE error problem?


Solution

  • It appears that if I call QApplication::processEvents() right after I create my instance of QApplication, then the SIGPIPE doesn't happen. Apparently, Qt somehow "confirms" application's validity to X11 in response to some event, which then lets the application be as slow as it pleases, without any fatal penalties.

    The edited example code follows:

    #include <QThread>
    #include <QWidget>
    #include <QApplication>
    
    int main(int argc, char** argv)
    {
        QApplication app(argc, argv);
        app.processEvents(); // This avoids SIGPIPE
        QWidget widget;
        QThread::currentThread()->sleep(60);
        widget.show();
        return app.exec();
    }
    

    I still don't know what exactly happens, what event needs a prompt reaction, and whether the timeout can be stretched, but at least the above described workaround lets me proceed with debugging.