eventloop: fix timer reentrancy
Avoid running multiple callbacks if a timer is stopped and restarted in quick succession. Signed-off-by: Eric Callahan <arksine.code@gmail.com>
This commit is contained in:
parent
d4316d9878
commit
bf9223225c
|
@ -164,12 +164,18 @@ class FlexTimer:
|
|||
self.eventloop = eventloop
|
||||
self.callback = callback
|
||||
self.timer_handle: Optional[asyncio.TimerHandle] = None
|
||||
self.timer_task: Optional[asyncio.Task] = None
|
||||
self.running: bool = False
|
||||
|
||||
def in_callback(self) -> bool:
|
||||
return self.timer_task is not None and not self.timer_task.done()
|
||||
|
||||
def start(self, delay: float = 0.):
|
||||
if self.running:
|
||||
return
|
||||
self.running = True
|
||||
if self.in_callback():
|
||||
return
|
||||
call_time = self.eventloop.get_loop_time() + delay
|
||||
self.timer_handle = self.eventloop.call_at(
|
||||
call_time, self._schedule_task)
|
||||
|
@ -182,9 +188,14 @@ class FlexTimer:
|
|||
self.timer_handle.cancel()
|
||||
self.timer_handle = None
|
||||
|
||||
async def wait_timer_done(self) -> None:
|
||||
if self.timer_task is None:
|
||||
return
|
||||
await self.timer_task
|
||||
|
||||
def _schedule_task(self):
|
||||
self.timer_handle = None
|
||||
self.eventloop.create_task(self._call_wrapper())
|
||||
self.timer_task = self.eventloop.create_task(self._call_wrapper())
|
||||
|
||||
def is_running(self) -> bool:
|
||||
return self.running
|
||||
|
@ -192,8 +203,14 @@ class FlexTimer:
|
|||
async def _call_wrapper(self):
|
||||
if not self.running:
|
||||
return
|
||||
try:
|
||||
ret = self.callback(self.eventloop.get_loop_time())
|
||||
if isinstance(ret, Awaitable):
|
||||
ret = await ret
|
||||
except Exception:
|
||||
self.running = False
|
||||
raise
|
||||
finally:
|
||||
self.timer_task = None
|
||||
if self.running:
|
||||
self.timer_handle = self.eventloop.call_at(ret, self._schedule_task)
|
||||
|
|
Loading…
Reference in New Issue