Skip to content

Commit 6674dda

Browse files
authored
fix(runtime): guard sync API inside async contexts (#1085) (#1224)
* fix(runtime): guard sync API when called from async context (closes #1085) * fix(runtime): guard sync API when called from async context (closes #1085) * fix(runtime): guard sync API when called from async context (closes #1085)
1 parent 5d779e7 commit 6674dda

File tree

1 file changed

+34
-6
lines changed

1 file changed

+34
-6
lines changed

python/cocoindex/runtime.py

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@
66
import threading
77
import asyncio
88
import inspect
9-
from typing import Any, Callable, Coroutine, TypeVar, Awaitable
9+
import warnings
1010

11+
from typing import Any, Callable, Awaitable, TypeVar, Coroutine
1112

1213
T = TypeVar("T")
1314

@@ -24,15 +25,42 @@ def event_loop(self) -> asyncio.AbstractEventLoop:
2425
"""Get the event loop for the cocoindex library."""
2526
with self._lock:
2627
if self._event_loop is None:
27-
self._event_loop = asyncio.new_event_loop()
28-
threading.Thread(
29-
target=self._event_loop.run_forever, daemon=True
30-
).start()
28+
loop = asyncio.new_event_loop()
29+
self._event_loop = loop
30+
31+
def _runner(l: asyncio.AbstractEventLoop) -> None:
32+
asyncio.set_event_loop(l)
33+
l.run_forever()
34+
35+
threading.Thread(target=_runner, args=(loop,), daemon=True).start()
3136
return self._event_loop
3237

3338
def run(self, coro: Coroutine[Any, Any, T]) -> T:
3439
"""Run a coroutine in the event loop, blocking until it finishes. Return its result."""
35-
return asyncio.run_coroutine_threadsafe(coro, self.event_loop).result()
40+
try:
41+
running_loop = asyncio.get_running_loop()
42+
except RuntimeError:
43+
running_loop = None
44+
45+
loop = self.event_loop
46+
47+
if running_loop is not None:
48+
if running_loop is loop:
49+
raise RuntimeError(
50+
"CocoIndex sync API was called from inside CocoIndex's async context. "
51+
"Use the async variant of this method instead."
52+
)
53+
warnings.warn(
54+
"CocoIndex sync API was called inside an existing event loop. "
55+
"This may block other tasks. Prefer the async method.",
56+
RuntimeWarning,
57+
stacklevel=2,
58+
)
59+
fut = asyncio.run_coroutine_threadsafe(coro, loop)
60+
return fut.result()
61+
62+
fut = asyncio.run_coroutine_threadsafe(coro, loop)
63+
return fut.result()
3664

3765

3866
execution_context = _ExecutionContext()

0 commit comments

Comments
 (0)