Skip to content

Qt Ctrl-C broken on windows #20932

Closed
Closed

Description

@tacaswell

@anntzer @tacaswell
Hi! I have just tested the master branch on Windows and found that there is indeed a problem with the QSocketNotifier (it appeared here: #13306 (comment)). Previously, on that Windows machine, I was running my old code with QAbstractSocket which was fine, and did not test the newest version with QSocketNotifier there (my fault, sorry). I have tested on Linux, and there is no such problem there.

The code to reproduce on Windows (in Jupyter):

%matplotlib qt
from matplotlib import pyplot as plt
plt.plot([1,2])
plt.pause(.1)
plt.pause(.1)
plt.pause(.1)

The problem seems to be that if socketpair generates a socket with the same fileno value as it generated in the previous pause() call, the QSocketNotifier unexpectedly fires and hangs here:

sn.activated.connect(lambda *args: rsock.recv(1))

because nothing was actually written to the wakeup_fd. I don't understand how that can be.

Workaround fix:

rsock.set_blocking(False)

@sn.activated.connect
def on_signal(*args):
    try:
        signal = rsock.recv(1)  # clear the socket to re-arm the notifier
    except BlockingIOError:
        pass # false-trigger, nothing on rsock

What should we do with this? Is there a better solution?

Originally posted by @vdrhtc in #13306 (comment)

Activity

  1. added this to the v3.5.0 milestone on Aug 28, 2021
  2. added
    Release criticalFor bugs that make the library unusable (segfaults, incorrect plots, etc) and major regressions.
    on Aug 28, 2021
  3. QuLogic commented on Aug 30, 2021

    @QuLogic
    Member

    Is it the current QSocketNotifier that fires, or the previous one? Are we just missing some cleanup?

  4. vdrhtc commented on Aug 31, 2021

    @vdrhtc
    Contributor

    @QuLogic
    image

    Here how it looks like, I don't think it is the same QSocketNotifier, it is a different object every time. But false-trigger only happens if the fileno is re-used (1092 in this case, for 1028 nothing happens). So maybe it is necessary to clear the rsock somehow.

  5. QuLogic commented on Sep 1, 2021

    @QuLogic
    Member

    Does something like this work?

    import select
    import socket
    
    
    for i in range(10):
        wsock, rsock = socket.socketpair()
        wsock.setblocking(False)
        rfd = rsock.fileno()
        wfd = wsock.fileno()
        print('rsock:', rfd, 'wsock:', wfd)
        ready = select.select([rfd], [], [], 2)
        print('ready:', ready)
        wsock.close()
        rsock.close()

    It should print out that no sockets are ready. Windows support was only added in 3.5, but maybe there's a bug?

    Maybe we should call socket.shutdown on the sockets to force them to be disabled, even though we do close them:

    Shut down one or both halves of the connection. If how is SHUT_RD, further receives are disallowed. If how is SHUT_WR, further sends are disallowed. If how is SHUT_RDWR, further sends and receives are disallowed.

  6. QuLogic commented on Sep 1, 2021

    @QuLogic
    Member

    Also, does reusing the existing socket pair work?

    wsock, rsock = socket.socketpair()
    wsock.setblocking(False)
    sn = QtCore.QSocketNotifier(
        rsock.fileno(), _enum('QtCore.QSocketNotifier.Type').Read
    )
    sn.activated.connect(lambda *args: print('activated 1'))
    del sn
    sn = QtCore.QSocketNotifier(
        rsock.fileno(), _enum('QtCore.QSocketNotifier.Type').Read
    )
    sn.activated.connect(lambda *args: print('activated 2'))
  7. vdrhtc commented on Sep 1, 2021

    @vdrhtc
    Contributor

    Nothing unexpected in the first test despite occasional re-uses:
    image

    Second test also works:
    image

    If I remove the del sn line, then there is a warning about multiple notifiers for the same socket in the terminal, and no activation happens on both notifiers.

    Calling

    wsock.shutdown(socket.SHUT_RDWR)
    rsock.shutdown(socket.SHUT_RDWR)
    wsock.close()
    rsock.close()

    does not help.

  8. QuLogic commented on Sep 1, 2021

    @QuLogic
    Member

    Actually, for that second test, maybe we should run the event loop in between?

    wsock, rsock = socket.socketpair()
    wsock.setblocking(False)
    
    sn = QtCore.QSocketNotifier(
        rsock.fileno(), _enum('QtCore.QSocketNotifier.Type').Read
    )
    sn.activated.connect(lambda *args: print('activated 1'))
    # 5 seconds of event processing should be enough?
    event_loop = QtCore.QEventLoop()
    timer = QtCore.QTimer.singleShot(5000, event_loop.quit)
    qt_compat._exec(event_loop)
    
    del sn
    
    sn = QtCore.QSocketNotifier(
        rsock.fileno(), _enum('QtCore.QSocketNotifier.Type').Read
    )
    sn.activated.connect(lambda *args: print('activated 2'))
    # 5 seconds of event processing should be enough?
    event_loop = QtCore.QEventLoop()
    timer = QtCore.QTimer.singleShot(5000, event_loop.quit)
    qt_compat._exec(event_loop)
  9. vdrhtc commented on Sep 16, 2021

    @vdrhtc
    Contributor

    @QuLogic For some reason, the event loop does not exit after 5s and then the program hangs...

    I've obtained the previous results by running %matplotlib qt first (it probably starts the event loop correctly by itself)

  10. timhoffm commented on Oct 1, 2021

    @timhoffm
    Member

    Is this fixed by #20907?

  11. QuLogic commented on Oct 1, 2021

    @QuLogic
    Member

    It should be.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    GUI: QtOS: MicrosoftRelease criticalFor bugs that make the library unusable (segfaults, incorrect plots, etc) and major regressions.

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions