2015-01-29 3 views
1

У меня проблема с boost python, деструктором базового объекта. Когда объект boost :: python :: dict создается и уничтожается в области py_init, тогда все в порядке. Но в py_smth scope dict создан только успех, после выполнения конструктора, когда локальный dict-дескриптор называется, тогда у меня есть ошибка сегментации. версияSegfault от dict destructor

class py_init 
    { 
    public: 
     py_init::py_init() 
     { 
       Py_Initialize(); 
       object main_module = import("__main__"); 
       main_namespace = main_module.attr("__dict__"); 
       main_namespace["sys"] = import("sys'); 
       exec("sys.path.insert(0, "/foo/boo"), main_namespace, main_namespace); 
     } 
    object main_namespace; 
    }; 


    class py_smth 
    { 
    public: 
     py_smth(std::shared_ptr<py_init> py) 
     { 
      dict local; 
     } 

}; 

Backtrace: 
Program terminated with signal SIGSEGV, Segmentation fault 
#0 0x00007f501ddf9a38 in ??() from /usr/lib/x86_64-linux-gnu/libpython3.4m.so.1.0 
(gdb) bt 
#0 0x00002b9545827a38 in ??() from /usr/lib/x86_64-linux-gnu/libpython3.4m.so.1.0 
#1 0x00002b95450ff12f in boost::python::api::object_base::~object_base()() from libplugin.so 
#2 0x00002b95450ff060 in boost::python::api::object::~object()() from libplugin.so 
#3 0x00002b9545101198 in boost::python::detail::dict_base::~dict_base() libplugin.so 
#4 0x00002b95451in boost::python::dict::~dict() libplugin.so 

Libs: -boost версия 1,54 -python 3.4.2

И я не знаю, почему ...

Ok. это пример кода, который не работает

main.cpp файл:

#include <iostream> 
#include <boost/python.hpp> 
#include <memory> 
#include "smth.h" 

using namespace boost::python; 

class py_init 
{ 
public: 
    py_init() 
    { 
      Py_Initialize(); 
      object main_module = import("__main__"); 
      main_namespace = main_module.attr("__dict__"); 
      main_namespace["sys"] = import("sys"); 
     main_namespace["time"] = import("time"); 
     main_namespace["threading"] = import("threading"); 
      exec("sys.path.insert(0, \"/foo/boo\")", main_namespace, main_namespace); 

     exec("def foo():print (\"bla\"); time.sleep(1);" , main_namespace, main_namespace); 
     exec("thread = threading.Thread(target=foo)" , main_namespace, main_namespace); 
     exec("thread.start()" , main_namespace, main_namespace); 
     state = PyEval_SaveThread(); 
    } 
    ~py_init() 
    { 
      PyEval_RestoreThread(state); 
      PyGILState_STATE gstate; 
      gstate = PyGILState_Ensure(); 
      exec("thread.join()" , main_namespace, main_namespace); 
      PyGILState_Release(gstate); 
     std::cout<<"Py_init dest"<<std::endl; 
    } 

    object main_namespace; 
    PyThreadState* state; 
}; 

int main() 
{ 
    std::shared_ptr<py_init> py(new py_init()); 
    smth s(py); 
    s.print_foo(); 
    return 0; 
} 

smth.h

#ifndef SMTH_H_ 
#define SMTH_H_ 

#include <boost/python.hpp> 
#include <memory> 
using namespace boost::python; 
class py_init; 

class smth 
{ 
public: 
    smth(std::shared_ptr<py_init> py) 
    { 
     dict local; 
    } 
    void print_foo() 
    { 
     std::cout<<"FOO"<<std::endl; 
    } 
    ~smth() 
    { 
     std::cout<<"smth dest"<<std::endl; 
    } 
}; 

#endif 

трассировку:

> Program terminated with signal SIGSEGV, Segmentation fault. 
> #0 0x00007f9c5d787a38 in ??() from /usr/lib/x86_64-linux-gnu/libpython3.4m.so.1.0 (gdb) bt 
> #0 0x00007f9c5d787a38 in ??() from /usr/lib/x86_64-linux-gnu/libpython3.4m.so.1.0 
> #1 0x0000000000401a9d in boost::python::api::object_base::~object_base()() 
> #2 0x0000000000401922 in boost::python::api::object::~object()() 
> #3 0x0000000000401b50 in boost::python::detail::dict_base::~dict_base()() 
> #4 0x0000000000401bde in boost::python::dict::~dict()() 
> #5 0x0000000000401c08 in smth::smth(std::shared_ptr<py_init>)() 
> #6 0x0000000000401727 in main() 

Компиляция:

g ++ -std = C++ 0x -fPIC -I/usr/include/python3.4m -c main.cpp -o app.o g ++ -std = C++ 0x app.o -lboost_python3 -lpython3.4m

+0

Наблюдение синтаксиса SO SO в действии, это ясно показывает, что код, который вы там представляете, очень вводит в заблуждение. Интересно, что даже компилирует ... –

+0

это псевдокод, который фиксирует ситуации – wrj

+0

Это даже не псевдокод, и этого не должно быть. И даже если бы это было так, вы действительно думаете, что псевдокод полезен при попытке диагностики segfault? Я не знаю о вас, но, по крайней мере, * мой * псевдокод редко сталкивается. –

ответ

1

программа вызова неопределенное поведение, как объект boost::python::dict создается и разрушается потоком, который не держит Global Interpreter Lock (GIL). Если поток выполняет что-либо, что влияет на подсчет ссылок на управляемый объект на основе python, тогда он должен получить GIL. Чтобы решить эту проблему, приобретите и отпустите GIL в конструкторе smth.

smth::smth(std::shared_ptr<py_init> py) 
{ 
    PyGILState_STATE gstate; 
    gstate = PyGILState_Ensure(); // Acquire GIL. 
    // Use scope to force destruction while GIL is held. 
    { 
    boost::python::dict local; 
    } 
    PyGILState_Release(gstate); // Release GIL. 
} 

Это может быть стоит рассмотреть с помощью RAII классов, чтобы помочь управлять GIL. Например, со следующим классом gil_lock, когда создается объект gil_lock, вызывающий поток получит GIL. Когда объект gil_lock разрушен, он освобождает GIL.

/// @brief RAII class used to lock and unlock the GIL. 
class gil_lock 
{ 
public: 
    gil_lock() { state_ = PyGILState_Ensure(); } 
    ~gil_lock() { PyGILState_Release(state_); } 
private: 
    PyGILState_STATE state_; 
}; 

smth конструктор может быть записано как:

smth::smth(std::shared_ptr<py_init> py) 
{ 
    gil_lock lock; 
    boost::python::dict local; 
} 

Вот аннотированный версия минимального первоначального кода. Это подчеркивает, что вызывающий объект не имеет GIL, когда возвращается конструктор py_init. Таким образом, создание и уничтожение boost::python::dict приводит к неопределенному поведению.

class py_init 
{ 
public: 
    py_init() 
    { 
    Py_Initialize(); // Acquires GIL (1). 
    // ... spawn Python thread that will compete for GIL. 
    state = PyEval_SaveThread(); // Release GIL (0). 
    } 

    ~py_init() 
    { 
    PyEval_RestoreThread(state); // Acquire GIL (1). 
    PyGILState_STATE gstate; 
    gstate = PyGILState_Ensure(); // Acquire GIL (2). 
    // ... 
    PyGILState_Release(gstate); // Release GIL (1). 
    } 

    PyThreadState* state; 
};  

int main() 
{ 
    std::shared_ptr<py_init> py(new py_init()); 
    // GIL not held. 
    { 
    // Modifying object without GIL; other thread may hold it. 
    boost::python::dict local; 
    } 
} // ~py_init() acquires GIL. 

В Python 3.4, можно также использовать PyGILState_Check() функцию, чтобы проверить, если вызывающий поток держит GIL. В документации это, главным образом, вспомогательная/диагностическая функция.Для более ранних версий, можно выполнить аналогичную проверку с помощью прямого доступа один из глобал Python:

_PyThreadState_Current == PyGILState_GetThisThreadState(); 

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