@@ -57,47 +57,52 @@ def __init__(self) -> None:
5757 self ._startup_error : BaseException | None = None
5858 self ._close_requested = False
5959 self ._lock = threading .Lock ()
60- self ._operation_lock = threading .Lock ()
6160
6261 def run (self , awaitable : Awaitable [Any ]) -> Any :
63- with self ._operation_lock :
62+ if threading .current_thread () is self ._thread :
63+ raise RuntimeError ("cannot synchronously run from the runner thread" )
64+
65+ while True :
6466 loop = self ._ensure_loop ()
65- future = asyncio .run_coroutine_threadsafe (
66- self ._await_result (awaitable ), loop
67- )
68- return future .result ()
67+ with self ._lock :
68+ if loop is self ._loop and not self ._close_requested :
69+ future = asyncio .run_coroutine_threadsafe (
70+ self ._await_result (awaitable ), loop
71+ )
72+ break
73+ return future .result ()
6974
7075 def close (self ) -> None :
7176 current = threading .current_thread ()
7277 with self ._lock :
73- if current is self ._thread and self ._loop is not None :
74- self ._loop .call_soon (self ._loop .stop )
78+ loop = self ._loop
79+ thread = self ._thread
80+ if thread is None :
7581 return
76-
77- with self ._operation_lock :
78- with self ._lock :
79- loop = self ._loop
80- thread = self ._thread
81- if thread is None :
82- return
83- if loop is None :
84- self ._close_requested = True
85- else :
86- self ._loop = None
87- self ._thread = None
88- self ._closing_threads .add (thread )
89-
82+ self ._close_requested = True
9083 if loop is None :
84+ self ._startup_error = RuntimeError ("runner closed during startup" )
85+ else :
86+ self ._loop = None
87+ self ._thread = None
88+ self ._closing_threads .add (thread )
89+
90+ if loop is None :
91+ if thread is not current :
9192 thread .join ()
92- return
93+ return
9394
94- if loop .is_closed ():
95- with self ._lock :
96- self ._closing_threads .discard (thread )
97- return
95+ if loop .is_closed ():
96+ with self ._lock :
97+ self ._closing_threads .discard (thread )
98+ return
99+
100+ if thread is current :
101+ loop .call_soon (loop .stop )
102+ return
98103
99- loop .call_soon_threadsafe (loop .stop )
100- thread .join ()
104+ loop .call_soon_threadsafe (loop .stop )
105+ thread .join ()
101106
102107 def owns_thread (self , thread : threading .Thread ) -> bool :
103108 with self ._lock :
@@ -152,6 +157,8 @@ def _run_loop(self) -> None:
152157 with self ._lock :
153158 self ._loop = loop
154159 close_requested = self ._close_requested
160+ if close_requested and self ._startup_error is None :
161+ self ._startup_error = RuntimeError ("runner closed during startup" )
155162 self ._started .set ()
156163
157164 if close_requested :
0 commit comments