diff --git a/doc/source/changelog.rst b/doc/source/changelog.rst index e76b10d75..3a51847c9 100644 --- a/doc/source/changelog.rst +++ b/doc/source/changelog.rst @@ -102,6 +102,10 @@ organisation on `GitHub `__. * Fixed CMAP grids not being converted between kcal mol-1 and kJ mol-1 when writing to, and reading from, GROMACS topology files. +* Released the GIL for the duration of ``LambdaLever::setLambda``, so that threaded + callers, such as replica exchange workers, are no longer serialised against one + another while lambda is being updated in a context. + `2026.1.0 `__ - June 2026 ----------------------------------------------------------------------------------------- diff --git a/wrapper/Convert/SireOpenMM/LambdaLever.pypp.cpp b/wrapper/Convert/SireOpenMM/LambdaLever.pypp.cpp index 2e4d52185..42c029f54 100644 --- a/wrapper/Convert/SireOpenMM/LambdaLever.pypp.cpp +++ b/wrapper/Convert/SireOpenMM/LambdaLever.pypp.cpp @@ -2,8 +2,8 @@ // (C) Christopher Woods, GPL >= 3 License -#include "boost/python.hpp" #include "LambdaLever.pypp.hpp" +#include "boost/python.hpp" namespace bp = boost::python; @@ -31,7 +31,7 @@ namespace bp = boost::python; #include "tostring.h" -SireOpenMM::LambdaLever __copy__(const SireOpenMM::LambdaLever &other){ return SireOpenMM::LambdaLever(other); } +SireOpenMM::LambdaLever __copy__(const SireOpenMM::LambdaLever &other) { return SireOpenMM::LambdaLever(other); } #include "Helpers/copy.hpp" @@ -39,303 +39,216 @@ SireOpenMM::LambdaLever __copy__(const SireOpenMM::LambdaLever &other){ return S #include "Helpers/release_gil_policy.hpp" +#include "Helpers/scoped_gil_release.hpp" + #include "Qt/qdatastream.hpp" -void register_LambdaLever_class(){ +namespace +{ + /** setLambda holds the GIL for the whole parameter update, which + serialises replica exchange workers against one another. Nothing in + the call touches Python, so the GIL is dropped for the duration. + */ + double setLambda_no_gil(const SireOpenMM::LambdaLever &lever, + OpenMM::Context &context, + double lambda_value, + double rest2_scale, + bool update_constraints) + { + SireHelpers::ScopedGILRelease release_gil; + return lever.setLambda(context, lambda_value, rest2_scale, update_constraints); + } +} + +void register_LambdaLever_class() +{ { //::SireOpenMM::LambdaLever - typedef bp::class_< SireOpenMM::LambdaLever > LambdaLever_exposer_t; - LambdaLever_exposer_t LambdaLever_exposer = LambdaLever_exposer_t( "LambdaLever", "This is a lever that is used to change the parameters in an OpenMM\ncontext according to a lambda value. This is actually a collection\nof levers, each of which is controlled by the main lever.\n\nYou can use SireCAS expressions to control how each lever changes\neach parameter\n", bp::init< >("") ); - bp::scope LambdaLever_scope( LambdaLever_exposer ); - LambdaLever_exposer.def( bp::init< SireOpenMM::LambdaLever const & >(( bp::arg("other") ), "") ); + typedef bp::class_ LambdaLever_exposer_t; + LambdaLever_exposer_t LambdaLever_exposer = LambdaLever_exposer_t("LambdaLever", "This is a lever that is used to change the parameters in an OpenMM\ncontext according to a lambda value. This is actually a collection\nof levers, each of which is controlled by the main lever.\n\nYou can use SireCAS expressions to control how each lever changes\neach parameter\n", bp::init<>("")); + bp::scope LambdaLever_scope(LambdaLever_exposer); + LambdaLever_exposer.def(bp::init((bp::arg("other")), "")); { //::SireOpenMM::LambdaLever::addLever - - typedef void ( ::SireOpenMM::LambdaLever::*addLever_function_type)( ::QString const & ) ; - addLever_function_type addLever_function_value( &::SireOpenMM::LambdaLever::addLever ); - - LambdaLever_exposer.def( - "addLever" - , addLever_function_value - , ( bp::arg("lever_name") ) - , bp::release_gil_policy() - , "" ); - + + typedef void (::SireOpenMM::LambdaLever::*addLever_function_type)(::QString const &); + addLever_function_type addLever_function_value(&::SireOpenMM::LambdaLever::addLever); + + LambdaLever_exposer.def( + "addLever", addLever_function_value, (bp::arg("lever_name")), bp::release_gil_policy(), ""); } { //::SireOpenMM::LambdaLever::addPerturbableMolecule - - typedef int ( ::SireOpenMM::LambdaLever::*addPerturbableMolecule_function_type)( ::SireOpenMM::OpenMMMolecule const &,::QHash< QString, int > const &,::SireBase::PropertyMap const & ) ; - addPerturbableMolecule_function_type addPerturbableMolecule_function_value( &::SireOpenMM::LambdaLever::addPerturbableMolecule ); - - LambdaLever_exposer.def( - "addPerturbableMolecule" - , addPerturbableMolecule_function_value - , ( bp::arg("molecule"), bp::arg("start_indicies"), bp::arg("map")=SireBase::PropertyMap() ) - , "Add info for the passed perturbable OpenMMMolecule, returning\n its index in the list of perturbable molecules\n" ); - + + typedef int (::SireOpenMM::LambdaLever::*addPerturbableMolecule_function_type)(::SireOpenMM::OpenMMMolecule const &, ::QHash const &, ::SireBase::PropertyMap const &); + addPerturbableMolecule_function_type addPerturbableMolecule_function_value(&::SireOpenMM::LambdaLever::addPerturbableMolecule); + + LambdaLever_exposer.def( + "addPerturbableMolecule", addPerturbableMolecule_function_value, (bp::arg("molecule"), bp::arg("start_indicies"), bp::arg("map") = SireBase::PropertyMap()), "Add info for the passed perturbable OpenMMMolecule, returning\n its index in the list of perturbable molecules\n"); } { //::SireOpenMM::LambdaLever::addRestraintIndex - - typedef void ( ::SireOpenMM::LambdaLever::*addRestraintIndex_function_type)( ::QString const &,int ) ; - addRestraintIndex_function_type addRestraintIndex_function_value( &::SireOpenMM::LambdaLever::addRestraintIndex ); - - LambdaLever_exposer.def( - "addRestraintIndex" - , addRestraintIndex_function_value - , ( bp::arg("force"), bp::arg("index") ) - , bp::release_gil_policy() - , "Add the index of a restraint force called restraint in the\n OpenMM System. There can be multiple restraint forces with\n the same name\n" ); - + + typedef void (::SireOpenMM::LambdaLever::*addRestraintIndex_function_type)(::QString const &, int); + addRestraintIndex_function_type addRestraintIndex_function_value(&::SireOpenMM::LambdaLever::addRestraintIndex); + + LambdaLever_exposer.def( + "addRestraintIndex", addRestraintIndex_function_value, (bp::arg("force"), bp::arg("index")), bp::release_gil_policy(), "Add the index of a restraint force called restraint in the\n OpenMM System. There can be multiple restraint forces with\n the same name\n"); } { //::SireOpenMM::LambdaLever::getForceIndex - - typedef int ( ::SireOpenMM::LambdaLever::*getForceIndex_function_type)( ::QString const & ) const; - getForceIndex_function_type getForceIndex_function_value( &::SireOpenMM::LambdaLever::getForceIndex ); - - LambdaLever_exposer.def( - "getForceIndex" - , getForceIndex_function_value - , ( bp::arg("name") ) - , bp::release_gil_policy() - , "Get the index of the force called name. Returns -1 if\n there is no force with this name\n" ); - + + typedef int (::SireOpenMM::LambdaLever::*getForceIndex_function_type)(::QString const &) const; + getForceIndex_function_type getForceIndex_function_value(&::SireOpenMM::LambdaLever::getForceIndex); + + LambdaLever_exposer.def( + "getForceIndex", getForceIndex_function_value, (bp::arg("name")), bp::release_gil_policy(), "Get the index of the force called name. Returns -1 if\n there is no force with this name\n"); } { //::SireOpenMM::LambdaLever::getForceType - - typedef ::QString ( ::SireOpenMM::LambdaLever::*getForceType_function_type)( ::QString const &,::OpenMM::System const & ) const; - getForceType_function_type getForceType_function_value( &::SireOpenMM::LambdaLever::getForceType ); - - LambdaLever_exposer.def( - "getForceType" - , getForceType_function_value - , ( bp::arg("name"), bp::arg("system") ) - , bp::release_gil_policy() - , "Get the C++ type of the force called name. Returns an\n empty string if there is no such force\n" ); - + + typedef ::QString (::SireOpenMM::LambdaLever::*getForceType_function_type)(::QString const &, ::OpenMM::System const &) const; + getForceType_function_type getForceType_function_value(&::SireOpenMM::LambdaLever::getForceType); + + LambdaLever_exposer.def( + "getForceType", getForceType_function_value, (bp::arg("name"), bp::arg("system")), bp::release_gil_policy(), "Get the C++ type of the force called name. Returns an\n empty string if there is no such force\n"); } { //::SireOpenMM::LambdaLever::getLeverValues - - typedef ::SireBase::PropertyList ( ::SireOpenMM::LambdaLever::*getLeverValues_function_type)( ::QVector< double > const &,::SireOpenMM::PerturbableOpenMMMolecule const & ) const; - getLeverValues_function_type getLeverValues_function_value( &::SireOpenMM::LambdaLever::getLeverValues ); - - LambdaLever_exposer.def( - "getLeverValues" - , getLeverValues_function_value - , ( bp::arg("lambda_values"), bp::arg("mol") ) - , bp::release_gil_policy() - , "Get all of the lever values that would be set for the passed\n lambda values using the current context. This returns a PropertyList\n of columns, where each column is a PropertyMap with the column name\n and either double or QString array property of values.\n\n This is designed to be used by a higher-level python function that\n will convert this output into, e.g. a pandas DataFrame\n" ); - + + typedef ::SireBase::PropertyList (::SireOpenMM::LambdaLever::*getLeverValues_function_type)(::QVector const &, ::SireOpenMM::PerturbableOpenMMMolecule const &) const; + getLeverValues_function_type getLeverValues_function_value(&::SireOpenMM::LambdaLever::getLeverValues); + + LambdaLever_exposer.def( + "getLeverValues", getLeverValues_function_value, (bp::arg("lambda_values"), bp::arg("mol")), bp::release_gil_policy(), "Get all of the lever values that would be set for the passed\n lambda values using the current context. This returns a PropertyList\n of columns, where each column is a PropertyMap with the column name\n and either double or QString array property of values.\n\n This is designed to be used by a higher-level python function that\n will convert this output into, e.g. a pandas DataFrame\n"); } { //::SireOpenMM::LambdaLever::getPerturbableMoleculeMaps - - typedef ::QHash< SireMol::MolNum, SireBase::PropertyMap > ( ::SireOpenMM::LambdaLever::*getPerturbableMoleculeMaps_function_type)( ) const; - getPerturbableMoleculeMaps_function_type getPerturbableMoleculeMaps_function_value( &::SireOpenMM::LambdaLever::getPerturbableMoleculeMaps ); - - LambdaLever_exposer.def( - "getPerturbableMoleculeMaps" - , getPerturbableMoleculeMaps_function_value - , bp::release_gil_policy() - , "Return all of the property maps used to find the perturbable properties\n of the perturbable molecules. This is indexed by molecule number\n" ); - + + typedef ::QHash (::SireOpenMM::LambdaLever::*getPerturbableMoleculeMaps_function_type)() const; + getPerturbableMoleculeMaps_function_type getPerturbableMoleculeMaps_function_value(&::SireOpenMM::LambdaLever::getPerturbableMoleculeMaps); + + LambdaLever_exposer.def( + "getPerturbableMoleculeMaps", getPerturbableMoleculeMaps_function_value, bp::release_gil_policy(), "Return all of the property maps used to find the perturbable properties\n of the perturbable molecules. This is indexed by molecule number\n"); } { //::SireOpenMM::LambdaLever::getRestraints - - typedef ::QList< OpenMM::Force * > ( ::SireOpenMM::LambdaLever::*getRestraints_function_type)( ::QString const &,::OpenMM::System & ) const; - getRestraints_function_type getRestraints_function_value( &::SireOpenMM::LambdaLever::getRestraints ); - - LambdaLever_exposer.def( - "getRestraints" - , getRestraints_function_value - , ( bp::arg("name"), bp::arg("system") ) - , bp::release_gil_policy() - , "Return the pointers to all of the forces from the passed System\n are restraints called restraint. This returns an empty\n list if there are no restraints with this name" ); - + + typedef ::QList (::SireOpenMM::LambdaLever::*getRestraints_function_type)(::QString const &, ::OpenMM::System &) const; + getRestraints_function_type getRestraints_function_value(&::SireOpenMM::LambdaLever::getRestraints); + + LambdaLever_exposer.def( + "getRestraints", getRestraints_function_value, (bp::arg("name"), bp::arg("system")), bp::release_gil_policy(), "Return the pointers to all of the forces from the passed System\n are restraints called restraint. This returns an empty\n list if there are no restraints with this name"); } { //::SireOpenMM::LambdaLever::getSchedule - - typedef ::SireCAS::LambdaSchedule ( ::SireOpenMM::LambdaLever::*getSchedule_function_type)( ) const; - getSchedule_function_type getSchedule_function_value( &::SireOpenMM::LambdaLever::getSchedule ); - - LambdaLever_exposer.def( - "getSchedule" - , getSchedule_function_value - , bp::release_gil_policy() - , "" ); - + + typedef ::SireCAS::LambdaSchedule (::SireOpenMM::LambdaLever::*getSchedule_function_type)() const; + getSchedule_function_type getSchedule_function_value(&::SireOpenMM::LambdaLever::getSchedule); + + LambdaLever_exposer.def( + "getSchedule", getSchedule_function_value, bp::release_gil_policy(), ""); } { //::SireOpenMM::LambdaLever::hasLever - - typedef bool ( ::SireOpenMM::LambdaLever::*hasLever_function_type)( ::QString const & ) ; - hasLever_function_type hasLever_function_value( &::SireOpenMM::LambdaLever::hasLever ); - - LambdaLever_exposer.def( - "hasLever" - , hasLever_function_value - , ( bp::arg("lever_name") ) - , bp::release_gil_policy() - , "" ); - + + typedef bool (::SireOpenMM::LambdaLever::*hasLever_function_type)(::QString const &); + hasLever_function_type hasLever_function_value(&::SireOpenMM::LambdaLever::hasLever); + + LambdaLever_exposer.def( + "hasLever", hasLever_function_value, (bp::arg("lever_name")), bp::release_gil_policy(), ""); } - LambdaLever_exposer.def( bp::self != bp::self ); + LambdaLever_exposer.def(bp::self != bp::self); { //::SireOpenMM::LambdaLever::operator= - - typedef ::SireOpenMM::LambdaLever & ( ::SireOpenMM::LambdaLever::*assign_function_type)( ::SireOpenMM::LambdaLever const & ) ; - assign_function_type assign_function_value( &::SireOpenMM::LambdaLever::operator= ); - - LambdaLever_exposer.def( - "assign" - , assign_function_value - , ( bp::arg("other") ) - , bp::return_self< >() - , "" ); - + + typedef ::SireOpenMM::LambdaLever &(::SireOpenMM::LambdaLever::*assign_function_type)(::SireOpenMM::LambdaLever const &); + assign_function_type assign_function_value(&::SireOpenMM::LambdaLever::operator=); + + LambdaLever_exposer.def( + "assign", assign_function_value, (bp::arg("other")), bp::return_self<>(), ""); } - LambdaLever_exposer.def( bp::self == bp::self ); + LambdaLever_exposer.def(bp::self == bp::self); { //::SireOpenMM::LambdaLever::setConstraintIndicies - - typedef void ( ::SireOpenMM::LambdaLever::*setConstraintIndicies_function_type)( int,::QVector< int > const & ) ; - setConstraintIndicies_function_type setConstraintIndicies_function_value( &::SireOpenMM::LambdaLever::setConstraintIndicies ); - - LambdaLever_exposer.def( - "setConstraintIndicies" - , setConstraintIndicies_function_value - , ( bp::arg("idx"), bp::arg("constraint_idxs") ) - , bp::release_gil_policy() - , "Set the constraint indicies for the perturbable molecule at\n index mol_idx\n" ); - + + typedef void (::SireOpenMM::LambdaLever::*setConstraintIndicies_function_type)(int, ::QVector const &); + setConstraintIndicies_function_type setConstraintIndicies_function_value(&::SireOpenMM::LambdaLever::setConstraintIndicies); + + LambdaLever_exposer.def( + "setConstraintIndicies", setConstraintIndicies_function_value, (bp::arg("idx"), bp::arg("constraint_idxs")), bp::release_gil_policy(), "Set the constraint indicies for the perturbable molecule at\n index mol_idx\n"); } { //::SireOpenMM::LambdaLever::setExceptionIndicies - - typedef void ( ::SireOpenMM::LambdaLever::*setExceptionIndicies_function_type)( int,::QString const &,::QVector< boost::tuples::tuple< int, int > > const & ) ; - setExceptionIndicies_function_type setExceptionIndicies_function_value( &::SireOpenMM::LambdaLever::setExceptionIndicies ); - - LambdaLever_exposer.def( - "setExceptionIndicies" - , setExceptionIndicies_function_value - , ( bp::arg("idx"), bp::arg("ff"), bp::arg("exception_idxs") ) - , bp::release_gil_policy() - , "Set the exception indices for the perturbable molecule at\n index mol_idx\n" ); - + + typedef void (::SireOpenMM::LambdaLever::*setExceptionIndicies_function_type)(int, ::QString const &, ::QVector> const &); + setExceptionIndicies_function_type setExceptionIndicies_function_value(&::SireOpenMM::LambdaLever::setExceptionIndicies); + + LambdaLever_exposer.def( + "setExceptionIndicies", setExceptionIndicies_function_value, (bp::arg("idx"), bp::arg("ff"), bp::arg("exception_idxs")), bp::release_gil_policy(), "Set the exception indices for the perturbable molecule at\n index mol_idx\n"); } { //::SireOpenMM::LambdaLever::setForceIndex - typedef void ( ::SireOpenMM::LambdaLever::*setForceIndex_function_type)( ::QString const &,int ) ; - setForceIndex_function_type setForceIndex_function_value( &::SireOpenMM::LambdaLever::setForceIndex ); + typedef void (::SireOpenMM::LambdaLever::*setForceIndex_function_type)(::QString const &, int); + setForceIndex_function_type setForceIndex_function_value(&::SireOpenMM::LambdaLever::setForceIndex); LambdaLever_exposer.def( - "setForceIndex" - , setForceIndex_function_value - , ( bp::arg("force"), bp::arg("index") ) - , bp::release_gil_policy() - , "Set the index of the force called force in the OpenMM System.\n There can only be one force with this name. Attempts to add\n a duplicate will cause an error to be raised.\n" ); - + "setForceIndex", setForceIndex_function_value, (bp::arg("force"), bp::arg("index")), bp::release_gil_policy(), "Set the index of the force called force in the OpenMM System.\n There can only be one force with this name. Attempts to add\n a duplicate will cause an error to be raised.\n"); } { //::SireOpenMM::LambdaLever::setForceGroup - typedef void ( ::SireOpenMM::LambdaLever::*setForceGroup_function_type)( ::QString const &,int ) ; - setForceGroup_function_type setForceGroup_function_value( &::SireOpenMM::LambdaLever::setForceGroup ); + typedef void (::SireOpenMM::LambdaLever::*setForceGroup_function_type)(::QString const &, int); + setForceGroup_function_type setForceGroup_function_value(&::SireOpenMM::LambdaLever::setForceGroup); LambdaLever_exposer.def( - "setForceGroup" - , setForceGroup_function_value - , ( bp::arg("name"), bp::arg("group_idx") ) - , bp::release_gil_policy() - , "Set the force group index for the named force." ); - + "setForceGroup", setForceGroup_function_value, (bp::arg("name"), bp::arg("group_idx")), bp::release_gil_policy(), "Set the force group index for the named force."); } { //::SireOpenMM::LambdaLever::getForceGroup - typedef int ( ::SireOpenMM::LambdaLever::*getForceGroup_function_type)( ::QString const & ) const; - getForceGroup_function_type getForceGroup_function_value( &::SireOpenMM::LambdaLever::getForceGroup ); + typedef int (::SireOpenMM::LambdaLever::*getForceGroup_function_type)(::QString const &) const; + getForceGroup_function_type getForceGroup_function_value(&::SireOpenMM::LambdaLever::getForceGroup); LambdaLever_exposer.def( - "getForceGroup" - , getForceGroup_function_value - , ( bp::arg("name") ) - , bp::release_gil_policy() - , "Get the force group index for the named force. Returns -1 if not found." ); - + "getForceGroup", getForceGroup_function_value, (bp::arg("name")), bp::release_gil_policy(), "Get the force group index for the named force. Returns -1 if not found."); } { //::SireOpenMM::LambdaLever::getForceNames - typedef ::QStringList ( ::SireOpenMM::LambdaLever::*getForceNames_function_type)( ) const; - getForceNames_function_type getForceNames_function_value( &::SireOpenMM::LambdaLever::getForceNames ); + typedef ::QStringList (::SireOpenMM::LambdaLever::*getForceNames_function_type)() const; + getForceNames_function_type getForceNames_function_value(&::SireOpenMM::LambdaLever::getForceNames); LambdaLever_exposer.def( - "getForceNames" - , getForceNames_function_value - , bp::release_gil_policy() - , "Return the names of all forces and restraints that have been assigned a force group index." ); - + "getForceNames", getForceNames_function_value, bp::release_gil_policy(), "Return the names of all forces and restraints that have been assigned a force group index."); } { //::SireOpenMM::LambdaLever::wasForceChanged - typedef bool ( ::SireOpenMM::LambdaLever::*wasForceChanged_function_type)( ::QString const & ) const; - wasForceChanged_function_type wasForceChanged_function_value( &::SireOpenMM::LambdaLever::wasForceChanged ); + typedef bool (::SireOpenMM::LambdaLever::*wasForceChanged_function_type)(::QString const &) const; + wasForceChanged_function_type wasForceChanged_function_value(&::SireOpenMM::LambdaLever::wasForceChanged); LambdaLever_exposer.def( - "wasForceChanged" - , wasForceChanged_function_value - , ( bp::arg("name") ) - , bp::release_gil_policy() - , "Return whether the named force had parameters changed in the last setLambda call." ); - + "wasForceChanged", wasForceChanged_function_value, (bp::arg("name")), bp::release_gil_policy(), "Return whether the named force had parameters changed in the last setLambda call."); } { //::SireOpenMM::LambdaLever::setLambda - - typedef double ( ::SireOpenMM::LambdaLever::*setLambda_function_type)( ::OpenMM::Context &,double,double,bool ) const; - setLambda_function_type setLambda_function_value( &::SireOpenMM::LambdaLever::setLambda ); - - LambdaLever_exposer.def( - "setLambda" - , setLambda_function_value - , ( bp::arg("system"), bp::arg("lambda_value"), bp::arg("rest2_scale")=(double)(1.0), bp::arg("update_constraints")=(bool)(true) ) - , "Set the value of lambda in the passed context. Returns the\n actual value of lambda set.\n" ); - + + LambdaLever_exposer.def( + "setLambda", &setLambda_no_gil, (bp::arg("system"), bp::arg("lambda_value"), bp::arg("rest2_scale") = (double)(1.0), bp::arg("update_constraints") = (bool)(true)), "Set the value of lambda in the passed context. Returns the\n actual value of lambda set.\n"); } { //::SireOpenMM::LambdaLever::setSchedule - - typedef void ( ::SireOpenMM::LambdaLever::*setSchedule_function_type)( ::SireCAS::LambdaSchedule const & ) ; - setSchedule_function_type setSchedule_function_value( &::SireOpenMM::LambdaLever::setSchedule ); - - LambdaLever_exposer.def( - "setSchedule" - , setSchedule_function_value - , ( bp::arg("schedule") ) - , bp::release_gil_policy() - , "" ); - + + typedef void (::SireOpenMM::LambdaLever::*setSchedule_function_type)(::SireCAS::LambdaSchedule const &); + setSchedule_function_type setSchedule_function_value(&::SireOpenMM::LambdaLever::setSchedule); + + LambdaLever_exposer.def( + "setSchedule", setSchedule_function_value, (bp::arg("schedule")), bp::release_gil_policy(), ""); } { //::SireOpenMM::LambdaLever::typeName - - typedef char const * ( *typeName_function_type )( ); - typeName_function_type typeName_function_value( &::SireOpenMM::LambdaLever::typeName ); - - LambdaLever_exposer.def( - "typeName" - , typeName_function_value - , bp::release_gil_policy() - , "" ); - + + typedef char const *(*typeName_function_type)(); + typeName_function_type typeName_function_value(&::SireOpenMM::LambdaLever::typeName); + + LambdaLever_exposer.def( + "typeName", typeName_function_value, bp::release_gil_policy(), ""); } { //::SireOpenMM::LambdaLever::what - - typedef char const * ( ::SireOpenMM::LambdaLever::*what_function_type)( ) const; - what_function_type what_function_value( &::SireOpenMM::LambdaLever::what ); - - LambdaLever_exposer.def( - "what" - , what_function_value - , bp::release_gil_policy() - , "" ); - - } - LambdaLever_exposer.staticmethod( "typeName" ); - LambdaLever_exposer.def( "__copy__", &__copy__); - LambdaLever_exposer.def( "__deepcopy__", &__copy__); - LambdaLever_exposer.def_pickle(sire_pickle_suite< ::SireOpenMM::LambdaLever >()); - LambdaLever_exposer.def( "clone", &__copy__); - LambdaLever_exposer.def( "__str__", &__str__< ::SireOpenMM::LambdaLever > ); - LambdaLever_exposer.def( "__repr__", &__str__< ::SireOpenMM::LambdaLever > ); - } + typedef char const *(::SireOpenMM::LambdaLever::*what_function_type)() const; + what_function_type what_function_value(&::SireOpenMM::LambdaLever::what); + + LambdaLever_exposer.def( + "what", what_function_value, bp::release_gil_policy(), ""); + } + LambdaLever_exposer.staticmethod("typeName"); + LambdaLever_exposer.def("__copy__", &__copy__); + LambdaLever_exposer.def("__deepcopy__", &__copy__); + LambdaLever_exposer.def_pickle(sire_pickle_suite<::SireOpenMM::LambdaLever>()); + LambdaLever_exposer.def("clone", &__copy__); + LambdaLever_exposer.def("__str__", &__str__<::SireOpenMM::LambdaLever>); + LambdaLever_exposer.def("__repr__", &__str__<::SireOpenMM::LambdaLever>); + } } diff --git a/wrapper/Helpers/CMakeLists.txt b/wrapper/Helpers/CMakeLists.txt index 7bba26784..d3f34abce 100644 --- a/wrapper/Helpers/CMakeLists.txt +++ b/wrapper/Helpers/CMakeLists.txt @@ -23,6 +23,7 @@ set ( WRAPHELPERS_SOURCES release_gil_policy.hpp release_gil_policy.cpp + scoped_gil_release.hpp ) diff --git a/wrapper/Helpers/release_gil_policy.cpp b/wrapper/Helpers/release_gil_policy.cpp index 3ed7d4a5c..15789aa97 100644 --- a/wrapper/Helpers/release_gil_policy.cpp +++ b/wrapper/Helpers/release_gil_policy.cpp @@ -5,102 +5,119 @@ #include +// This disables release_gil_policy everywhere: GilHolder is defined only here, +// so every wrapper module links this neutered version and all of the +// bp::release_gil_policy() annotations are inert. Leave it defined. +// +// The policy cannot work as written. boost/python/detail/caller.hpp calls +// precall (which releases the GIL), then detail::invoke, then postcall (which +// restores it) - and detail::invoke runs the result converter, so any wrapped +// function returning a converted value builds a Python object with the GIL +// released. Enabling this segfaults during "import sire", in +// _fix_atomproperty_types. Patching individual converters to re-acquire does +// not help, as default_result_converter uses boost's own machinery. +// +// To release the GIL for a specific hot function, use +// Helpers/scoped_gil_release.hpp, which releases inside the wrapped call so +// that argument and result conversion still hold the GIL. #define SIRE_DISABLE_GIL_POLICY 1 -//#define SIRE_PRINT_GIL_STATUS 1 +// #define SIRE_PRINT_GIL_STATUS 1 boost::python::detail::GilHolder::GilHolder() : thread_state(0) { - #ifndef SIRE_DISABLE_GIL_POLICY - #ifdef SIRE_PRINT_GIL_STATUS - qDebug() << "--RELEASE GIL"; - //for (const auto &bt : SireError::getBackTrace()) - //{ - // qDebug() << bt; - //} - #endif - thread_state = PyEval_SaveThread(); - #endif +#ifndef SIRE_DISABLE_GIL_POLICY +#ifdef SIRE_PRINT_GIL_STATUS + qDebug() << "--RELEASE GIL"; + // for (const auto &bt : SireError::getBackTrace()) + //{ + // qDebug() << bt; + // } +#endif + thread_state = PyEval_SaveThread(); +#endif } boost::python::detail::GilHolder::~GilHolder() { - #ifndef SIRE_DISABLE_GIL_POLICY - if (thread_state) +#ifndef SIRE_DISABLE_GIL_POLICY + if (thread_state) + { + if (_Py_IsFinalizing()) + { + qDebug() << "FINALIZING!"; + } + else { - if (_Py_IsFinalizing()) - { - qDebug() << "FINALIZING!"; - } - else - { - #ifdef SIRE_PRINT_GIL_STATUS - qDebug() << "--ACQUIRE GIL"; - #endif - PyEval_RestoreThread(thread_state); - } +#ifdef SIRE_PRINT_GIL_STATUS + qDebug() << "--ACQUIRE GIL"; +#endif + PyEval_RestoreThread(thread_state); } - #endif + } +#endif } boost::python::detail::GilRaiiData::GilRaiiData() -{} +{ +} boost::python::detail::GilRaiiData::~GilRaiiData() { - #ifndef SIRE_DISABLE_GIL_POLICY - if (boost::python::release_gil_policy::gil.hasLocalData()) - { - qDebug() << "WARNING - DOUBLE HOLD GIL - POTENTIAL FOR DEADLOCK!"; - } - else - { - boost::python::release_gil_policy::gil.setLocalData(new boost::python::detail::GilHolder()); - } - #endif +#ifndef SIRE_DISABLE_GIL_POLICY + if (boost::python::release_gil_policy::gil.hasLocalData()) + { + qDebug() << "WARNING - DOUBLE HOLD GIL - POTENTIAL FOR DEADLOCK!"; + } + else + { + boost::python::release_gil_policy::gil.setLocalData(new boost::python::detail::GilHolder()); + } +#endif } boost::python::GilRaii::GilRaii(bool acquired) { - #ifndef SIRE_DISABLE_GIL_POLICY - if (acquired) - { - d.reset(new boost::python::detail::GilRaiiData()); - } - #endif +#ifndef SIRE_DISABLE_GIL_POLICY + if (acquired) + { + d.reset(new boost::python::detail::GilRaiiData()); + } +#endif } boost::python::GilRaii::~GilRaii() -{} +{ +} /** Acquire the GIL, returning a RAII object which will release * the GIL when it is destroyed */ boost::python::GilRaii boost::python::release_gil_policy::acquire_gil() { - #ifdef SIRE_DISABLE_GIL_POLICY - return boost::python::GilRaii(false); - #else - if (gil.hasLocalData()) - { - gil.setLocalData(0); - return GilRaii(true); - } - else - { - return GilRaii(false); - } - #endif +#ifdef SIRE_DISABLE_GIL_POLICY + return boost::python::GilRaii(false); +#else + if (gil.hasLocalData()) + { + gil.setLocalData(0); + return GilRaii(true); + } + else + { + return GilRaii(false); + } +#endif } /** Acquire the GIL without handling via a RAII object */ void boost::python::release_gil_policy::acquire_gil_no_raii() { - #ifndef SIRE_DISABLE_GIL_POLICY - if (gil.hasLocalData()) - { - gil.setLocalData(0); - } - #endif +#ifndef SIRE_DISABLE_GIL_POLICY + if (gil.hasLocalData()) + { + gil.setLocalData(0); + } +#endif } /** Release the GIL without handling via a RAII object. You will @@ -109,15 +126,15 @@ void boost::python::release_gil_policy::acquire_gil_no_raii() */ void boost::python::release_gil_policy::release_gil_no_raii() { - #ifndef SIRE_DISABLE_GIL_POLICY - if (boost::python::release_gil_policy::gil.hasLocalData()) - { - qDebug() << "WARNING - DOUBLE HOLD GIL - POTENTIAL FOR DEADLOCK!"; - return; - } +#ifndef SIRE_DISABLE_GIL_POLICY + if (boost::python::release_gil_policy::gil.hasLocalData()) + { + qDebug() << "WARNING - DOUBLE HOLD GIL - POTENTIAL FOR DEADLOCK!"; + return; + } - boost::python::release_gil_policy::gil.setLocalData(new boost::python::detail::GilHolder()); - #endif + boost::python::release_gil_policy::gil.setLocalData(new boost::python::detail::GilHolder()); +#endif } void boost::python::release_gil_policy::_precall() @@ -133,4 +150,4 @@ void boost::python::release_gil_policy::_postcall() } } -QThreadStorage boost::python::release_gil_policy::gil; +QThreadStorage boost::python::release_gil_policy::gil; diff --git a/wrapper/Helpers/scoped_gil_release.hpp b/wrapper/Helpers/scoped_gil_release.hpp new file mode 100644 index 000000000..abf2508ba --- /dev/null +++ b/wrapper/Helpers/scoped_gil_release.hpp @@ -0,0 +1,35 @@ +#ifndef _HELPERS_SCOPED_GIL_RELEASE_HPP_ +#define _HELPERS_SCOPED_GIL_RELEASE_HPP_ + +#include "boost/python.hpp" + +namespace SireHelpers +{ + /** Release the GIL for the lifetime of this object. + * + * Use this in a hand-written wrapper function for a call that is hot, + * long-running and never re-enters Python. Unlike bp::release_gil_policy, + * the GIL is restored during stack unwinding, so this is safe for + * functions that throw and for functions with default arguments. + */ + class ScopedGILRelease + { + public: + ScopedGILRelease() : thread_state(PyEval_SaveThread()) + { + } + + ~ScopedGILRelease() + { + PyEval_RestoreThread(thread_state); + } + + private: + ScopedGILRelease(const ScopedGILRelease &); + ScopedGILRelease &operator=(const ScopedGILRelease &); + + PyThreadState *thread_state; + }; +} + +#endif