[C++-sig] boost::python and threads
William Ladwig
wladwig at wdtinc.com
Sat Jul 4 20:34:00 CEST 2009
It looks to me like you have a garbage collection problem going on. If you create a wrapped c++ object in python, then python is going to own the object and will destroy it when its reference count goes to 0. In your python example script at the bottom, you call the Ticker's run() function, which from the python point of view, returns quickly and the script ends. Python has no idea that you spawned off a new thread from the C++ side, so when the script ends, python destroys the object and now you have a problem. One way that you can check to see if this is what is going on is to add this to the bottom of the test script and see if the crashes go away:
# Warning, you'll need to kill this script manually
import time
while True:
time.sleep(1)
Generally when I want to fire off a new C++ thread, I hold any objects that the thread needs in an auto_ptr (see held type for class wrappers), take ownership of them in C++ (see the FAQ in the documentation) and start the thread from the C++ side. Or, you can also create C++ wrappers which accept shared_ptr arguments, while holding your classes in shared_ptrs, and this should handle the reference counting as well. Unfortunately, this may require some interface changes to what you have already written (or possibly some clever wrapping).
Hope this helps,
Bill
________________________________________
From: cplusplus-sig-bounces+wladwig=wdtinc.com at python.org [cplusplus-sig-bounces+wladwig=wdtinc.com at python.org] On Behalf Of Paul Scruby [paul at gingernut.tv]
Sent: Friday, July 03, 2009 6:15 AM
To: cplusplus-sig at python.org
Subject: [C++-sig] boost::python and threads
I am having some problems using boost::python with boost::thread. I'm using
threads because I want to run some tasks in the background when I'm using
the Python's interactive shell. However, when I use get_override() to call
a Python method from another boost::thread it crashes internally. For
example:
#include <boost/python.hpp>
#include <boost/thread/thread.hpp>
#include <boost/thread/xtime.hpp>
using namespace boost::python;
class Ticker
: public wrapper<Ticker>
{
private:
bool run_;
volatile bool * running_;
boost::thread * thread_;
boost::xtime xt_;
public:
Ticker() : running_(&run_) { *running_ = false; }
void operator()()
{
while (*running_)
{
boost::xtime_get(&xt_, boost::TIME_UTC);
++xt_.sec;
boost::thread::sleep(xt_);
onTick();
}
}
void run()
{
if (*running_ == false)
{
*running_ = true;
thread_ = new boost::thread(*this);
}
}
void stop()
{
if (*running_ == true)
{
*running_ = false;
thread_->join();
delete thread_;
}
}
virtual void onTick() { get_override("onTick")(); }
void default_onTick() {}
};
BOOST_PYTHON_MODULE(tick)
{
class_<Ticker, boost::noncopyable> ("Ticker")
.def("run", &Ticker::run)
.def("stop", &Ticker::stop)
.def("onTick", &Ticker::default_onTick);
}
Here is a test script that which will crash when you import it into Python's
interactive shell.
from tick import Ticker
class MyTicker(Ticker):
def onTick(self):
print "Each second"
myticker = MyTicker()
myticker.run()
I ran this test initially on Python 2.4.4 with the Sun C++ 5.9 compiler on
Solaris and I also tested it using Python 2.6.2 with Visual Studio 2008 on
Windows XP.
The call-stack in dbx on Solaris:
>>> t at 2 (l at 2) signal SEGV (no mapping at the fault address) in
PyErr_Restore at 0xfef38fa1
0xfef38fa1: PyErr_Restore+0x0031: movl 0x00000028(%edi),%ecx
Current function is boost::python::override::operator()
99 detail::method_result x(
(dbx) where
current thread: t at 2
[1] PyErr_Restore(0x80652fc, 0x80f1220, 0x0, 0xfe77ee90, 0xfef3951e,
0x80652fc), at 0xfef38fa1
[2] PyErr_SetObject(0x80652fc, 0x80f1220), at 0xfef3901e
[3] PyErr_Format(0x80652fc, 0xfef5c2d8, 0xfef7902c), at 0xfef3951e
[4] PyObject_Call(0xfef88768, 0x806102c, 0x0), at 0xfeee291a
[5] PyEval_CallObjectWithKeywords(0xfef88768, 0x806102c, 0x0), at
0xfef2bf02
[6] PyEval_CallFunction(0xfef88768, 0xfeb02004), at 0xfef434c5
=>[7] boost::python::override::operator()(this = 0xfe77ef30), line 99 in
"override.hpp"
[8] Ticker::onTick(this = 0x810a304), line 48 in "ticker.cc"
[9] Ticker::operator()(this = 0x810a304), line 25 in "ticker.cc"
[10] boost::detail::thread_data<Ticker>::run(this = 0x810a288), line
56 in "thread.hpp"
[11] thread_proxy(0x810a288), at 0xfea78ce4
[12] _thr_setup(0xfe670200), at 0xfee159b9
[13] _lwp_start(0xfe77ef54, 0x80f1220, 0xfe77ee7c, 0xfef3901e,
0x80652fc, 0x80f1220), at 0xfee15ca0
The call-stack in Visual Studio 2008:
python26.dll!1e013595()
[Frames below may be incorrect and/or missing, no symbols loaded for
python26.dll]
python26.dll!1e09ee7d()
> tick.pyd!boost::python::override::operator()() Line 103 + 0x16 bytes
C++
00f3fd64()
tick.pyd!Ticker::operator()() Line 27 + 0xe bytes C++
tick.pyd!boost::detail::thread_data<Ticker>::run() Line 57 C++
tick.pyd!boost::`anonymous namespace'::thread_start_function(void *
param=0x00245f30) Line 168 C++
msvcr90d.dll!_callthreadstartex() Line 348 + 0xf bytes C
msvcr90d.dll!_threadstartex(void * ptd=0x00d46938) Line 331 C
kernel32.dll!7c80b729()
Have a missed a trick using the wrapper, or does boost::python not support
threading?
Many thanks,
Paul
_______________________________________________
Cplusplus-sig mailing list
Cplusplus-sig at python.org
http://mail.python.org/mailman/listinfo/cplusplus-sig
More information about the Cplusplus-sig
mailing list