11"""Helper for event loop management. Allows consistently running async generate requests in sync code."""
22
33import asyncio
4+ import os
45import threading
56from collections .abc import Coroutine
67from typing import Any , TypeVar
@@ -18,13 +19,21 @@ def __init__(self):
1819
1920 Do not instantiate this class. Rely on the exported `_run_async_in_thread` function.
2021 """
22+ self ._pid = os .getpid () # Store the pid incase users fork this process.
2123 self ._event_loop = asyncio .new_event_loop ()
2224 self ._thread : threading .Thread = threading .Thread ( # type: ignore[annotation-unchecked]
2325 target = self ._event_loop .run_forever ,
2426 daemon = True , # type: ignore
2527 )
2628 self ._thread .start ()
2729
30+ def _reinit_if_forked (self ) -> None :
31+ """Reinitialize the event loop and thread if we're in a forked child to prevent hanging on awaited tasks."""
32+ if os .getpid () != self ._pid :
33+ # If the process has been forked, reset the event loop and thread.
34+ # Don't cleanup the parent's objects.
35+ self .__init__ ()
36+
2837 def __del__ (self ):
2938 """Delete the event loop handler."""
3039 self ._close_event_loop ()
@@ -55,6 +64,7 @@ async def finalize_tasks():
5564
5665 def __call__ (self , co : Coroutine [Any , Any , R ]) -> R :
5766 """Runs the coroutine in the event loop."""
67+ self ._reinit_if_forked ()
5868 if self ._event_loop == get_current_event_loop ():
5969 # If this gets called from the same event loop, launch in a separate thread to prevent blocking.
6070 return _EventLoopHandler ()(co )
0 commit comments