Внедренные Python Segfaults - PullRequest
       5

Внедренные Python Segfaults

3 голосов
/ 17 января 2012

В моем многопоточном приложении происходит сбой при вызове PyImport_ImportModule("my_module").

БТ будет размещен внизу.

Некоторый фон:

  1. Мое приложение создает несколько экземпляров многих производных классов C ++ и запускает функцию Run() базового класса, которая использует виртуальный метод для определения того, что делать.
  2. Один производный класс использует класс Python, Grasp_Behavior (класс) в grasp_behavior (модуль)
  3. После подробного прочтения я использовал Python API для достижения (2) (выдержки ниже)
  4. Я генерирую 2 экземпляра указанного класса и запускаю их "параллельно" (python interpr на самом деле не работает параллельно)
  5. Я пытаюсь сгенерировать другой экземпляр указанного класса, segfault на PyImport_ImportModule

Я думаю, что, возможно, я не могу дважды импортировать модуль в одном и том же интерпретаторе. Но я не могу понять, как это проверить. Я предполагаю, что мне нужно посмотреть, есть ли grasp_behavior в словаре, но я не знаю, какой именно, возможно, я получаю словарь модуля __main__?

Но я могу ошибаться, любой совет был бы невероятно полезен!

В конструкторе:

//Check if Python is Initialized, otherwise initialize it
if(!Py_IsInitialized())
{
    std::cout << "[GraspBehavior] InitPython: Initializing the Python Interpreter" << std::endl;
    Py_Initialize();
    PyEval_InitThreads(); //Initialize Python thread ability
    PyEval_ReleaseLock(); //Release the implicit lock on the Python GIL
}

// --- Handle Imports ----

PyObject * pModule = PyImport_ImportModule("grasp_behavior");
if(pModule == NULL)
{
    std::cout << "[GraspBehavior] InitPython: Unable to import grasp_behavior module: ";
    PyErr_Print();
}
 // --- Get our Class Pointer From the Module ...
PyObject * pClass = PyObject_GetAttrString(pModule, "Grasp_Behavior");
if(pClass == NULL)
{
    std::cout << "[GraspBehavior] InitPython: Unable to get Class from Module: ";
    PyErr_Print();
}
Py_DECREF(pModule); //clean up, this is a new reference

behavior_instance_ = PyObject_Call(pClass, pArguments_Tuple, pArguments_Dict);
if(behavior_instance_ == NULL)
{
    std::cout << "[GraspBehavior] InitPython: Couldn't generate instance: ";
    PyErr_Print();
}
Py_DECREF(pArguments_Tuple);
Py_DECREF(pArguments_Dict);
Py_DECREF(pClass);

Здесь обратите внимание, что я инициализирую интерпретатор Python, только если он не был инициализирован. Я предполагаю, что он инициализируется для всего процесса.

В методе Run() (запускается из потока поддержки):

std::cout << "[GraspBehavior] PerformBehavior: Acquiring Python GIL Lock ..." << std::endl;
PyGILState_STATE py_gilstate;
py_gilstate = PyGILState_Ensure();

/* ---- Perform Behavior Below ----- */

std::vector<std::pair<double, double> > desired_body_offsets;
//desired_body_offsets.push_back( std::pair<double, double>(0.6, 0));
PyObject * base_positions = GetTrialBasePositions(my_env_, desired_body_offsets);

PyObject * grasps = EvaluateBasePositions(my_env_, base_positions);

//Did we get any grasps? What do we do with them? [TODO]
if(grasps != NULL)
{
    std::cout << grasps->ob_type->tp_name << std::endl;
    std::cout << "Number of grasps: " << PyList_Size(grasps) << std::endl;
    successful_ = true;
}

/* --------------------------------- */

std::cout << "[GraspBehavior] PerformBehavior: Releasing Python GIL Lock ..." << std::endl;
PyGILState_Release(py_gilstate);

Здесь я пошел с PyGILState замком. Некоторое время я читал, и некоторые статьи, на которые ссылаются многие, используют более старый стиль блокировки в Python ... возможно, мне придется изменить это.


Backtrace:

Program received signal SIGSEGV, Segmentation fault.
0x00007fffee9c4330 in ?? () from /usr/lib/libpython2.6.so.1.0
(gdb) bt
#0  0x00007fffee9c4330 in ?? () from /usr/lib/libpython2.6.so.1.0
#1  0x00007fffee99ff09 in PyEval_GetGlobals ()
   from /usr/lib/libpython2.6.so.1.0
#2  0x00007fffee9bd993 in PyImport_Import () from /usr/lib/libpython2.6.so.1.0
#3  0x00007fffee9bdbec in PyImport_ImportModule ()
   from /usr/lib/libpython2.6.so.1.0
#4  0x000000000042d6f0 in GraspBehavior::InitPython (this=0x7948690)
    at grasp_behavior.cpp:241

Ответы [ 2 ]

4 голосов
/ 17 января 2012

Прежде всего, вы не должны вызывать какие-либо функции Python API при освобождении GIL (за исключением вызовов получения GIL).

В этом коде произойдет сбой:

PyEval_ReleaseLock();

PyObject * pModule = PyImport_ImportModule("grasp_behavior");

Отпустите GIL после того, как вы закончите настройку, а затем повторно приобретите при необходимости (в вашем Run()).

Кроме того, PyEval_ReleaseLock устарело, вместо этого следует использовать PyEval_SaveThread.

PyThreadState* tstate = PyEval_SaveThread();

Это сохранит состояние потока и освободит GIL.

Затем, прямо перед тем, как вы начнете финализировать интерпретатор, сделайте следующее:

PyEval_RestoreThread(tstate);

передача возвращаемого значения вызова PyEval_SaveThread.

В Run() вы должны использовать PyGILState_Ensure и PyGILState_Release, как и сейчас, но вам следует подумать об исключениях из C ++. Прямо сейчас PyGILState_Release не будет вызвано, если Run() бросит.

Хорошим свойством вызовов PyGILState является то, что вы можете использовать их независимо от того, приобретен ли GIL или нет, и они будут поступать правильно, в отличие от старых API.

Кроме того, вы должны инициализировать интерпретатор один раз при запуске в главном потоке (до запуска других потоков) и завершать работу после закрытия всех потоков, кроме основного.

1 голос
/ 17 января 2012

Предоставляет ли Boost моральный эквивалент функции pthread_once(), которая позволяет выполнить некоторую задачу инициализации ровно один раз, независимо от того, сколько потоков пытается запустить ее одновременно?Если бы это была моя проблема при отладке, я бы попытался защитить PyImport_ImportModule от нескольких вызовов в нескольких потоках, и использование стандартного инструмента для этого было бы моей первой попыткой.

...