diff --git a/cpp/ycm/ClangCompleter/ClangCompleter.cpp b/cpp/ycm/ClangCompleter/ClangCompleter.cpp index c83c4d6a..88404564 100644 --- a/cpp/ycm/ClangCompleter/ClangCompleter.cpp +++ b/cpp/ycm/ClangCompleter/ClangCompleter.cpp @@ -140,6 +140,9 @@ bool ClangCompleter::UpdatingTranslationUnit( const std::string &filename ) { if ( !unit ) return false; + // Thankfully, an invalid, sentinel TU always returns true for + // IsCurrentlyUpdating, so no caller will try to rely on the TU object, even + // if unit is currently pointing to a sentinel. return unit->IsCurrentlyUpdating(); } @@ -366,14 +369,23 @@ shared_ptr< TranslationUnit > ClangCompleter::GetTranslationUnitForFile( translation_unit_created = false; return it->second; } + + // We create and store an invalid, sentinel TU so that other threads don't + // try to create a TU for the same file while we are trying to create this + // TU object. When we are done creating the TU, we will overwrite this value + // with the valid object. + filename_to_translation_unit_[ filename ] = + make_shared< TranslationUnit >(); } shared_ptr< TranslationUnit > unit; + try { unit = make_shared< TranslationUnit >( filename, unsaved_files, flags, clang_index_ ); } catch ( ClangParseError & ) { + Erase( filename_to_translation_unit_, filename ); return unit; } diff --git a/cpp/ycm/ClangCompleter/TranslationUnit.cpp b/cpp/ycm/ClangCompleter/TranslationUnit.cpp index 4f54a4db..c33bfb1e 100644 --- a/cpp/ycm/ClangCompleter/TranslationUnit.cpp +++ b/cpp/ycm/ClangCompleter/TranslationUnit.cpp @@ -36,6 +36,11 @@ namespace YouCompleteMe { typedef shared_ptr < remove_pointer< CXCodeCompleteResults >::type > CodeCompleteResultsWrap; +TranslationUnit::TranslationUnit() + : filename_(""), + clang_translation_unit_( NULL ) { +} + TranslationUnit::TranslationUnit( const std::string &filename, const std::vector< UnsavedFile > &unsaved_files, @@ -85,6 +90,10 @@ void TranslationUnit::Destroy() { std::vector< Diagnostic > TranslationUnit::LatestDiagnostics() { std::vector< Diagnostic > diagnostics; + + if ( !clang_translation_unit_ ) + return diagnostics; + unique_lock< mutex > lock( diagnostics_mutex_ ); // We don't need the latest diags after we return them once so we swap the @@ -103,6 +112,11 @@ std::vector< Diagnostic > TranslationUnit::LatestDiagnostics() { bool TranslationUnit::IsCurrentlyUpdating() const { + // We return true when the TU is invalid; an invalid TU also acts a sentinel, + // preventing other threads from trying to use it. + if ( !clang_translation_unit_ ) + return true; + unique_lock< mutex > lock( clang_access_mutex_, try_to_lock_t() ); return !lock.owns_lock(); } diff --git a/cpp/ycm/ClangCompleter/TranslationUnit.h b/cpp/ycm/ClangCompleter/TranslationUnit.h index 8d0834d8..313e7d60 100644 --- a/cpp/ycm/ClangCompleter/TranslationUnit.h +++ b/cpp/ycm/ClangCompleter/TranslationUnit.h @@ -41,6 +41,11 @@ typedef boost::shared_ptr< std::vector< CompletionData > > AsyncCompletions; class TranslationUnit : boost::noncopyable { public: + // This constructor creates an invalid, sentinel TU. All of it's methods + // return empty vectors, and IsCurrentlyUpdating always returns true so that + // no callers try to rely on the invalid TU. + TranslationUnit(); + TranslationUnit( const std::string &filename, const std::vector< UnsavedFile > &unsaved_files,