uvloop icon indicating copy to clipboard operation
uvloop copied to clipboard

Loop.call_later(0) and Loop.call_later(-1) return a uvloop.loop.Handle and not an asyncio.TimerHandle

Open graingert opened this issue 2 years ago • 2 comments

  • uvloop version: master
  • Python version: 3.10
  • Platform: ubuntu
  • Can you reproduce the bug with PYTHONASYNCIODEBUG in env?: yes
  • Does uvloop behave differently from vanilla asyncio? How?: yes
Python 3.10.5 (main, Jun 11 2022, 16:53:24) [GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import asyncio
>>> import uvloop
>>> uvloop.new_event_loop().call_later(-1, lambda: None)
<Handle <lambda>>
>>> asyncio.new_event_loop().call_later(-1, lambda: None)
<TimerHandle when=79332.145640902 <lambda>() at <stdin>:1>
>>> 

the bug is here: https://github.com/MagicStack/uvloop/blob/5926386f81ea49903db9d5095058787ed91cff3d/uvloop/loop.pyx#L1327-L1328

graingert avatar Jul 20 '22 14:07 graingert

@fantix I think the issue here is that uvloop should not use uv_idle_callback for call_soon,

Instead uvloop should schedule a timer for running with uv_timer_init set at -1 called "call_soon_handle"

for the loop.call_soon calls, whenever there are items added to the self._ready deque uvloop should start the "call_soon_handle", when the timer fires (note it will always fire first because it is the only timer scheduled at -1) uvloop should then call all the recently added handles on the ready queue. If there are handles added to the ready queue at this point restart the call_soon_handle again.

for loop.call_later calls scheduled in the past uvloop should maintain an internal "self._call_later_expired". whenever call_soon_handle is called after calling all of the "_ready" callbacks uvloop should clear the original self._call_later_expired heap and then call all the handles in a copy of the call_later_expired heap

for call_later in the future the behavior can remain the same

graingert avatar Jul 29 '22 11:07 graingert

import asyncio
import uvloop
from functools import partial

async def main():
    loop = asyncio.get_running_loop()
    loop.call_later(-0.5, partial(print, "4", end=""))
    loop.call_later(-1, partial(print, "3", end=""))
    loop.call_soon(partial(print, "1", end=""))
    await asyncio.sleep(0)
    print("2", end="")


print("asyncio")
asyncio.run(main())
print()
uvloop.install()
print("uvloop")
asyncio.run(main())
print()


# asyncio
# 1234
# uvloop
# 4312

graingert avatar Jul 29 '22 11:07 graingert