Skip to content

Commit

Permalink
Decompose run_forever() into parts to allow external usage.
Browse files Browse the repository at this point in the history
  • Loading branch information
freakboy3742 committed Oct 12, 2023
1 parent 269005e commit a0c1fde
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 30 deletions.
54 changes: 42 additions & 12 deletions Lib/asyncio/base_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -601,29 +601,59 @@ def _check_running(self):
raise RuntimeError(
'Cannot run the event loop while another loop is running')

def run_forever(self):
"""Run until stop() is called."""
def run_forever_setup(self):
"""Set up an event loop so that it is ready to start actively looping
to process events.
Returns the state that must be restored when the loop concludes. This state
should be passed in as arguments to ``run_forever_cleanup()``.
This method is only needed if you are writing your own event loop, with
customized ``run_forever`` semantics (e.g., integrating a GUI event loop
with Python's event loop).
"""
self._check_closed()
self._check_running()
self._set_coroutine_origin_tracking(self._debug)

old_agen_hooks = sys.get_asyncgen_hooks()
try:
self._thread_id = threading.get_ident()
sys.set_asyncgen_hooks(firstiter=self._asyncgen_firstiter_hook,
finalizer=self._asyncgen_finalizer_hook)
self._thread_id = threading.get_ident()
sys.set_asyncgen_hooks(
firstiter=self._asyncgen_firstiter_hook,
finalizer=self._asyncgen_finalizer_hook
)

events._set_running_loop(self)

return (old_agen_hooks,)

events._set_running_loop(self)
def run_forever_cleanup(self, orig_state):
"""Clean up an event loop after the event loop finishes the looping over
events.
Restores any state preserved by ``run_forever_setup()``.
This method is only needed if you are writing your own event loop, with
customized ``run_forever`` semantics (e.g., integrating a GUI event loop
with Python's event loop).
"""
old_agen_hooks, = orig_state
self._stopping = False
self._thread_id = None
events._set_running_loop(None)
self._set_coroutine_origin_tracking(False)
sys.set_asyncgen_hooks(*old_agen_hooks)

def run_forever(self):
"""Run until stop() is called."""
try:
orig_state = self.run_forever_setup()
while True:
self._run_once()
if self._stopping:
break
finally:
self._stopping = False
self._thread_id = None
events._set_running_loop(None)
self._set_coroutine_origin_tracking(False)
sys.set_asyncgen_hooks(*old_agen_hooks)
self.run_forever_cleanup(orig_state)

def run_until_complete(self, future):
"""Run until the Future is done.
Expand Down
37 changes: 19 additions & 18 deletions Lib/asyncio/windows_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -314,24 +314,25 @@ def __init__(self, proactor=None):
proactor = IocpProactor()
super().__init__(proactor)

def run_forever(self):
try:
assert self._self_reading_future is None
self.call_soon(self._loop_self_reading)
super().run_forever()
finally:
if self._self_reading_future is not None:
ov = self._self_reading_future._ov
self._self_reading_future.cancel()
# self_reading_future was just cancelled so if it hasn't been
# finished yet, it never will be (it's possible that it has
# already finished and its callback is waiting in the queue,
# where it could still happen if the event loop is restarted).
# Unregister it otherwise IocpProactor.close will wait for it
# forever
if ov is not None:
self._proactor._unregister(ov)
self._self_reading_future = None
def run_forever_setup(self):
assert self._self_reading_future is None
self.call_soon(self._loop_self_reading)

return super().run_forever_setup()

def run_forever_cleanup(self, orig_state):
if self._self_reading_future is not None:
ov = self._self_reading_future._ov
self._self_reading_future.cancel()
# self_reading_future was just cancelled so if it hasn't been
# finished yet, it never will be (it's possible that it has
# already finished and its callback is waiting in the queue,
# where it could still happen if the event loop is restarted).
# Unregister it otherwise IocpProactor.close will wait for it
# forever
if ov is not None:
self._proactor._unregister(ov)
self._self_reading_future = None

async def create_pipe_connection(self, protocol_factory, address):
f = self._proactor.connect_pipe(address)
Expand Down

0 comments on commit a0c1fde

Please sign in to comment.