Home > Mobile >  asyncio.wait_for didn't cancel the task
asyncio.wait_for didn't cancel the task

Time:09-30

After the asyncio.wait_for timeout, the task was not cancelled

The script below is the minimized script to reproduce it. The tcp server just sent two chars after 100 seconds later after client connected

import sys
import asyncio
import socket

async def test_single_call():
    reader, writer = await asyncio.open_connection(host='127.0.0.1', port=8888)
    try:
        msg = await asyncio.wait_for(reader.read(1), timeout=3)
        print("Unexcepted message received:" , msg, file=sys.stderr)
        assert False
    except asyncio.TimeoutError:
        pass

    msg = await reader.read(1)

loop = asyncio.get_event_loop()
loop.run_until_complete(test_single_call())
loop.close()

The tcpclient(code above) is expected to timeout 3 seconds later, and read again after that; but it seems the task was not cancelled after it was timeout. My python version is 3.6.9

Traceback (most recent call last):
  File "tcpclient.py", line 17, in <module>
    loop.run_until_complete(test_single_call())
  File "/usr/lib/python3.6/asyncio/base_events.py", line 484, in run_until_complete
    return future.result()
  File "tcpclient.py", line 14, in test_single_call
    msg = await reader.read(1)
  File "/usr/lib/python3.6/asyncio/streams.py", line 634, in read
    yield from self._wait_for_data('read')
  File "/usr/lib/python3.6/asyncio/streams.py", line 452, in _wait_for_data
    'already waiting for incoming data' % func_name)
RuntimeError: read() called while another coroutine is already waiting for incoming data

I also uploaded the tcp server here

CodePudding user response:

I can not duplicate your problem in Python 3.6 and Python 3.9. You can try the following test code if it does not work, specify you event loop, I used ProactorEventLoop for Python 3.9 and _WindowsSelectorEventLoop for Python 3.6 since I use Windows:

import asyncio

# MAKE SLEEP more than 5 to get another results
SLEEP = 3


async def main():
    try:
        reader, writer = await asyncio.open_connection(host="localhost", port=8888)
        # choose sleep number more than 3 to simulate slow connection

        try_again = False
        try:
            data = await asyncio.wait_for(reader.read(8192), SLEEP)
        except asyncio.TimeoutError:
            try_again = True
            print("Timeout")
        else:
            print(f"Without timeout data {data.decode('utf8')}")

        # add proper error handling
        if try_again:
            data = await asyncio.wait_for(reader.read(8192), 30)
            print(f"After timeout data {data.decode('utf8')}")

        if writer.can_write_eof():
            writer.write_eof()  # tell server that we sent all data

    except ConnectionAbortedError:
        # if our client was too slow
        print("Server timed out connection")
        writer.close()


if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    try:
        loop.run_until_complete(main())
    finally:
        loop.close()

CodePudding user response:

For Linux Python 3.6, it had this issue. two options:

  1. Upgrade to Python 3.8 or 3.9
  2. Replace the open_connection and StreamReader with loop.sock_connet, loop.sock_recv, loop.sock_sendall

eg:

import sys
import asyncio
import socket

async def test_single_call(loop):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock = await loop.sock_connect(sock, server_address)

    try:
        msg = await asyncio.wait_for(loop.sock_recv(sock, 1), timeout=3)
        print("Unexcepted message received:" , msg, file=sys.stderr)
        assert False
    except asyncio.TimeoutError:
        pass

    msg = await loop.sock_recv(sock, 1)

loop = asyncio.get_event_loop()
loop.run_until_complete(test_single_call(loop))
loop.close()

  • Related