Thread: [pygccxml-development] Help needed with embedding
Brought to you by:
mbaas,
roman_yakovenko
From: Berserker <ber...@ho...> - 2009-08-03 15:03:05
|
Hi, we are trying the Py++ library for embedding Python in our project. We need to deal with GIL since we have to run multiple Python script "concurrently". The first problem we have encountered is that Py++ doesn't seems to support the ability to write a "member function adaptor" (that releases and aqcuires the GIL around the function call): libtorrent has a nice example of what we need here http://libtorrent.svn.sourceforge.net/viewvc/libtorrent/trunk/bindings/python/src/gil.hpp?revision=2460&view=markup At the moment we are "emulating" this feature with a custom transformer, here is the code: class allow_threading_transformer_t(transformer.transformer_t): def __init__(self, function): transformer.transformer_t.__init__(self, function) def __configure_sealed(self, controller): controller.add_pre_call_code("allow_threading_guard guard;") def configure_mem_fun(self, controller): self.__configure_sealed(controller) def configure_free_fun(self, controller): self.__configure_sealed(controller) def configure_virtual_mem_fun(self, controller): self.__configure_sealed(controller.default_controller) The only problem with this solution is that it creates too much code since it requires a static function for each wrapped methods, for example: Given the "Foo" class: class Foo { public: void test_method_1(); void test_method_2(); }; Py++ creates: struct Foo_wrapper : ::Foo, ::boost::python::wrapper< ::Foo > { static void test_method_1( ::Foo & inst) { allow_threading_guard guard; inst.test_method_1(); } static void test_method_2( ::Foo & inst) { allow_threading_guard guard; inst.test_method_2(); } } ::boost::python::class_< Foo_wrapper >( "Foo" ) .def("test_method_1", (void (*)( ::Foo & ))( &Foo_wrapper::test_method_1 ) ) .def("test_method_2", (void (*)( ::Foo & ))( &Foo_wrapper::test_method_2 ) ) ; insted of (based on the libtorrent code) only: boost::python::class_< Foo >("Foo") .def("test_method_1", allow_threads(&Foo::test_method_1)) .def("test_method_2", allow_threads(&Foo::test_method_2)) ; Is there any hope for a patch to support "member function adaptors"? The second problem is related to virtual member functions, let's consider a "test_method" in the Foo class class Foo { public: virtual void test_method(); }; Py++ now creates: struct Foo_wrapper : ::Foo, ::boost::python::wrapper< ::Foo > { virtual void test_method( ) { namespace bpl = boost::python; if( bpl::override func_test_method = this->get_override( "test_method" ) ){ bpl::object py_result = bpl::call<bpl::object>( func_test_method.ptr() ); } else{ ::Foo::test_method( ); } } static void default_test_method( ::Foo & inst ){ allow_threading_guard guard; if( dynamic_cast< Foo_wrapper * >( boost::addressof( inst ) ) ){ inst.::Foo::test_method(); } else{ inst.test_method(); } } } ::boost::python::class_< Foo_wrapper >( "Foo", ::boost::python::init< >() ) .def( "test_method", (void (*)( ::Foo & ))( &Foo_wrapper::default_test_method ) ) ; default_test_method works fine as in the first example but here we need to restore the right PyThreadState before calling any CPython function in the overridden "test_method" (this->get_override and bpl::call in this case). At the moment we have no solution for this problem but the idea is to create a custom base class for the Foo_wrapper class instead of boost::python::wrapper (where we can save the PyThreadState and restore that state before any call to the CPython API), here is the code: Py++ code: def override_create_identifier(creator, full_name ): if full_name == "::boost::python::wrapper": return "allow_threads_wrapper" return full_name pyplusplus.code_creators.algorithm.create_identifier = override_create_identifier C++: template <typename T> class allow_threads_wrapper : public boost::python::wrapper<T> { public: allow_threads_wrapper() : m_state(PyThreadState_Get()) { } PyThreadState *m_state; }; The last step should be the ability to add custom code before the "get_override" call, something like: struct Foo_wrapper : ::Foo, allow_threads_wrapper< ::Foo > { virtual void test_method( ) { allow_threading_swap swap(m_state); // allow_threading_swap is a class that swap/restore the PyThreadState namespace bpl = boost::python; if( bpl::override func_test_method = this->get_override( "test_method" ) ){ bpl::object py_result = bpl::call<bpl::object>( func_test_method.ptr() ); } else{ ::Foo::test_method( ); } } } Unfortunately the template of virtual_mem_fun in Py++ doesn't support the ability to add custom code at the beginning of the function, is it possible to make a small patch for this problem? Sorry for the long post, plz help Thanks _________________________________________________________________ Porta Messenger in Vacanza! Scaricalo sul cellulare! http://new.windowslivemobile.msn.com/IT-IT/windows-live-messenger/default.aspx |
From: Roman Y. <rom...@gm...> - 2009-08-03 18:59:58
|
On Mon, Aug 3, 2009 at 6:02 PM, Berserker<ber...@ho...> wrote: > Hi, we are trying the Py++ library for embedding Python in our project. > We need to deal with GIL since we have to run multiple Python script > "concurrently". > > The first problem we have encountered is that Py++ doesn't seems to support > the ability to write a "member function adaptor" (that releases and aqcuires > the GIL around the function call): libtorrent has a nice example of what we > need here > http://libtorrent.svn.sourceforge.net/viewvc/libtorrent/trunk/bindings/python/src/gil.hpp?revision=2460&view=markup > At the moment we are "emulating" this feature with a custom transformer, > here is the code: > > class allow_threading_transformer_t(transformer.transformer_t): > def __init__(self, function): > transformer.transformer_t.__init__(self, function) > def __configure_sealed(self, controller): > controller.add_pre_call_code("allow_threading_guard guard;") > > def configure_mem_fun(self, controller): > self.__configure_sealed(controller) > def configure_free_fun(self, controller): > self.__configure_sealed(controller) > def configure_virtual_mem_fun(self, controller): > self.__configure_sealed(controller.default_controller) > > The only problem with this solution is that it creates too much code since > it requires a static function for each wrapped methods, for example: > Given the "Foo" class: > > class Foo > { > public: > void test_method_1(); > void test_method_2(); > }; > > Py++ creates: > > struct Foo_wrapper : ::Foo, ::boost::python::wrapper< ::Foo > { > > static void test_method_1( ::Foo & inst) { > allow_threading_guard guard; > inst.test_method_1(); > } > > static void test_method_2( ::Foo & inst) { > allow_threading_guard guard; > inst.test_method_2(); > } > > } > > ::boost::python::class_< Foo_wrapper >( "Foo" ) > .def("test_method_1", (void (*)( ::Foo & ))( &Foo_wrapper::test_method_1 > ) ) > .def("test_method_2", (void (*)( ::Foo & ))( &Foo_wrapper::test_method_2 > ) ) > ; > > insted of (based on the libtorrent code) only: > > boost::python::class_< Foo >("Foo") > .def("test_method_1", allow_threads(&Foo::test_method_1)) > .def("test_method_2", allow_threads(&Foo::test_method_2)) > ; > > Is there any hope for a patch to support "member function adaptors"? This is a quick solution. By the way Py++ defines gil_guard_t class( http://pygccxml.svn.sourceforge.net/viewvc/pygccxml/pyplusplus_dev/pyplusplus/code_repository/gil_guard.py?revision=1428&view=markup ) I guess it is possible to create custom call policy, so the code will look like: > boost::python::class_< Foo >("Foo") > .def("test_method_1", &Foo::test_method_1, pythread_safe< return_value_policy< ... > >() ); Is this an open source project? > The second problem is related to virtual member functions, let's consider a > "test_method" in the Foo class > > class Foo > { > public: > virtual void test_method(); > }; > > Py++ now creates: > > struct Foo_wrapper : ::Foo, ::boost::python::wrapper< ::Foo > { > > virtual void test_method( ) { > namespace bpl = boost::python; > if( bpl::override func_test_method = this->get_override( > "test_method" ) ){ > bpl::object py_result = bpl::call<bpl::object>( > func_test_method.ptr() ); > } > else{ > ::Foo::test_method( ); > } > } > > static void default_test_method( ::Foo & inst ){ > allow_threading_guard guard; > if( dynamic_cast< Foo_wrapper * >( boost::addressof( inst ) ) ){ > inst.::Foo::test_method(); > } > else{ > inst.test_method(); > } > } > > } > > ::boost::python::class_< Foo_wrapper >( "Foo", ::boost::python::init< >() > ) > .def( "test_method", (void (*)( ::Foo & ))( > &Foo_wrapper::default_test_method ) ) > ; > > default_test_method works fine as in the first example but here we need to > restore the right PyThreadState before calling any CPython function in the > overridden "test_method" (this->get_override and bpl::call in this case). > At the moment we have no solution for this problem but the idea is to create > a custom base class for the Foo_wrapper class instead of > boost::python::wrapper (where we can save the PyThreadState and restore that > state before any call to the CPython API), here is the code: Well this problem is actually solved: mb = module_builder_t( ... ) tm = mb.mem_fun( 'test_method' ) gil_guard_code = """ pyplusplus::threading::gil_guard_t guard; """ tm.add_override_precall_code( gil_guard_code ) tm.add_default_precall_code( gil_guard_code ) tm.include_files.append( code_repository.gil_guard.file_name ) > Py++ code: > > def override_create_identifier(creator, full_name ): > if full_name == "::boost::python::wrapper": > return "allow_threads_wrapper" > > return full_name > > pyplusplus.code_creators.algorithm.create_identifier = > override_create_identifier > > C++: > > template <typename T> > class allow_threads_wrapper : public boost::python::wrapper<T> > { > public: > allow_threads_wrapper() : m_state(PyThreadState_Get()) { } > PyThreadState *m_state; > }; > > The last step should be the ability to add custom code before the > "get_override" call, something like: > > struct Foo_wrapper : ::Foo, allow_threads_wrapper< ::Foo > { > > virtual void test_method( ) { > allow_threading_swap swap(m_state); // allow_threading_swap is a > class that swap/restore the PyThreadState > > namespace bpl = boost::python; > if( bpl::override func_test_method = this->get_override( > "test_method" ) ){ > bpl::object py_result = bpl::call<bpl::object>( > func_test_method.ptr() ); > } > else{ > ::Foo::test_method( ); > } > } > > } > > Unfortunately the template of virtual_mem_fun in Py++ doesn't support the > ability to add custom code at the beginning of the function, is it possible > to make a small patch for this problem? Do you still think Py++ needs the patch? > Sorry for the long post, plz help HTH -- Roman Yakovenko C++ Python language binding http://www.language-binding.net/ |
From: Berserker <ber...@ho...> - 2009-08-04 09:28:13
|
Thanks for the quick reply Roman > This is a quick solution. By the way Py++ defines gil_guard_t class( http://pygccxml.svn.sourceforge.net/viewvc/pygccxml/pyplusplus_dev/pyplusplus/code_repository/gil_guard.py?revision=1428&view=markup ) Thanks for the link but gil_guard_t acquires and releases the GIL allowing to operate on the CPython API, instead we need to release the lock (PyEval_SaveThread) before the c++ function invocation and to acquire it again (PyEval_RestoreThread) after allowing multiple interpreters to run concurrently during the c++ code execution where no CPython API will be touched > I guess it is possible to create custom call policy, so the code will look like: > boost::python::class_< Foo >("Foo") > .def("test_method_1", &Foo::test_method_1, pythread_safe<return_value_policy< ... > >() ); I have tried this solution with a custom policy that calls PyEval_SaveThread in the "precall" function and PyEval_RestoreThread in the "postcall" function (according to http://www.boost.org/doc/libs/1_39_0/libs/python/doc/v2/default_call_policies.html ) but unfortunately after the precall's policy boost::python works on the CPython API before of the handler invocation (converting arguments for example), instead with a custom visitor (like that in libtorrent) we can surround the c++ code invocation with PyEval_SaveThread/PyEval_RestoreThread exactly before/after its execution where, as I said before, no CPython API will be touched for sure. I still think that something like: > boost::python::class_< Foo >("Foo") > .def("test_method_1", allow_threads(&Foo::test_method_1))>; is the best solution, any hope to support this? > tm.add_override_precall_code( ... ) > tm.add_default_precall_code( ... ) add_override_precall_code is exactly what we need but "strangely" no code is added when the allow_threading_transformer_t is added to the function (we still need the transformer as I mentioned above until the "visitor" patch), my code: > tm.add_transformation(allow_threading_transformer_creator()) > tm.add_override_precall_code(gil_guard_code) If I comment the "add_transformation" line, the override_precall_code is correctly added to the wrapper. Any advice? > Is this an open source project? It will be :) Here it is the link http://osiris.kodeware.net/ if you are interested :) Thanks again for your help _________________________________________________________________ Scarica i nuovi gadget per personalizzare Messenger! http://www.messenger.it/home_gadget.aspx |
From: Roman Y. <rom...@gm...> - 2009-08-04 10:02:07
|
On Tue, Aug 4, 2009 at 12:27 PM, Berserker<ber...@ho...> wrote: > Thanks for the quick reply Roman > >> This is a quick solution. By the way Py++ defines gil_guard_t class( >> http://pygccxml.svn.sourceforge.net/viewvc/pygccxml/pyplusplus_dev/pyplusplus/code_repository/gil_guard.py?revision=1428&view=markup >> ) > > Thanks for the link but gil_guard_t acquires and releases the GIL allowing > to operate on the CPython API, instead we need to release the lock > (PyEval_SaveThread) before the c++ function invocation and to acquire it > again (PyEval_RestoreThread) after allowing multiple interpreters to run > concurrently during the c++ code execution where no CPython API will be > touched Sorry for misunderstanding. >> I guess it is possible to create custom call policy, so the code will look >> like: > >> boost::python::class_< Foo >("Foo") >> .def("test_method_1", &Foo::test_method_1, >> pythread_safe<return_value_policy< ... > >() ); > > I have tried this solution with a custom policy that calls PyEval_SaveThread > in the "precall" function and PyEval_RestoreThread in the "postcall" > function (according to > http://www.boost.org/doc/libs/1_39_0/libs/python/doc/v2/default_call_policies.html > ) but unfortunately after the precall's policy boost::python works on the > CPython API before of the handler invocation (converting arguments for > example), instead with a custom visitor (like that in libtorrent) we can > surround the c++ code invocation with PyEval_SaveThread/PyEval_RestoreThread > exactly before/after its execution where, as I said before, no CPython API > will be touched for sure. > I still think that something like: > >> boost::python::class_< Foo >("Foo") >> .def("test_method_1", allow_threads(&Foo::test_method_1))>; > > is the best solution, any hope to support this? Did you implement "allow_threads"? If yes, can you publish it and some usage example? If not, I guess you thought about it, right ? Do you mind to share your thoughts? This is an interesting question, and may be you should take it to Boost.Python mailing list. Whatever solution the list will come with, it will be possible to integrate it with Py++. >> tm.add_override_precall_code( ... ) >> tm.add_default_precall_code( ... ) > > add_override_precall_code is exactly what we need but "strangely" no code is > added when the allow_threading_transformer_t is added to the function (we > still need the transformer as I mentioned above until the "visitor" patch), > my code: > >> tm.add_transformation(allow_threading_transformer_creator()) >> tm.add_override_precall_code(gil_guard_code) > > If I comment the "add_transformation" line, the override_precall_code is > correctly added to the wrapper. Any advice? Yes: http://pygccxml.svn.sourceforge.net/viewvc/pygccxml/pyplusplus_dev/pyplusplus/function_transformers/templates.py?revision=1467&view=markup The short version: if you applied a transformation, than everything should be done in it. The long version: you can modify the templates so it will release the GIL. You can do it without changing Py++ code. >> Is this an open source project? > > It will be :) > Here it is the link http://osiris.kodeware.net/ if you are interested :) :-). Very cool project. > Thanks again for your help You are welcome. -- Roman Yakovenko C++ Python language binding http://www.language-binding.net/ |
From: Berserker <ber...@ho...> - 2009-08-04 11:02:43
|
> Did you implement "allow_threads"? If yes, can you publish it and some > usage example? > If not, I guess you thought about it, right ? Do you mind to share > your thoughts? > > This is an interesting question, and may be you should take it to > Boost.Python mailing list. Whatever solution the list will come with, > it will be possible to integrate it with Py++. I don't understand why do you think that this question should be posted in the Boost.Python mailing list. "allow_threads" is just the code extracted from http://libtorrent.svn.sourceforge.net/viewvc/libtorrent/trunk/bindings/python/src/gil.hpp?revision=2460&view=markup , maybe a property like "adaptor" or "visitor" for each Py++ function would do the work, but I'm not so skilled with the Py++ API and probably there are other better solutions, btw something like: > tm = foo_class.mem_fun( 'test_method' ) > tm.adaptor = "my_adaptor" and then Py++ will create: > boost::python::class_< Foo >("Foo") > .def("test_method_1", my_adaptor(&Foo::test_method_1)) Another solution (probably more "clean") could be a list of Templates for each boost::python API (boost::python::class_::def, boost::python::class_::add_property ecc...) so that we can customize them, what about? > Yes: http://pygccxml.svn.sourceforge.net/viewvc/pygccxml/pyplusplus_dev/pyplusplus/function_transformers/templates.py?revision=1467&view=markup > > The short version: if you applied a transformation, than everything > should be done in it. > > The long version: you can modify the templates so it will release the > GIL. You can do it without changing Py++ code. Thanks, that was useful! Btw I have to change the "whole template" and pay attention to keep it aligned in the next releases of Py++ (that's ok for the moment), what about adding $pre_call and $post_call params as in the "default" Template? > > Here it is the link http://osiris.kodeware.net/ if you are interested :) > > :-). Very cool project. Thanks :) _________________________________________________________________ Messenger è su Hotmail. Scopri le novità. http://www.messenger.it/accediWebMessengerHotmail.aspx |
From: Roman Y. <rom...@gm...> - 2009-08-04 12:10:17
|
On Tue, Aug 4, 2009 at 2:02 PM, Berserker<ber...@ho...> wrote: > I don't understand why do you think that this question should be posted in > the Boost.Python mailing list. Sorry, it seems that I need few more hours to sleep > "allow_threads" is just the code extracted from > http://libtorrent.svn.sourceforge.net/viewvc/libtorrent/trunk/bindings/python/src/gil.hpp?revision=2460&view=markup > , > maybe a property like "adaptor" or "visitor" for each Py++ function would do > the work, but I'm not so skilled with the Py++ API and probably there are > other better solutions, btw something like: > >> tm = foo_class.mem_fun( 'test_method' ) >> tm.adaptor = "my_adaptor" > > and then Py++ will create: > >> boost::python::class_< Foo >("Foo") >> .def("test_method_1", my_adaptor(&Foo::test_method_1)) > > Another solution (probably more "clean") could be a list of Templates for > each boost::python API (boost::python::class_::def, > boost::python::class_::add_property ecc...) so that we can customize them, > what about? Let me think about it. I will back to you in a day or two. I need to check how it could be integrated with Py++. >> Yes: >> http://pygccxml.svn.sourceforge.net/viewvc/pygccxml/pyplusplus_dev/pyplusplus/function_transformers/templates.py?revision=1467&view=markup >> >> The short version: if you applied a transformation, than everything >> should be done in it. >> >> The long version: you can modify the templates so it will release the >> GIL. You can do it without changing Py++ code. > > Thanks, that was useful! Btw I have to change the "whole template" and pay > attention to keep it aligned in the next releases of Py++ (that's ok for the > moment), More or less. You can change it value without touching Py++ code. For example: import pyplusplus import function_transformation as ft from ft import templates as ft_tmpl ft_tmpl.<class name>.<template> = NewTemplate You will only have to preserve the keywords and add your keywords/code. > what about adding $pre_call and $post_call params as in the > "default" Template? As above: let me think about it and I will back to you soon, okey? -- Roman Yakovenko C++ Python language binding http://www.language-binding.net/ |
From: Berserker <ber...@ho...> - 2009-08-05 07:31:11
|
> let me think about it and I will back to you soon, okey? Ok, thanks again! _________________________________________________________________ Porta Messenger in Vacanza! Scaricalo sul cellulare! http://new.windowslivemobile.msn.com/IT-IT/windows-live-messenger/default.aspx |
From: Berserker <ber...@ho...> - 2009-08-07 10:17:54
|
> I commited the code and testers to SVN: > http://pygccxml.svn.sourceforge.net/viewvc/pygccxml?view=rev&revision=1741 > pyplusplus_dev/unittests/function_adaptor_tester.py files contains > usage example. Thanks for your help! At the moment I'm using the "stable" versione but soon I'll migrate to svn (or maybe a new stable version ;) ) P.S. (OT): I'm debugging the embedded python code with Winpdb ( http://winpdb.org/docs/embedded-debugging/ ), that's a very cool stuff :D _________________________________________________________________ Porta Hotmail in vacanza. Leggi la posta dal cellulare! http://new.windowslivemobile.msn.com/IT-IT/windows-live-hotmail/default.aspx |
From: Roman Y. <rom...@gm...> - 2009-08-07 17:54:27
|
On Fri, Aug 7, 2009 at 1:17 PM, Berserker<ber...@ho...> wrote: > Thanks for your help! You are welcome. > At the moment I'm using the "stable" versione but soon I'll migrate to svn > (or maybe a new stable version ;) ) Right now I have some free time, I suggest you to verify that the committed code does what you want and report any problem with it. -- Roman Yakovenko C++ Python language binding http://www.language-binding.net/ |