Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
asyncio: fix race condition in command queue
When two commands were issued exactly 0.1 seconds apart, then `mpd.asyncio.MPDClient.__run` would incorrectly send an "idle" command to the server, and then attempt to parse the server's response to the second command as an "idle" response, causing errors like ``` mpd.base.ProtocolError: Expected key 'volume', got 'repeat' ``` as described by Mic92#195, and hangs as described by Mic92#173. The root of the problem is that wrapping `asyncio.Queue.get` in `asyncio.wait_for` can result in a `TimeoutError` exception even when the queue is not empty, as demonstrated by the following example (tested with python 3.9.2 and 3.11.2): ```python import asyncio TIMEOUT = 0.1 async def get_from_queue(queue): try: await asyncio.wait_for( queue.get(), timeout=TIMEOUT ) except asyncio.exceptions.TimeoutError: # This is counterintuitive: The "get" operation has timed out, # but the queue is not empty! assert not queue.empty() else: # This block is never executed. assert False async def main(): queue = asyncio.Queue() task = asyncio.create_task(get_from_queue(queue)) await asyncio.sleep(TIMEOUT) queue.put_nowait(1) await task asyncio.run(main()) ```
- Loading branch information