segfault: use of animations causes segfault on exit #175

Closed
opened 2026-01-04 20:29:59 +00:00 by john · 0 comments
Owner

More of an annoyance than anything, as the program is already shutting down. Regardless, there is technically a lifecycle issue: the Python interpreter is made unavailable while some C++ objects still hold references to Python objects which they attempt to refmanage.

Summary of the Bug Fix

Root Cause: When sys.exit() was called, Python's interpreter began shutting down. The AnimationManager singleton was destroyed during the atexit handler phase, but at that point Python was already finalized. When Animation::~Animation() tried to call PyGILState_Ensure() to safely decrement Python callback references, it crashed because the interpreter state was already invalid.

Fix: Added Py_IsInitialized() guards before any Python API calls in Animation::~Animation():

  Animation::~Animation() {
      PyObject* callback = pythonCallback;
      if (callback && Py_IsInitialized()) {  // Guard added
          pythonCallback = nullptr;
          PyGILState_STATE gstate = PyGILState_Ensure();
          Py_DECREF(callback);
          PyGILState_Release(gstate);
      }

      if (serial_number != 0 && Py_IsInitialized()) {  // Guard added
          PythonObjectCache::getInstance().remove(serial_number);
      }
  }

This pattern should be applied to any C++ destructor that handles Python objects when the object might outlive the Python interpreter (e.g., singletons, static objects, or objects destroyed during exit handlers).

More of an annoyance than anything, as the program is already shutting down. Regardless, there is technically a lifecycle issue: the Python interpreter is made unavailable while some C++ objects still hold references to Python objects which they attempt to refmanage. Summary of the Bug Fix Root Cause: When sys.exit() was called, Python's interpreter began shutting down. The AnimationManager singleton was destroyed during the atexit handler phase, but at that point Python was already finalized. When Animation::~Animation() tried to call PyGILState_Ensure() to safely decrement Python callback references, it crashed because the interpreter state was already invalid. Fix: Added Py_IsInitialized() guards before any Python API calls in Animation::~Animation(): ``` Animation::~Animation() { PyObject* callback = pythonCallback; if (callback && Py_IsInitialized()) { // Guard added pythonCallback = nullptr; PyGILState_STATE gstate = PyGILState_Ensure(); Py_DECREF(callback); PyGILState_Release(gstate); } if (serial_number != 0 && Py_IsInitialized()) { // Guard added PythonObjectCache::getInstance().remove(serial_number); } } ``` This pattern should be applied to any C++ destructor that handles Python objects when the object might outlive the Python interpreter (e.g., singletons, static objects, or objects destroyed during exit handlers).
john changed title from segfault: use of animations causes to segfault: use of animations causes segfault on exit 2026-01-04 20:30:24 +00:00
john closed this issue 2026-01-04 20:32:20 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
john/McRogueFace#175
No description provided.