-
Notifications
You must be signed in to change notification settings - Fork 118
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Delay of 0.1 seconds between asyncio MPD commands causes hang #173
Comments
yakshaver2000
pushed a commit
to yakshaver2000/python-mpd2
that referenced
this issue
Feb 9, 2023
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()) ```
yakshaver2000
added a commit
to yakshaver2000/python-mpd2
that referenced
this issue
Feb 9, 2023
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()) ```
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Consider this snippet:
Running this with Python 3.9.5 and python-mpd2 3.0.4, I get this output:
At this point, the second call to
client.status()
never completes, and the program hangs. If the 0.1 second delay is changed to any other value, the problem goes away. It seems like this is some kind of race condition withIMMEDIATE_COMMAND_TIMEOUT
.The text was updated successfully, but these errors were encountered: