Uploaded image for project: 'Qt for Python'
  1. Qt for Python
  2. PYSIDE-308

Py_Finalize() crashes if there is PySide.QtCore imported

    XMLWordPrintable

Details

    • Bug
    • Resolution: Invalid
    • P2: Important
    • 6.0
    • 1.2.1, 1.2.2
    • PySide
    • Windows 7 x64
      Python 2.7.6
      PySide 1.2.1
      Microsoft Visual Studtio 2008

    Description

      I have to do Py_Initialize() and Py_Finalize() several times in my C++ application during process execution.

      But the 2nd call to Py_Finalize() causes a crash if there is the PySide.QtCore module imported.
      The shortest example:

      #include <iostream>
      #include <Python.h>
      
      using namespace std;
      
      void test()
      {
         Py_Initialize();
         PyObject * module = PyImport_ImportModule("PySide.QtCore");
         cout << module << endl;  // module is not a null pointer
         Py_Finalize();
      }
      
      int main(int, char**)
      {
         test();
         test();  // << crash
         test();
         test();
         test();
         test();
         test();
         test();
         return 0;
      }
      
      Callstack

      > python27.dll!visit_decref(_object * op=0x00000000026d39f0, void * data=0x0000000000000000) Line 360 + 0x3d bytes C
      python27.dll!dict_traverse(_object * op=0x00000000027331a8, int (_object , void *) visit=0x000000001e06baf0, void * arg=0x0000000000000000) Line 2114 + 0x16 bytes C
      python27.dll!subtract_refs(_gc_head * containers=0x000000001e3894c0) Line 388 C
      python27.dll!collect(int generation=2) Line 933 C
      python27.dll!PyGC_Collect() Line 1440 + 0xa bytes C
      python27.dll!Py_Finalize() Line 459 C

      The actual crash occurs on gcmodule.c:360

          if (PyObject_IS_GC(op)) {
      

      The op's type is likely to be inconsistent, as op->ob_type->tp_is_gc equals to 0xfbfbfbfbfb007075

      I've tried the same for PyQt4.QtCore and it does not crash.

      UPD:
      Got a similar error on Debian Linux (gcc (Debian 5.3.1-12), Python 2.7.6, PySide 1.2.1, Py_Finalize() crashes on the 4th call to it but there is a bit different callstack:

      Callstack

      type_dealloc() at typeobject.c:2 643 0x7ffff7a8a3c4
      dict_dealloc() at dictobject.c:1 010 0x7ffff7a6a14f
      dict_dealloc() at dictobject.c:1 010 0x7ffff7a6a14f
      _PyImport_Fini() at import.c:272 0x7ffff7af332a
      Py_Finalize() at pythonrun.c:481 0x7ffff7b07ff5
      test() at main.cpp:12 0x400a5e
      main() at main.cpp:20 0x400a79

      The actual crash occurs on typeobject.c:2643

          _PyObject_GC_UNTRACK(type);
      

      Looks familiar, as there something went wrong in the garbage collector code.

      UPD2:
      Having reduced the initialization code of PySide.QtCore, I found that PySide::init(module); causes the issue. If I comment it out, it will work.
      Went deeper, found that if I comment out MetaFunction::init(module); inside the PySide::init implementation this also will work fine. Found there in contrast to the other init function implementation (ClassInfo::init, Signal::init, Slot::init, Property::init), it DOES NOT incref a counter of its type (PySideMetaFunctionType).
      I have fixed the issue on Linux. There is an error inside MetaFunction::init

      pysidemetafunction.cpp
      void init(PyObject* module)
      {
          if (PyType_Ready(&PySideMetaFunctionType) < 0)
              return;
      
          Py_INCREF(&PySideMetaFunctionType);  // <<< I added this line and it does not crash anymore.
          PyModule_AddObject(module, "MetaFunction", ((PyObject*)&PySideMetaFunctionType));
      }
      

      UPD3:
      Unfortunately, that fix didn't help to fix the crash on Windows.
      I used the same approach to locate the problematic code on Windows. What I've found is that any call to Shiboken::Enum::createGlobalEnumItem inside the PySide.QtCore initialization function causes the following crash.
      I'm not sure whether it's a proper fix (may lead to memory leaks) but:

      sbkenum.cpp
      static PyObject* createEnumItem(PyTypeObject* enumType, const char* itemName, long itemValue)
      {
          PyObject* enumItem = newItem(enumType, itemValue, itemName);
          if (PyDict_SetItemString(enumType->tp_dict, itemName, enumItem) < 0)
              return 0;
          Py_DECREF(enumItem);
          return enumItem;
      }
      
      bool createGlobalEnumItem(PyTypeObject* enumType, PyObject* module, const char* itemName, long itemValue)
      {
          PyObject* enumItem = createEnumItem(enumType, itemName, itemValue);
          if (enumItem) {
              if (PyModule_AddObject(module, itemName, enumItem) < 0)
                  return false;
              Py_DECREF(enumItem);
              return true;
          }
          return false;
      }
      

      There are two possible calls to Py_DECREF, that it calls decref twice for a single object. So that, I commented Py_DECREF out in the createGlobalEnumItem function, now it doesn't crash.

      UPD4:
      Phew!
      Another one issue that leads to an assertion failure on typeobject.c:2642

      typeobject.c
      assert(type->tp_flags & Py_TPFLAGS_HEAPTYPE);
      

      Of course, type there is the PySideMetaFunctionType instance.

      Comparing to the similar types (such as the PySideSlotType), discovered that the tp_base of the PySideMetaFunctionType is NULL. PySideSlotType->tp_base is &PyType_Type.
      Did the same for the PySideMetaFunctionType, now it's OK. I hope so. The code is so erratic!

      Attachments

        Issue Links

          Activity

            People

              crmaurei Cristian Maureira-Fredes
              qbatman qbatman
              Votes:
              3 Vote for this issue
              Watchers:
              6 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved: