-
-
Notifications
You must be signed in to change notification settings - Fork 95
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
dispatcher.wait_for future handling race condition #77
Comments
Managed to fix this 👍 |
ghost
pushed a commit
that referenced
this issue
Aug 23, 2020
Looks like, at least on my machine, asyncio immediately invokes anything you await rather than switching to another task on the queue first unless the call does raw IO. I have confirmed this with Epoll, Poll and Select selector implementations on a non-debug asyncio SelectorEventLoop implementation. This means that the bulk of dispatching an event would currently occur as soon as the event is dispatched rather than after another task runs, which could lead to immediate slowdown if other tasks are queued. Switching to sync dispatching and using create task to invoke the callback management "later" seems to speed up this implementation significantly and allows other race conditions we have not accounted for properly as part of #77 to be detectable with test scripts that saturate the event loop.
Reproduced with optimisations introduced in b9d2855 and the following test script.
import asyncio
import logging
import selectors
import hikari.impl.event_manager_base
logging.basicConfig(level="DEBUG")
class FooEvent(hikari.Event):
@property
def app(self) -> hikari.RESTAware:
return NotImplemented
class Dispatcher(hikari.impl.event_manager_base.EventManagerBase):
async def on_foo(self, _, __):
await self.dispatch(FooEvent())
instance = Dispatcher(NotImplemented, None)
async def producer():
i = 0
while True:
# Do a really short sleep, which puts an event on the selector and
# causes a different task to be invoked. Asyncio otherwise appears
# to immediately invoke the same task, meaning we would keep producing
# and never get a chance to consume. This might be a CPython bug, I
# am not sure.
# Note, setting to anything NON ZERO (even 0.0000000001) seems to hide
# this bug, annoyingly.
await asyncio.sleep(0)
i += 1
if not (i % 1000):
print("produced", i)
instance.consume_raw_event(NotImplemented, "FOO", {})
async def consumer():
i = 0
while True:
i += 1
if not (i % 1000):
print("consumed", i)
def pred(curry):
def predicate(event):
nonlocal curry
if curry:
return True
else:
curry = True
return False
await asyncio.gather(
instance.wait_for(FooEvent, timeout=None, predicate=pred(True)),
instance.wait_for(FooEvent, timeout=None, predicate=pred(False)),
instance.wait_for(FooEvent, timeout=None, predicate=pred(True)),
instance.wait_for(FooEvent, timeout=None, predicate=pred(False)),
)
async def runner():
asyncio.create_task(consumer())
asyncio.create_task(producer())
selector = selectors.SelectSelector()
asyncio.set_event_loop(asyncio.SelectorEventLoop(selector))
asyncio.get_event_loop().set_debug(True)
asyncio.get_event_loop().run_until_complete(runner())
asyncio.get_event_loop().run_forever() |
ghost
pushed a commit
that referenced
this issue
Aug 23, 2020
ghost
mentioned this issue
Aug 23, 2020
Merged
ghost
pushed a commit
that referenced
this issue
Aug 23, 2020
Looks like, at least on my machine, asyncio immediately invokes anything you await rather than switching to another task on the queue first unless the call does raw IO. I have confirmed this with Epoll, Poll and Select selector implementations on a non-debug asyncio SelectorEventLoop implementation. This means that the bulk of dispatching an event would currently occur as soon as the event is dispatched rather than after another task runs, which could lead to immediate slowdown if other tasks are queued. Switching to sync dispatching and using create task to invoke the callback management "later" seems to speed up this implementation significantly and allows other race conditions we have not accounted for properly as part of #77 to be detectable with test scripts that saturate the event loop.
ghost
pushed a commit
that referenced
this issue
Aug 23, 2020
ghost
pushed a commit
that referenced
this issue
Aug 23, 2020
ghost
pushed a commit
that referenced
this issue
Aug 24, 2020
ghost
pushed a commit
that referenced
this issue
Aug 24, 2020
ghost
pushed a commit
that referenced
this issue
Aug 24, 2020
* Optimised raw event dispatching to uncover bug. Looks like, at least on my machine, asyncio immediately invokes anything you await rather than switching to another task on the queue first unless the call does raw IO. I have confirmed this with Epoll, Poll and Select selector implementations on a non-debug asyncio SelectorEventLoop implementation. This means that the bulk of dispatching an event would currently occur as soon as the event is dispatched rather than after another task runs, which could lead to immediate slowdown if other tasks are queued. Switching to sync dispatching and using create task to invoke the callback management "later" seems to speed up this implementation significantly and allows other race conditions we have not accounted for properly as part of #77 to be detectable with test scripts that saturate the event loop. * Updated CLi script to show OS type as well. * Added code to allow debugging of asyncio loop blocking incidents. * Fixes #77 dispatcher wait_for race condition. * Removed async predicates for wait_for, removing last parts of race condition hopefully. * Fixes #77 dispatcher wait_for race condition.
ghost
added this to the 2.0.0 milestone
Sep 4, 2020
This issue was closed.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Steps to reproduce
Trying to handle the chunk events for a large guild too quickly using the event dispatcher's wait_for method leads to the event manager trying to handle the wait_for listener's generated future at an invalid state leading to a traceback being raised to logging and some events being missed.
Expected Result
the following to be printed
Actual Result
is printed along with the following traceback making it's way to logging for each missed chunk event.
System info
hikari v2.0.0 HEAD
located at C:\Users\Snab\PycharmProjects\hikari\hikari
CPython 3.8.2 MSC v.1916 64 bit (AMD64)
Further info
Checklist
credentials, personal details, etc).
If it is a follow up of another issue, I have specified it.
The text was updated successfully, but these errors were encountered: