From f6432e1498300956ededaceb6f542ecb03e3f452 Mon Sep 17 00:00:00 2001 From: Strahinja Val Markovic Date: Fri, 11 Oct 2013 19:27:04 -0700 Subject: [PATCH] Releasing Python's GIL in C++ code where possible Without this, all requests to the server become effectively serialized. --- cpp/ycm/ClangCompleter/ClangCompleter.cpp | 7 ++++ .../ClangCompleter/CompilationDatabase.cpp | 2 + cpp/ycm/IdentifierCompleter.cpp | 5 +++ cpp/ycm/PythonSupport.cpp | 34 ++++++++------- cpp/ycm/ReleaseGil.h | 42 +++++++++++++++++++ cpp/ycm/tests/CMakeLists.txt | 2 +- cpp/ycm/tests/main.cpp | 13 ++++++ cpp/ycm/ycm_core.cpp | 5 ++- python/ycm/base.py | 2 +- 9 files changed, 94 insertions(+), 18 deletions(-) create mode 100644 cpp/ycm/ReleaseGil.h create mode 100644 cpp/ycm/tests/main.cpp diff --git a/cpp/ycm/ClangCompleter/ClangCompleter.cpp b/cpp/ycm/ClangCompleter/ClangCompleter.cpp index a8497293..7c4a74ab 100644 --- a/cpp/ycm/ClangCompleter/ClangCompleter.cpp +++ b/cpp/ycm/ClangCompleter/ClangCompleter.cpp @@ -25,6 +25,7 @@ #include "CompletionData.h" #include "Utils.h" #include "ClangUtils.h" +#include "ReleaseGil.h" #include #include @@ -59,6 +60,7 @@ ClangCompleter::~ClangCompleter() { bool ClangCompleter::UpdatingTranslationUnit( const std::string &filename ) { + ReleaseGil unlock; shared_ptr< TranslationUnit > unit = translation_unit_store_.Get( filename ); if ( !unit ) @@ -75,6 +77,7 @@ std::vector< Diagnostic > ClangCompleter::UpdateTranslationUnit( const std::string &filename, const std::vector< UnsavedFile > &unsaved_files, const std::vector< std::string > &flags ) { + ReleaseGil unlock; bool translation_unit_created; shared_ptr< TranslationUnit > unit = translation_unit_store_.GetOrCreate( filename, @@ -111,6 +114,7 @@ ClangCompleter::CandidatesForLocationInFile( int column, const std::vector< UnsavedFile > &unsaved_files, const std::vector< std::string > &flags ) { + ReleaseGil unlock; shared_ptr< TranslationUnit > unit = translation_unit_store_.GetOrCreate( filename, unsaved_files, flags ); @@ -129,6 +133,7 @@ Location ClangCompleter::GetDeclarationLocation( int column, const std::vector< UnsavedFile > &unsaved_files, const std::vector< std::string > &flags ) { + ReleaseGil unlock; shared_ptr< TranslationUnit > unit = translation_unit_store_.GetOrCreate( filename, unsaved_files, flags ); @@ -146,6 +151,7 @@ Location ClangCompleter::GetDefinitionLocation( int column, const std::vector< UnsavedFile > &unsaved_files, const std::vector< std::string > &flags ) { + ReleaseGil unlock; shared_ptr< TranslationUnit > unit = translation_unit_store_.GetOrCreate( filename, unsaved_files, flags ); @@ -158,6 +164,7 @@ Location ClangCompleter::GetDefinitionLocation( void ClangCompleter::DeleteCachesForFile( const std::string &filename ) { + ReleaseGil unlock; translation_unit_store_.Remove( filename ); } diff --git a/cpp/ycm/ClangCompleter/CompilationDatabase.cpp b/cpp/ycm/ClangCompleter/CompilationDatabase.cpp index 7a3b5f43..c1f94694 100644 --- a/cpp/ycm/ClangCompleter/CompilationDatabase.cpp +++ b/cpp/ycm/ClangCompleter/CompilationDatabase.cpp @@ -18,6 +18,7 @@ #include "CompilationDatabase.h" #include "ClangUtils.h" #include "standard.h" +#include "ReleaseGil.h" #include #include @@ -58,6 +59,7 @@ bool CompilationDatabase::DatabaseSuccessfullyLoaded() { CompilationInfoForFile CompilationDatabase::GetCompilationInfoForFile( const std::string &path_to_file ) { + ReleaseGil unlock; CompilationInfoForFile info; if ( !is_loaded_ ) diff --git a/cpp/ycm/IdentifierCompleter.cpp b/cpp/ycm/IdentifierCompleter.cpp index 36ca2436..a6bd2674 100644 --- a/cpp/ycm/IdentifierCompleter.cpp +++ b/cpp/ycm/IdentifierCompleter.cpp @@ -22,6 +22,7 @@ #include "IdentifierUtils.h" #include "Result.h" #include "Utils.h" +#include "ReleaseGil.h" #include @@ -49,6 +50,7 @@ void IdentifierCompleter::AddIdentifiersToDatabase( const std::vector< std::string > &new_candidates, const std::string &filetype, const std::string &filepath ) { + ReleaseGil unlock; identifier_database_.AddIdentifiers( new_candidates, filetype, filepath ); @@ -57,6 +59,7 @@ void IdentifierCompleter::AddIdentifiersToDatabase( void IdentifierCompleter::AddIdentifiersToDatabaseFromTagFiles( const std::vector< std::string > &absolute_paths_to_tag_files ) { + ReleaseGil unlock; foreach( const std::string & path, absolute_paths_to_tag_files ) { identifier_database_.AddIdentifiers( ExtractIdentifiersFromTagsFile( path ) ); @@ -69,6 +72,7 @@ void IdentifierCompleter::AddIdentifiersToDatabaseFromBuffer( const std::string &filetype, const std::string &filepath, bool collect_from_comments_and_strings ) { + ReleaseGil unlock; identifier_database_.ClearCandidatesStoredForFile( filetype, filepath ); std::string new_contents = @@ -92,6 +96,7 @@ std::vector< std::string > IdentifierCompleter::CandidatesForQuery( std::vector< std::string > IdentifierCompleter::CandidatesForQueryAndType( const std::string &query, const std::string &filetype ) const { + ReleaseGil unlock; std::vector< Result > results; identifier_database_.ResultsForQueryAndType( query, filetype, results ); diff --git a/cpp/ycm/PythonSupport.cpp b/cpp/ycm/PythonSupport.cpp index 7b94a7a3..1b14cd63 100644 --- a/cpp/ycm/PythonSupport.cpp +++ b/cpp/ycm/PythonSupport.cpp @@ -20,6 +20,8 @@ #include "Result.h" #include "Candidate.h" #include "CandidateRepository.h" +#include "ReleaseGil.h" + #include #include #include @@ -76,31 +78,33 @@ boost::python::list FilterAndSortCandidates( return candidates; } + int num_candidates = len( candidates ); std::vector< const Candidate * > repository_candidates = CandidatesFromObjectList( candidates, candidate_property ); - Bitset query_bitset = LetterBitsetFromString( query ); - bool query_has_uppercase_letters = any_of( query, is_upper() ); - - int num_candidates = len( candidates ); std::vector< ResultAnd< int > > object_and_results; + { + ReleaseGil unlock; + Bitset query_bitset = LetterBitsetFromString( query ); + bool query_has_uppercase_letters = any_of( query, is_upper() ); - for ( int i = 0; i < num_candidates; ++i ) { - const Candidate *candidate = repository_candidates[ i ]; + for ( int i = 0; i < num_candidates; ++i ) { + const Candidate *candidate = repository_candidates[ i ]; - if ( !candidate->MatchesQueryBitset( query_bitset ) ) - continue; + if ( !candidate->MatchesQueryBitset( query_bitset ) ) + continue; - Result result = candidate->QueryMatchResult( query, - query_has_uppercase_letters ); + Result result = candidate->QueryMatchResult( query, + query_has_uppercase_letters ); - if ( result.IsSubsequence() ) { - ResultAnd< int > object_and_result( i, result ); - object_and_results.push_back( boost::move( object_and_result ) ); + if ( result.IsSubsequence() ) { + ResultAnd< int > object_and_result( i, result ); + object_and_results.push_back( boost::move( object_and_result ) ); + } } - } - std::sort( object_and_results.begin(), object_and_results.end() ); + std::sort( object_and_results.begin(), object_and_results.end() ); + } foreach ( const ResultAnd< int > &object_and_result, object_and_results ) { diff --git a/cpp/ycm/ReleaseGil.h b/cpp/ycm/ReleaseGil.h new file mode 100644 index 00000000..c22de2be --- /dev/null +++ b/cpp/ycm/ReleaseGil.h @@ -0,0 +1,42 @@ +// Copyright (C) 2013 Strahinja Val Markovic +// +// This file is part of YouCompleteMe. +// +// YouCompleteMe is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// YouCompleteMe is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with YouCompleteMe. If not, see . + +#ifndef RELEASEGIL_H_RDIEBSQ1 +#define RELEASEGIL_H_RDIEBSQ1 + +#include + +namespace YouCompleteMe { + +class ReleaseGil { +public: + ReleaseGil() { + thread_state_ = PyEval_SaveThread(); + } + + ~ReleaseGil() { + PyEval_RestoreThread( thread_state_ ); + } + +private: + PyThreadState *thread_state_; +}; + +} // namespace YouCompleteMe + +#endif /* end of include guard: RELEASEGIL_H_RDIEBSQ1 */ + diff --git a/cpp/ycm/tests/CMakeLists.txt b/cpp/ycm/tests/CMakeLists.txt index c4d8aa7c..951c0aba 100644 --- a/cpp/ycm/tests/CMakeLists.txt +++ b/cpp/ycm/tests/CMakeLists.txt @@ -68,7 +68,7 @@ add_executable( ${PROJECT_NAME} target_link_libraries( ${PROJECT_NAME} ycm_core - gmock_main ) + gmock ) if ( NOT CMAKE_GENERATOR_IS_XCODE ) diff --git a/cpp/ycm/tests/main.cpp b/cpp/ycm/tests/main.cpp new file mode 100644 index 00000000..62c5198c --- /dev/null +++ b/cpp/ycm/tests/main.cpp @@ -0,0 +1,13 @@ +#include "gtest/gtest.h" +#include "gmock/gmock.h" +#include + +int main( int argc, char **argv ) { + Py_Initialize(); + // Necessary because of usage of the ReleaseGil class + PyEval_InitThreads(); + + testing::InitGoogleMock(&argc, argv); + return RUN_ALL_TESTS(); +} + diff --git a/cpp/ycm/ycm_core.cpp b/cpp/ycm/ycm_core.cpp index ba348bcc..d252e1ee 100644 --- a/cpp/ycm/ycm_core.cpp +++ b/cpp/ycm/ycm_core.cpp @@ -45,7 +45,7 @@ int YcmCoreVersion() { // We increment this every time when we want to force users to recompile // ycm_core. - return 5; + return 6; } @@ -54,6 +54,9 @@ BOOST_PYTHON_MODULE(ycm_core) using namespace boost::python; using namespace YouCompleteMe; + // Necessary because of usage of the ReleaseGil class + PyEval_InitThreads(); + def( "HasClangSupport", HasClangSupport ); def( "FilterAndSortCandidates", FilterAndSortCandidates ); def( "YcmCoreVersion", YcmCoreVersion ); diff --git a/python/ycm/base.py b/python/ycm/base.py index dfcb5f7b..8bf94784 100644 --- a/python/ycm/base.py +++ b/python/ycm/base.py @@ -152,7 +152,7 @@ def AdjustCandidateInsertionText( candidates ): return new_candidates -COMPATIBLE_WITH_CORE_VERSION = 5 +COMPATIBLE_WITH_CORE_VERSION = 6 def CompatibleWithYcmCore(): try: