Skip to content

Commit 0c5b5af

Browse files
authored
Fix Python 3.13 GIL and event loop compatibility
- Add gil_acquire()/gil_release() helpers that check PyGILState_Check() before calling PyGILState_Ensure() to avoid double-acquisition on Python 3.12+ - Fix event loop thread isolation: ErlangEventLoop for main thread only, SelectorEventLoop for worker threads - Worker threads in py_callback.c now create SelectorEventLoop directly, bypassing the policy to avoid ErlangEventLoop conflicts - Use per-call ErlNifEnv for timer scheduling to prevent races in free-threaded Python mode - Fail fast on RuntimeError in erlang_loop.py instead of silently swallowing initialization errors that cause hangs Fixes py_async_e2e_SUITE timeouts on Python 3.12+ by ensuring proper thread isolation for asyncio event loops.
1 parent 952ed3b commit 0c5b5af

4 files changed

Lines changed: 297 additions & 63 deletions

File tree

c_src/py_callback.c

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1654,8 +1654,21 @@ static void *async_event_loop_thread(void *arg) {
16541654
return NULL;
16551655
}
16561656

1657-
/* Create new event loop */
1658-
PyObject *loop = PyObject_CallMethod(asyncio, "new_event_loop", NULL);
1657+
/* Create a default selector event loop directly, bypassing the policy.
1658+
* Worker threads should NOT use ErlangEventLoop since it requires the
1659+
* main thread's event router. Using SelectorEventLoop ensures these
1660+
* background threads have their own independent event loops. */
1661+
PyObject *selector_loop_class = PyObject_GetAttrString(asyncio, "SelectorEventLoop");
1662+
PyObject *loop = NULL;
1663+
if (selector_loop_class != NULL) {
1664+
loop = PyObject_CallObject(selector_loop_class, NULL);
1665+
Py_DECREF(selector_loop_class);
1666+
}
1667+
if (loop == NULL) {
1668+
/* Fallback to new_event_loop if SelectorEventLoop not available */
1669+
PyErr_Clear();
1670+
loop = PyObject_CallMethod(asyncio, "new_event_loop", NULL);
1671+
}
16591672
if (loop == NULL) {
16601673
PyErr_Print();
16611674
Py_DECREF(asyncio);
@@ -1664,7 +1677,7 @@ static void *async_event_loop_thread(void *arg) {
16641677
return NULL;
16651678
}
16661679

1667-
/* Set as current loop */
1680+
/* Set as current loop for this thread */
16681681
PyObject *set_result = PyObject_CallMethod(asyncio, "set_event_loop", "O", loop);
16691682
Py_XDECREF(set_result);
16701683

0 commit comments

Comments
 (0)