[New-bugs-announce] [issue41437] SIGINT blocked by socket operations like recv on Windows

Zhiming Wang report at bugs.python.org
Wed Jul 29 14:13:12 EDT 2020


New submission from Zhiming Wang <zmwangx at gmail.com>:

I noticed that on Windows, socket operations like recv appear to always block SIGINT until it's done, so if a recv hangs, Ctrl+C cannot interrupt the program. (I'm a *nix developer investigating a behavioral problem of my program on Windows, so please excuse my limited knowledge of Windows.)

Consider the following example where I spawn a TCP server that stalls connections by 5 seconds in a separate thread, and use a client to connect to it on the main thread. I then try to interrupt the client with Ctrl+C.

    import socket
    import socketserver
    import time
    import threading


    interrupted = threading.Event()


    class HoneypotServer(socketserver.TCPServer):
        # Stall each connection for 5 seconds.
        def get_request(self):
            start = time.time()
            while time.time() - start < 5 and not interrupted.is_set():
                time.sleep(0.1)
            return self.socket.accept()


    class EchoHandler(socketserver.BaseRequestHandler):
        def handle(self):
            data = self.request.recv(1024)
            self.request.sendall(data)


    class HoneypotServerThread(threading.Thread):
        def __init__(self):
            super().__init__()
            self.server = HoneypotServer(("127.0.0.1", 0), EchoHandler)

        def run(self):
            self.server.serve_forever(poll_interval=0.1)


    def main():
        start = time.time()
        server_thread = HoneypotServerThread()
        server_thread.start()
        sock = socket.create_connection(server_thread.server.server_address)
        try:
            sock.sendall(b"hello")
            sock.recv(1024)
        except KeyboardInterrupt:
            print(f"processed SIGINT {time.time() - start:.3f}s into the program")
            interrupted.set()
        finally:
            sock.close()
            server_thread.server.shutdown()
            server_thread.join()


    if __name__ == "__main__":
        main()

On *nix systems the KeyboardInterrupt is processed immediately. On Windows, the KeyboardInterrupt is always processed more than 5 seconds into the program, when the recv is finished.

I suppose this is a fundamental limitation of Windows? Is there any workaround (other than going asyncio)?

Btw, I learned about SIGBREAK, which when unhandled seems to kill the process immediately, but that means no chance of cleanup. I tried to handle SIGBREAK but whenever a signal handler is installed, the behavior reverts to that of SIGINT -- the handler is called only after 5 seconds have passed.

(I'm attaching a socket_sigint_sigbreak.py which is a slightly expanded version of my sample program above, showing my attempt at handler SIGBREAK. Both

    python .\socket_sigint_sigbreak.py --sigbreak-handler interrupt

and

    python .\socket_sigint_sigbreak.py --sigbreak-handler exit

stall for 5 seconds.)

----------
components: Windows
files: socket_sigint_sigbreak.py
messages: 374580
nosy: paul.moore, steve.dower, tim.golden, zach.ware, zmwangx
priority: normal
severity: normal
status: open
title: SIGINT blocked by socket operations like recv on Windows
type: behavior
versions: Python 3.8
Added file: https://bugs.python.org/file49348/socket_sigint_sigbreak.py

_______________________________________
Python tracker <report at bugs.python.org>
<https://bugs.python.org/issue41437>
_______________________________________


More information about the New-bugs-announce mailing list