classification
Title: socketserver: BaseServer.handle_request() infinite loop
Type: behavior Stage:
Components: Library (Lib) Versions: Python 3.7
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: jmberg
Priority: normal Keywords:

Created on 2019-05-23 22:06 by jmberg, last changed 2019-05-23 22:06 by jmberg.

Messages (1)
msg343330 - (view) Author: (jmberg) Date: 2019-05-23 22:06
Hi,

Alright - you may very well consider this to be a stupid idea and everything, but I figured I'd report it anyway, just so it's there even if you decide to ignore it.

For context, I'm running python in a ARCH=um Linux system that has completely virtual time, using basically this patch:
https://patchwork.ozlabs.org/patch/1095055/

Now, as I replied there myself, you would in fact think that this can lead to infinite loops, but not really in a select/poll loop in the socket server, hence this report.

We have the following code:

        [...]

        if timeout is not None:
            deadline = time() + timeout

        # Wait until a request arrives or the timeout expires - the loop is
        # necessary to accommodate early wakeups due to EINTR.
        with _ServerSelector() as selector:
            selector.register(self, selectors.EVENT_READ)

            while True:
                ready = selector.select(timeout)
                if ready:
                    return self._handle_request_noblock()
                else:
                    if timeout is not None:
                        timeout = deadline - time()
                        if timeout < 0:
                            return self.handle_timeout()

Assume that a timeout is given, so deadline is set to time() + timeout.

Now, if selector.select(timeout) returns [] because nothing was ready for reading, you'd expect us to treat this as a timeout, right?

However, in my case of virtual time with infinite processing power, it doesn't.

Let's say that timeout is 1 (second) and that the call to time() returned 10000 (clearly not realistic, but doesn't matter). Now, in a virtual time implementation with infinite processing power, select(timeout) will sleep for *exactly* 1 second, and return nothing is ready. Then, in the else: branch, we set "timeout = deadline - time()" - but now time will return 10001 (remember we slept for exactly 1 second), and timeout will be 0. We will thus not handle a timeout, instead, we'll go into select() again with a timeout of 0. Due to the "infinite processing power" aspect of this system, this will happen over and over again.

The trivial fix here is to handle it as a timeout "if timeout <= 0" rather than just "if timeout < 0".

Obviously, I can also fix my virtual time system, for example, I can give it less processing power by actually interrupting the process after some real time (which I implemented in https://patchwork.ozlabs.org/patch/1095814/).

An alternative is to make every "what time is it now" request actually take some virtual time, thus the time() calls cannot return the same value over and over again. This also fixes the issue I saw.

But it stands to reason that if it should happen that the new timeout is actually 0, we should really treat it as a timeout here in this code and make it <=0 rather than just <0.
History
Date User Action Args
2019-05-23 22:06:55jmbergcreate