Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 33.9k
Description
Bug report
Bug description:
Hello everyone,
I am working on an application embedding multiple Python subinterpreters - for which the Python version should be upgraded from Python 3.10 to Python 3.12.1. For the time being, the "legacy version" of subinterpreters (i.e., using a global, shared GIL) should be used, since all Python extensions (including _tkinter and all extensions with single-phase initialization) should be supported.
If I understand the docs correctly, using the legacy Py_NewInterpreter() method should preserve the existing behavior. Still, the following application crashes at shutdown:
#include<Python.h>classPythonInterpreter{public:PythonInterpreter(PyThreadState* parent, int id) : mParent(parent) , mId(id){PyEval_RestoreThread(mParent); mThread = Py_NewInterpreter(); PyObject* globals = PyModule_GetDict(PyImport_AddModule("__main__")); PyRun_String("import _tkinter", Py_single_input, globals, globals); PyEval_SaveThread(); std::cout << "Subinterpreter " << id << " done" << std::endl} virtual~PythonInterpreter(){std::cout << "destructor " << mId << std::endl; PyThreadState_Swap(mThread); Py_EndInterpreter(mThread)} private: PyThreadState* mParent; PyThreadState* mThread; intmId}; intmain(int/* argc */, char** /* argv[] */){PyConfig config; PyConfig_InitPythonConfig(&config); PyStatus status = Py_InitializeFromConfig(&config); if (PyStatus_Exception(status)){PyConfig_Clear(&config); Py_ExitStatusException(status)} else{PyConfig_Clear(&config)} PyThreadState* s0 = PyThreadState_Get(); PyEval_SaveThread(); PythonInterpreter* i0 = newPythonInterpreter(s0, 0); PythonInterpreter* i1 = newPythonInterpreter(s0, 1); delete i0; delete i1; PyEval_RestoreThread(s0); Py_Finalize()}When compiling and running this program with a debug build of Python 3.12.1 (or later) on Linux, I get this output:
Subinterpreter 0 done Subinterpreter 1 done destructor 0 destructor 1 main: Objects/dictobject.c:283: unicode_get_hash: Assertion `Py_IS_TYPE(((PyObject*)(((o)))), (&PyUnicode_Type))' failed. With a non-debug build, the program exits with a segmentation fault.
The gdb backtrace looks as follows:
#0 0x00007ffff6311387 in raise () from /lib64/libc.so.6 #1 0x00007ffff6312a78 in abort () from /lib64/libc.so.6 #2 0x00007ffff630a1a6 in __assert_fail_base () from /lib64/libc.so.6 #3 0x00007ffff630a252 in __assert_fail () from /lib64/libc.so.6 #4 0x00007ffff6a9baf5 in unicode_get_hash (o=<optimized out>) at Objects/dictobject.c:2143 #5 _PyDict_Next (op=op@entry=0x7fffecfd2270, ppos=ppos@entry=0x7fffffffd2a8, pkey=pkey@entry=0x7fffffffd2a0, pvalue=pvalue@entry=0x7fffffffd298, phash=phash@entry=0x0) at Objects/dictobject.c:2142 #6 0x00007ffff6a9c0d8 in PyDict_Next (op=op@entry=0x7fffecfd2270, ppos=ppos@entry=0x7fffffffd2a8, pkey=pkey@entry=0x7fffffffd2a0, pvalue=pvalue@entry=0x7fffffffd298) at Objects/dictobject.c:2189 #7 0x00007ffff6ab3751 in _PyModule_ClearDict (d=0x7fffecfd2270) at Objects/moduleobject.c:624 #8 0x00007ffff6ab40dd in _PyModule_Clear (m=m@entry=0x7fffed02ca70) at Objects/moduleobject.c:604 #9 0x00007ffff6c11bd4 in finalize_modules_clear_weaklist (interp=interp@entry=0x7fffed03c020, weaklist=weaklist@entry=0x7fffef912da0, verbose=verbose@entry=0) at Python/pylifecycle.c:1526 #10 0x00007ffff6c125ef in finalize_modules (tstate=tstate@entry=0x7fffed099950) at Python/pylifecycle.c:1609 #11 0x00007ffff6c202da in Py_EndInterpreter (tstate=0x7fffed099950) at Python/pylifecycle.c:2220 #12 0x00000000004014b5 in PythonInterpreter::~PythonInterpreter (this=0x5092f0, __in_chrg=<optimized out>) at main.cc:25 #13 PythonInterpreter::~PythonInterpreter (this=0x5092f0, __in_chrg=<optimized out>) at main.cc:26 #14 0x00000000004012e2 in main () at main.cc:60 Digging further into the backtrace, it looks like the Python garbage collector is trying to decrease the reference counter to the _tkinter module twice, despite it having been increased only once. Oddly enough, the program runs just fine when destroying the interpreters in reverse order:
PythonInterpreter* i0 = new PythonInterpreter(s0, 0); PythonInterpreter* i1 = new PythonInterpreter(s0, 1); delete i1; delete i0;Can anyone help me shed some light into this issue? Is there anything I am overlooking?
CPython versions tested on:
3.12.1, 3.12.2, 3.13.0a4
Operating systems tested on:
Linux, Windows
### Tasks Linked PRs
Metadata
Metadata
Assignees
Labels
Projects
Status