static */
| 139 | |
| 140 | /* static */ |
| 141 | PyEventLoop PyEventLoop::_getLoopOnThread(PyThreadState *tstate) { |
| 142 | // Modified from Python 3.9 `get_running_loop` https://github.com/python/cpython/blob/7cb3a44/Modules/_asynciomodule.c#L241-L278 |
| 143 | |
| 144 | PyObject *ts_dict; |
| 145 | #if PY_VERSION_HEX >= 0x030d0000 // Python version is greater than 3.13 |
| 146 | // The private `_PyThreadState_GetDict(tstate)` API gets removed in Python 3.13. |
| 147 | // However, simply replacing it with `PyThreadState_GetDict()` does not work, |
| 148 | // since the public `PyThreadState_GetDict()` API can only get from the current thread. |
| 149 | // We need to somehow get the thread dictionary on the main thread instead of the current thread. |
| 150 | // |
| 151 | // UPDATE: We don't need the thread dictionary anymore. |
| 152 | // To get the thread's running event-loop in Python 3.13 is as simple as `thread_state->asyncio_running_loop` |
| 153 | { |
| 154 | // Every `PyThreadState` is actually allocated with extra fields as a `_PyThreadStateImpl` struct |
| 155 | // See https://github.com/python/cpython/blob/v3.13.0rc1/Include/internal/pycore_tstate.h#L17-L24 |
| 156 | using PyThreadStateHolder = struct { // _PyThreadStateImpl |
| 157 | PyThreadState base; |
| 158 | #if PY_VERSION_HEX >= 0x030e0000 // Python version is greater than 3.14 |
| 159 | // the struct is changed with more additional fields, see https://github.com/python/cpython/blob/v3.14.0rc3/Include/internal/pycore_tstate.h#L24-L40 |
| 160 | Py_ssize_t refcount; |
| 161 | uintptr_t c_stack_top; |
| 162 | uintptr_t c_stack_soft_limit; |
| 163 | uintptr_t c_stack_hard_limit; |
| 164 | #endif |
| 165 | PyObject *asyncio_running_loop; // we only need the first few fields of `_PyThreadStateImpl` |
| 166 | }; |
| 167 | |
| 168 | // Modified from https://github.com/python/cpython/blob/v3.13.0rc1/Modules/_asynciomodule.c#L3205-L3210 |
| 169 | PyObject *loop = ((PyThreadStateHolder *)tstate)->asyncio_running_loop; |
| 170 | if (loop == NULL) { |
| 171 | return _loopNotFound(); |
| 172 | } |
| 173 | |
| 174 | Py_INCREF(loop); |
| 175 | return PyEventLoop(loop); |
| 176 | } |
| 177 | #elif PY_VERSION_HEX >= 0x03090000 // Python version is greater than 3.9 |
| 178 | ts_dict = _PyThreadState_GetDict(tstate); // borrowed reference |
| 179 | #else // Python 3.8 |
| 180 | ts_dict = tstate->dict; // see https://github.com/python/cpython/blob/v3.8.17/Modules/_asynciomodule.c#L244-L245 |
| 181 | #endif |
| 182 | if (ts_dict == NULL) { |
| 183 | return _loopNotFound(); |
| 184 | } |
| 185 | |
| 186 | // TODO: Python `get_running_loop` caches the PyRunningLoopHolder, should we do it as well for the main thread? |
| 187 | // see https://github.com/python/cpython/blob/7cb3a44/Modules/_asynciomodule.c#L234-L239 |
| 188 | PyObject *rl = PyDict_GetItemString(ts_dict, "__asyncio_running_event_loop__"); // borrowed reference |
| 189 | if (rl == NULL) { |
| 190 | return _loopNotFound(); |
| 191 | } |
| 192 | |
| 193 | #if PY_VERSION_HEX < 0x030c0000 // Python version is less than 3.12 |
| 194 | using PyRunningLoopHolder = struct { |
| 195 | PyObject_HEAD |
| 196 | PyObject *rl_loop; |
| 197 | }; |
| 198 |
nothing calls this directly
no test coverage detected