[C++-sig] Deriving Python function from C++ base class with pure virtual function
John Meinel
john at johnmeinel.com
Sun Nov 14 18:52:04 CET 2004
Niall Douglas wrote:
> On 13 Nov 2004 at 1:53, Paul F. Kunz wrote:
>
>
[...]
>
>>> Should I start the second thread with Python's own thread class? Is
>>>there some Python C API functions I could call to read the Python
>>>object to be called from this second thread? Any suggestions /
>>>advise would be appreciated.
>
>
> Before entering python, you must ensure that python's thread state
> has been set for the current thread. This implies holding python's
> Global Interpreter Lock. If you try entering python without setting
> the thread state, you'll get a warning on stderr and a segfault most
> probably.
>
> If however you want to have multiple threads working with python
> concurrently, that is a whole new bag of worms. There is a solution
> but it involves patching BPL to release and grab the python GIL every
> time it enters and exits C/C++ code. Do say if you want to do this,
> but be warned it's not trivial.
>
> Cheers,
> Niall
>
>
I've done some work with multiple threads in python. Here is what I did.
First, I have C++ functions that run for a long time doing analysis, and
some that are very quick. I don't bother threading the quick ones, just
the long running ones.
I have a Callback class which handles the reporting of how things are
going. The C++ functions don't realize that they are being called from
python, because their only interaction with the outside world is through
the Callback object. The Callback is a C++ object, which I inherit from
in python.
All of my threads are started from python, which means that they already
have thread state associated with them, and when the call is made to the
C++ code, they already have the GIL (or they wouldn't be running.)
I created a class which when instantiated replaces the Callback object
with one of it's own, such that whenever a call is made, it grabs the
GIL, and then places the call to the original Callback, and when that
returns, it releases the GIL.
So what does this all look like.
The C++ side:
class Callback
{
public:
int send_message(const std::string &msg) = 0;
};
class Worker
{
Callback *cb_;
public:
Callback *setCallback(Callback *cb)
{
Callback oldcb = cb_;
cb_ = cb;
return oldcb;
}
int doSomethingFast(int arg);
int doSomethingLong(int arg);
};
class PyCallback
{
Callback *other_;
PyThreadState *state_;
Worker *worker_;
public:
PyCallback ()
: state_(0), other_(0), worker_(0)
{ }
~PyCallback ()
{
releaseCallback();
}
void grabCallback(Worker& w)
{
other_ = w.setCallback(this);
worker_ = &w;
state_ = PyEval_SaveThread();
}
void releaseCallback()
{
if(other_) {
PyEval_AcquireThread(state_);
worker_->setCallback(other_);
worker_ = 0;
state_ = 0;
other_ = 0;
}
}
int send_message(const std::string &msg)
{
int response = 0;
if (other_) {
PyEval_AcquireThread(state_);
response = other_->send_message(msg);
PyEval_ReleaseThread(state_);
}
return response;
}
};
And now the boost::python wrapper
static int workerDoSomething(Worker& w, int arg)
{
PyCallback pcb;
pcb.getCallback(w);
return w.doSomething(arg);
}
void exportWorker()
{
class_<Worker>("Worker")
.def("doSomethingLong", &workerDoSomething)
.def("doSomethingFast", &Worker::doSomethingFast)
;
}
Now, I happen to have a structure where everything that needs work done
inherits from a "Worker" class. So they all have the setCallback()
function, and they all use the Callback to talk about what is going on.
Mine is also a little bit more complicated, as I have another parent
class, which lets me write more like: (following RAII rules)
static int workerDoSomething(Worker& w, int arg)
{
PyWrap pw(w);
return w.doSomething(arg);
}
Also, I don't use raw pointers, everything is a boost::shared_ptr<>, but
I figured raw pointers is a little bit easier to read (and less to type).
I could see easily turning PyCallback into a template function, such
that Worker is not defined, and you just need a function like
setCallback() to exist.
But this is basically what I am doing. Now probably boost::python could
be rewritten such that it automatically does most of this stuff. (it
already is creating function wrappers, it could probably just add some
more threading code into them.)
But this is a way to do it without changing any of boost::python.
It depends on your framework, though.
John
=:->
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 256 bytes
Desc: OpenPGP digital signature
URL: <http://mail.python.org/pipermail/cplusplus-sig/attachments/20041114/6f734119/attachment.pgp>
More information about the Cplusplus-sig
mailing list