فهرست منبع

Releasing Python's GIL in C++ code where possible

Without this, all requests to the server become effectively serialized.
Strahinja Val Markovic 11 سال پیش
والد
کامیت
f6432e1498

+ 7 - 0
cpp/ycm/ClangCompleter/ClangCompleter.cpp

@@ -25,6 +25,7 @@
 #include "CompletionData.h"
 #include "Utils.h"
 #include "ClangUtils.h"
+#include "ReleaseGil.h"
 
 #include <clang-c/Index.h>
 #include <boost/shared_ptr.hpp>
@@ -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 );
 }
 

+ 2 - 0
cpp/ycm/ClangCompleter/CompilationDatabase.cpp

@@ -18,6 +18,7 @@
 #include "CompilationDatabase.h"
 #include "ClangUtils.h"
 #include "standard.h"
+#include "ReleaseGil.h"
 
 #include <boost/shared_ptr.hpp>
 #include <boost/make_shared.hpp>
@@ -58,6 +59,7 @@ bool CompilationDatabase::DatabaseSuccessfullyLoaded() {
 
 CompilationInfoForFile CompilationDatabase::GetCompilationInfoForFile(
   const std::string &path_to_file ) {
+  ReleaseGil unlock;
   CompilationInfoForFile info;
 
   if ( !is_loaded_ )

+ 5 - 0
cpp/ycm/IdentifierCompleter.cpp

@@ -22,6 +22,7 @@
 #include "IdentifierUtils.h"
 #include "Result.h"
 #include "Utils.h"
+#include "ReleaseGil.h"
 
 #include <algorithm>
 
@@ -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 );
 

+ 19 - 15
cpp/ycm/PythonSupport.cpp

@@ -20,6 +20,8 @@
 #include "Result.h"
 #include "Candidate.h"
 #include "CandidateRepository.h"
+#include "ReleaseGil.h"
+
 #include <boost/algorithm/string.hpp>
 #include <boost/algorithm/cxx11/any_of.hpp>
 #include <vector>
@@ -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 ) {

+ 42 - 0
cpp/ycm/ReleaseGil.h

@@ -0,0 +1,42 @@
+// Copyright (C) 2013  Strahinja Val Markovic  <val@markovic.io>
+//
+// 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 <http://www.gnu.org/licenses/>.
+
+#ifndef RELEASEGIL_H_RDIEBSQ1
+#define RELEASEGIL_H_RDIEBSQ1
+
+#include <boost/python.hpp>
+
+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 */
+

+ 1 - 1
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 )

+ 13 - 0
cpp/ycm/tests/main.cpp

@@ -0,0 +1,13 @@
+#include "gtest/gtest.h"
+#include "gmock/gmock.h"
+#include <boost/python.hpp>
+
+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();
+}
+

+ 4 - 1
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 );

+ 1 - 1
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: