【问题标题】:Segfault from dict destructor来自dict析构函数的段错误
【发布时间】:2015-03-28 23:47:03
【问题描述】:

我对 boost python、基础对象析构函数有疑问。 当在 py_init 范围内创建和销毁对象 boost::python::dict 时,一切都很好。 但是在 py_smth 范围内 dict 只有在本地 dict 析构函数是构造函数执行后才创建成功 调用然后我有分段错误。

    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  0x00002b9545101230 in boost::python::dict::~dict() libplugin.so

库版本: -提升版本 1.54 -python 3.4.2

我也不知道为什么……

好的。这是不工作的示例代码

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

【问题讨论】:

  • 观看 SO 的语法高亮显示,它清楚地表明您在那里呈现的代码非常具有误导性。我想知道这是否甚至可以编译...
  • 这是捕获情况的伪代码
  • 这甚至不是伪代码,也不应该是。即使是这样,你真的认为伪代码在尝试诊断段错误时有用吗?我不了解你,但至少 my 伪代码很少出现段错误。
  • 我添加了编译代码。也许现在你可以告诉我更多:)
  • 如果你使用-std=c++11,你能重现这个吗?你能把它减少到一个文件吗?是否需要动态创建py_init 实例并将shared_ptr 传递给smth 的ctor?如果不创建 Python 线程怎么办?

标签: python c++ boost dictionary destructor


【解决方案1】:

程序正在调用未定义的行为,因为boost::python::dict 对象正在由不持有Global Interpreter Lock (GIL) 的线程创建和销毁。如果一个线程正在做任何影响 python 托管对象的引用计数的事情,那么它需要获得 GIL。要解决此问题,请在 smth 构造函数中获取并释放 GIL。

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;
}

这里是最小原始代码的注释版本。它强调一旦py_init 的构造函数返回,调用者就不会持有 GIL。因此,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(这是一个有效状态),它会发出致命错误。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-02-05
    • 2018-09-24
    • 1970-01-01
    • 2012-03-09
    • 1970-01-01
    • 2022-01-03
    • 2013-04-30
    • 2016-07-05
    相关资源
    最近更新 更多