[C++-sig] Make threading support official (was: Re: Conversion of python files to C++ ostreams)

Niall Douglas s_sourceforge at nedprod.com
Sat Apr 10 16:14:41 CEST 2010


On 6 Apr 2010 at 12:14, troy d. straszheim wrote:

My apologies for the delay in replying.

> > I think, with hindsight, that we all were going at it wrong at that 
> > time. What we *ought* to have been doing is an upcall mechanism such 
> > that routine X is always called just before entering C++ space and 
> > routine Y is always called just before entering Python space. That 
> > way you get the best of all worlds.
> 
> Makes perfect sense to me, but I'm very new to the problem.  Help me out 
> a bit...  What code of yours should I be looking at?  I found this:
> 
> http://aspn.activestate.com/ASPN/Mail/Message/c++-sig/1865844

Yeah, that's old. Try 
http://github.com/ned14/tnfox/blob/master/Python/BoostPatches.zip 
which is about two years fresher.

> In that patch, why do you (un)lock in invoke.hpp instead of in
> 
>    static PyObject* function_call(...)
> 
> in function.cpp and the various other static C linkage functions that 
> are registered directly with the Python C/API via PyTypeObjects?  At a 
> glance, these seem the closest points to the language boundary.  

I am afraid that I do not remember why. It could have been out of 
ignorance, or simply because the patch I used as a base did so there 
as well. It could also have been that I had a very good reason such 
as forcing an ABI incompatibility to prevent accidental mixing of 
incompatible binaries or something, or perhaps it was a maintenance 
issue or some question of exception safety. It's old code I am happy 
to see replaced with something much better anyway.

> What 
> is supposed to happen when python calls cpp, which calls python again?

In my patch at least the GIL gets repeatedly locked and unlocked as 
necessary however deep you go, including if an exception gets thrown 
or something is being iterated. This isn't fast nor was I ever happy 
about it, but it meant behaviour consistent with people's 
expectations.
 
>   How about with multiple interpreters? 

Last time I checked this worked fine, though this was some time ago. 
Running multiple interpreters is one of the TnFOX test suite tests 
anyway.

> Would you also need to lock in e.g. object_protocol.cpp:
> 
> void setattr(object const& target, object const& key, object const& value)
> {
>      if (PyObject_SetAttr(target.ptr(), key.ptr(), value.ptr()) == -1)
>          throw_error_already_set();
> }

Maybe I am missing your point, but surely all accesses to Python must 
hold the GIL first, not least because the GIL also specifies the 
current interpreter to use? (I know that you can get away with some 
calls, but relying on this seems hardly prudent).

> If you could indeed use those C linkage functions, how about having 
> boost.python send boost::signals that cppland has been entered/left. 
> (sanity check?)  This would support multiple receivers, 
> connect/disconnect, all that stuff that comes with boost::signals.  This 
> could compile out for singlethreaded versions.   You could even send, 
> say, an enum value with the signal to indicate what was happening 
> (instance_get,  instance_new, function_call) and get some kind of 
> tracing ability out of it.  Thoughts?

I would have issue with boost::signals2 solely because it uses 
mutexes when a lock free implementation is not only entirely 
possible, but highly desirable given the typical use cases of a 
signals and slots implementation. It may however be possible to use 
boost:signals and the Python GIL to serialise around it - in this 
situation then yes, using boost::signals would be useful assuming 
that its overhead is minimal. Obviously enough if you're firing a 
signal in something as minor as your setattr() function then in the 
ideal case you want no code being executed if there are no slots 
registered for that particular signal.

In TnFOX I have a metaprogramming construct which assembles inline a 
jump table of specialisations of classes between which at run time 
can be dynamically chosen. Fairly amazingly, all major compilers 
correctly elide table entries which cannot be chosen such that they 
will remove the choice logic entirely if there is just one possible 
choice, or the whole construct if there are none. This particular 
construct is really useful in BPL actually because it lets you fake 
stuff like setting at runtime arbitrary python code as a C (not C++) 
API based sort function.

Hence it may well be that a static signals and slots implementation 
could be more appropriate in this situation. I guess I wouldn't know 
until I run benchmarks. Your thoughts?

Cheers,
Niall




More information about the Cplusplus-sig mailing list