来自 dict 析构函数的段错误
Segfault from dict destructor
我对 boost python、基础对象析构函数有疑问。
当在 py_init 范围内创建和销毁对象 boost::python::dict 时,一切都很好。
但是在 py_smth 范围内,dict 只创建成功,在构造函数执行之后,当本地 dict descructor 是
调用然后我有分段错误。
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
程序正在调用未定义的行为,因为 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 classes 来帮助管理 GIL。例如,下面的gil_lock
class,当一个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 once py_init
的构造函数 returns。因此,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。根据文档,它主要是一个 helper/diagnostic 函数。对于早期版本,可以通过直接访问 Python 全局变量之一来执行类似的检查:
_PyThreadState_Current == PyGILState_GetThisThreadState();
PyThreadState_Get()
函数不能用于此类诊断,因为如果没有线程持有 GIL(这是一个有效状态),它会发出致命错误。
我对 boost python、基础对象析构函数有疑问。 当在 py_init 范围内创建和销毁对象 boost::python::dict 时,一切都很好。 但是在 py_smth 范围内,dict 只创建成功,在构造函数执行之后,当本地 dict descructor 是 调用然后我有分段错误。
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
程序正在调用未定义的行为,因为 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 classes 来帮助管理 GIL。例如,下面的gil_lock
class,当一个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 once py_init
的构造函数 returns。因此,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。根据文档,它主要是一个 helper/diagnostic 函数。对于早期版本,可以通过直接访问 Python 全局变量之一来执行类似的检查:
_PyThreadState_Current == PyGILState_GetThisThreadState();
PyThreadState_Get()
函数不能用于此类诊断,因为如果没有线程持有 GIL(这是一个有效状态),它会发出致命错误。