Repository navigation
Description
Issue type
Bug
How did you install PythonMonkey?
Installed from pip
OS platform and distribution
All tested: Windows 11 x64, Ubuntu 24.04 x64 and arm64, macOS arm64 and x64 (GitHub Actions runners)
Python version (python --version)
3.14.3 (crashes); 3.14.10, 3.13, 3.12 and 3.15 work
PythonMonkey version (pip show pythonmonkey)
1.3.1 and 1.4.1 (both crash on 3.14.3)
Bug Description
On Python 3.14.0–3.14.3, awaiting any JS promise crashes the interpreter: a segfault (signal 11) on Linux/macOS, an access violation (0xC0000005) on Windows. Synchronous calls (pm.eval('1 + 1'), calling a JS function) work. On Python 3.14.4+ everything works.
Cause. PyEventLoop::_getLoopOnThread (src/PyEventLoop.cc) gets the running loop by casting the thread state to a local copy of CPython's private _PyThreadStateImpl and reading asyncio_running_loop at a fixed offset:
using PyThreadStateHolder = struct { // _PyThreadStateImpl
PyThreadState base;
Py_ssize_t refcount;
uintptr_t c_stack_top;
uintptr_t c_stack_soft_limit;
uintptr_t c_stack_hard_limit;
PyObject *asyncio_running_loop;
};Python 3.14.4 added a pointer field to the public PyThreadState (_PyStackChunk *datastack_cached_chunk;, python/cpython@19cbcc0f85, gh-142183). The wheels appear to be built against 3.14.4+ headers, so sizeof(PyThreadState) is 8 bytes larger than on 3.14.0–3.14.3. On those versions the read lands on the next field, asyncio_running_task. PythonMonkey then treats the running Task as the event loop. In the setTimeout case this surfaces as:
AttributeError: '_asyncio.Task' object has no attribute 'call_later'
before the process crashes. In the other cases it crashes outright (Py_INCREF and method calls on the wrong object).
The same mismatch would happen the other way round: a wheel built against 3.14.0–3.14.3 headers would misread on 3.14.4+. Since CPython changes these structs in patch releases, a compiled-in offset can break on any 3.x.y update.
Standalone code to reproduce the issue
import asyncio, pythonmonkey as pm
async def main():
return await pm.eval('Promise.resolve(1)')
print(asyncio.run(main())) # Python 3.14.3: segfault / access violation. 3.14.4+: prints 1.0Relevant log output or backtrace
Windows 11, Python 3.14.3 (MSC v.1944 64 bit), pythonmonkey 1.3.1:
eval_number ok 2.0
call_js_function ok 2.0
await_resolved CRASH 0xc0000005 Windows fatal exception: access violation
await_async_fn CRASH 0xc0000005
await_setTimeout CRASH 0xc0000005 AttributeError: '_asyncio.Task' object has no attribute 'call_later'
Ubuntu 24.04 x64, Python 3.14.3, pythonmonkey 1.4.1:
await_resolved CRASH signal 11 (Fatal Python error: Segmentation fault)
Same runner, Python 3.14.10, pythonmonkey 1.4.1:
await_resolved ok 1.0Additional info if applicable
Matrix run, 45 jobs: 5 OS/CPU x Python 3.12 / 3.13 / 3.14.3 / 3.14.10 / 3.15 x pythonmonkey 1.3.1 / 1.4.1.
All 10 Python 3.14.3 jobs crash; all 35 others pass.
https://github.com/dandydarshan/CodeWiki/actions/runs/42440036273
Probe scripts: https://github.com/dandydarshan/CodeWiki/tree/pythonmonkey-matrix/scripts (pm_narrow.py, pythonmonkey_repro.py)
Possible fixes, roughly from most to least robust:
- For the current thread, call asyncio's own _get_running_loop() (or the C equivalent) instead of reading the struct.
- For the main thread (getMainLoop, used from helper threads), remember the loop once it is found from the main thread instead of reading another thread's state.
- At minimum, at import time compare the runtime Py_Version with the PY_VERSION_HEX the wheel was built with, and refuse the struct read (raise) when the micro version crosses a known layout change, so users get an error instead of a crash.
Related reports that may share this cause or a similar one: #476, #271.What branch of PythonMonkey were you developing on? (If applicable)
No response
Metadata
Metadata
Labels
Type
Projects
- StatusShow more project fieldsBacklog
Activity