mirror of
				https://github.com/MaskRay/ccls.git
				synced 2025-11-04 06:15:20 +00:00 
			
		
		
		
	Some import pipeline refactors
This commit is contained in:
		
							parent
							
								
									819f5198de
								
							
						
					
					
						commit
						a1ae100478
					
				@ -150,14 +150,15 @@ ShouldParse FileNeedsParse(
 | 
			
		||||
  // File has been changed.
 | 
			
		||||
  if (!last_cached_modification ||
 | 
			
		||||
      modification_timestamp != *last_cached_modification) {
 | 
			
		||||
    file_consumer_shared->Reset(path);
 | 
			
		||||
    LOG_S(INFO) << "Timestamp has changed for " << path << unwrap_opt(from);
 | 
			
		||||
    file_consumer_shared->Reset(path);
 | 
			
		||||
    return ShouldParse::Yes;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Command-line arguments changed.
 | 
			
		||||
  if (opt_previous_index && opt_previous_index->args != args) {
 | 
			
		||||
    LOG_S(INFO) << "Arguments have changed for " << path << unwrap_opt(from);
 | 
			
		||||
    file_consumer_shared->Reset(path);
 | 
			
		||||
    return ShouldParse::Yes;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -165,18 +166,106 @@ ShouldParse FileNeedsParse(
 | 
			
		||||
  return ShouldParse::No;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
std::vector<Index_DoIdMap> ParseFile(
 | 
			
		||||
    Config* config,
 | 
			
		||||
    WorkingFiles* working_files,
 | 
			
		||||
enum CacheLoadResult { Parse, DoNotParse };
 | 
			
		||||
CacheLoadResult TryLoadFromCache(
 | 
			
		||||
    FileConsumerSharedState* file_consumer_shared,
 | 
			
		||||
    TimestampManager* timestamp_manager,
 | 
			
		||||
    IModificationTimestampFetcher* modification_timestamp_fetcher,
 | 
			
		||||
    ImportManager* import_manager,
 | 
			
		||||
    ICacheManager* cache_manager,
 | 
			
		||||
    IIndexer* indexer,
 | 
			
		||||
    bool is_interactive,
 | 
			
		||||
    const Project::Entry& entry,
 | 
			
		||||
    const std::string& entry_contents) {
 | 
			
		||||
    const std::string& path_to_index) {
 | 
			
		||||
  // Always run this block, even if we are interactive, so we can check
 | 
			
		||||
  // dependencies and reset files in |file_consumer_shared|.
 | 
			
		||||
  IndexFile* previous_index = cache_manager->TryLoad(path_to_index);
 | 
			
		||||
  if (!previous_index)
 | 
			
		||||
    return CacheLoadResult::Parse;
 | 
			
		||||
 | 
			
		||||
  // If none of the dependencies have changed and the index is not
 | 
			
		||||
  // interactive (ie, requested by a file save), skip parsing and just load
 | 
			
		||||
  // from cache.
 | 
			
		||||
 | 
			
		||||
  // Check timestamps and update |file_consumer_shared|.
 | 
			
		||||
  ShouldParse path_state = FileNeedsParse(
 | 
			
		||||
      is_interactive, timestamp_manager, modification_timestamp_fetcher,
 | 
			
		||||
      import_manager, cache_manager, file_consumer_shared, previous_index,
 | 
			
		||||
      path_to_index, entry.args, nullopt);
 | 
			
		||||
 | 
			
		||||
  // Target file does not exist on disk, do not emit any indexes.
 | 
			
		||||
  // TODO: Dependencies should be reassigned to other files. We can do this by
 | 
			
		||||
  // updating the "primary_file" if it doesn't exist. Might not actually be a
 | 
			
		||||
  // problem in practice.
 | 
			
		||||
  if (path_state == ShouldParse::NoSuchFile)
 | 
			
		||||
    return CacheLoadResult::DoNotParse;
 | 
			
		||||
 | 
			
		||||
  bool needs_reparse = is_interactive || path_state == ShouldParse::Yes;
 | 
			
		||||
 | 
			
		||||
  for (const std::string& dependency : previous_index->dependencies) {
 | 
			
		||||
    assert(!dependency.empty());
 | 
			
		||||
 | 
			
		||||
    // note: Use != as there are multiple failure results for FileParseQuery.
 | 
			
		||||
    if (FileNeedsParse(
 | 
			
		||||
            is_interactive, timestamp_manager, modification_timestamp_fetcher,
 | 
			
		||||
            import_manager, cache_manager, file_consumer_shared, previous_index,
 | 
			
		||||
            dependency, entry.args, previous_index->path) != ShouldParse::No) {
 | 
			
		||||
      needs_reparse = true;
 | 
			
		||||
      // SUBTLE: Do not break here, as |file_consumer_shared| is updated
 | 
			
		||||
      // inside of |file_needs_parse|.
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // FIXME: should we still load from cache?
 | 
			
		||||
  if (needs_reparse)
 | 
			
		||||
    return CacheLoadResult::Parse;
 | 
			
		||||
 | 
			
		||||
  // No timestamps changed - load directly from cache.
 | 
			
		||||
  LOG_S(INFO) << "Skipping parse; no timestamp change for " << path_to_index;
 | 
			
		||||
 | 
			
		||||
  // TODO/FIXME: real perf
 | 
			
		||||
  PerformanceImportFile perf;
 | 
			
		||||
 | 
			
		||||
  std::vector<Index_DoIdMap> result;
 | 
			
		||||
  result.push_back(Index_DoIdMap(cache_manager->TakeOrLoad(path_to_index), perf,
 | 
			
		||||
                                 is_interactive, false /*write_to_disk*/));
 | 
			
		||||
  for (const std::string& dependency : previous_index->dependencies) {
 | 
			
		||||
    // Only load a dependency if it is not already loaded.
 | 
			
		||||
    //
 | 
			
		||||
    // This is important for perf in large projects where there are lots of
 | 
			
		||||
    // dependencies shared between many files.
 | 
			
		||||
    if (!file_consumer_shared->Mark(dependency))
 | 
			
		||||
      continue;
 | 
			
		||||
 | 
			
		||||
    LOG_S(INFO) << "Emitting index result for " << dependency << " (via "
 | 
			
		||||
                << previous_index->path << ")";
 | 
			
		||||
 | 
			
		||||
    std::unique_ptr<IndexFile> dependency_index =
 | 
			
		||||
        cache_manager->TryTakeOrLoad(dependency);
 | 
			
		||||
 | 
			
		||||
    // |dependency_index| may be null if there is no cache for it but
 | 
			
		||||
    // another file has already started importing it.
 | 
			
		||||
    if (!dependency_index)
 | 
			
		||||
      continue;
 | 
			
		||||
 | 
			
		||||
    result.push_back(Index_DoIdMap(std::move(dependency_index), perf,
 | 
			
		||||
                                   is_interactive, false /*write_to_disk*/));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  QueueManager::instance()->do_id_map.EnqueueAll(std::move(result));
 | 
			
		||||
  return CacheLoadResult::DoNotParse;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ParseFile(Config* config,
 | 
			
		||||
               WorkingFiles* working_files,
 | 
			
		||||
               FileConsumerSharedState* file_consumer_shared,
 | 
			
		||||
               TimestampManager* timestamp_manager,
 | 
			
		||||
               IModificationTimestampFetcher* modification_timestamp_fetcher,
 | 
			
		||||
               ImportManager* import_manager,
 | 
			
		||||
               ICacheManager* cache_manager,
 | 
			
		||||
               IIndexer* indexer,
 | 
			
		||||
               bool is_interactive,
 | 
			
		||||
               const Project::Entry& entry,
 | 
			
		||||
               const std::string& entry_contents) {
 | 
			
		||||
  FileContents contents(entry.filename, entry_contents);
 | 
			
		||||
 | 
			
		||||
  // If the file is inferred, we may not actually be able to parse that file
 | 
			
		||||
@ -189,81 +278,12 @@ std::vector<Index_DoIdMap> ParseFile(
 | 
			
		||||
      path_to_index = entry_cache->import_file;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  std::vector<Index_DoIdMap> result;
 | 
			
		||||
 | 
			
		||||
  // Always run this block, even if we are interactive, so we can check
 | 
			
		||||
  // dependencies and reset files in |file_consumer_shared|.
 | 
			
		||||
  IndexFile* previous_index = cache_manager->TryLoad(path_to_index);
 | 
			
		||||
  if (previous_index) {
 | 
			
		||||
    // If none of the dependencies have changed and the index is not
 | 
			
		||||
    // interactive (ie, requested by a file save), skip parsing and just load
 | 
			
		||||
    // from cache.
 | 
			
		||||
 | 
			
		||||
    // Check timestamps and update |file_consumer_shared|.
 | 
			
		||||
    ShouldParse path_state = FileNeedsParse(
 | 
			
		||||
        is_interactive, timestamp_manager, modification_timestamp_fetcher,
 | 
			
		||||
        import_manager, cache_manager, file_consumer_shared, previous_index,
 | 
			
		||||
        path_to_index, entry.args, nullopt);
 | 
			
		||||
 | 
			
		||||
    // Target file does not exist on disk, do not emit any indexes.
 | 
			
		||||
    // TODO: Dependencies should be reassigned to other files. We can do this by
 | 
			
		||||
    // updating the "primary_file" if it doesn't exist. Might not actually be a
 | 
			
		||||
    // problem in practice.
 | 
			
		||||
    if (path_state == ShouldParse::NoSuchFile)
 | 
			
		||||
      return result;
 | 
			
		||||
 | 
			
		||||
    bool needs_reparse = is_interactive || path_state == ShouldParse::Yes;
 | 
			
		||||
 | 
			
		||||
    for (const std::string& dependency : previous_index->dependencies) {
 | 
			
		||||
      assert(!dependency.empty());
 | 
			
		||||
 | 
			
		||||
      // note: Use != as there are multiple failure results for FileParseQuery.
 | 
			
		||||
      if (FileNeedsParse(is_interactive, timestamp_manager,
 | 
			
		||||
                         modification_timestamp_fetcher, import_manager,
 | 
			
		||||
                         cache_manager, file_consumer_shared, previous_index,
 | 
			
		||||
                         dependency, entry.args,
 | 
			
		||||
                         previous_index->path) != ShouldParse::No) {
 | 
			
		||||
        needs_reparse = true;
 | 
			
		||||
        // SUBTLE: Do not break here, as |file_consumer_shared| is updated
 | 
			
		||||
        // inside of |file_needs_parse|.
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // No timestamps changed - load directly from cache.
 | 
			
		||||
    if (!needs_reparse) {
 | 
			
		||||
      LOG_S(INFO) << "Skipping parse; no timestamp change for "
 | 
			
		||||
                  << path_to_index;
 | 
			
		||||
 | 
			
		||||
      // TODO/FIXME: real perf
 | 
			
		||||
      PerformanceImportFile perf;
 | 
			
		||||
      result.push_back(Index_DoIdMap(cache_manager->TakeOrLoad(path_to_index),
 | 
			
		||||
                                     perf, is_interactive,
 | 
			
		||||
                                     false /*write_to_disk*/));
 | 
			
		||||
      for (const std::string& dependency : previous_index->dependencies) {
 | 
			
		||||
        // Only load a dependency if it is not already loaded.
 | 
			
		||||
        //
 | 
			
		||||
        // This is important for perf in large projects where there are lots of
 | 
			
		||||
        // dependencies shared between many files.
 | 
			
		||||
        if (!file_consumer_shared->Mark(dependency))
 | 
			
		||||
          continue;
 | 
			
		||||
 | 
			
		||||
        LOG_S(INFO) << "Emitting index result for " << dependency << " (via "
 | 
			
		||||
                    << previous_index->path << ")";
 | 
			
		||||
 | 
			
		||||
        std::unique_ptr<IndexFile> dependency_index =
 | 
			
		||||
            cache_manager->TryTakeOrLoad(dependency);
 | 
			
		||||
 | 
			
		||||
        // |dependency_index| may be null if there is no cache for it but
 | 
			
		||||
        // another file has already started importing it.
 | 
			
		||||
        if (!dependency_index)
 | 
			
		||||
          continue;
 | 
			
		||||
 | 
			
		||||
        result.push_back(Index_DoIdMap(std::move(dependency_index), perf,
 | 
			
		||||
                                       is_interactive,
 | 
			
		||||
                                       false /*write_to_disk*/));
 | 
			
		||||
      }
 | 
			
		||||
      return result;
 | 
			
		||||
    }
 | 
			
		||||
  // Try to load the file from cache.
 | 
			
		||||
  if (TryLoadFromCache(file_consumer_shared, timestamp_manager,
 | 
			
		||||
                       modification_timestamp_fetcher, import_manager,
 | 
			
		||||
                       cache_manager, is_interactive, entry,
 | 
			
		||||
                       path_to_index) == CacheLoadResult::DoNotParse) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  LOG_S(INFO) << "Parsing " << path_to_index;
 | 
			
		||||
@ -296,13 +316,15 @@ std::vector<Index_DoIdMap> ParseFile(
 | 
			
		||||
  if (!loaded_primary) {
 | 
			
		||||
    optional<std::string> content = ReadContent(path_to_index);
 | 
			
		||||
    if (!content) {
 | 
			
		||||
      // Modification timestamp should have detected this already.
 | 
			
		||||
      LOG_S(ERROR) << "Skipping index (file cannot be found): "
 | 
			
		||||
                   << path_to_index;
 | 
			
		||||
      return result;
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    file_contents.push_back(FileContents(path_to_index, *content));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  std::vector<Index_DoIdMap> result;
 | 
			
		||||
  PerformanceImportFile perf;
 | 
			
		||||
  std::vector<std::unique_ptr<IndexFile>> indexes =
 | 
			
		||||
      indexer->Index(config, file_consumer_shared, path_to_index, entry.args,
 | 
			
		||||
@ -323,7 +345,7 @@ std::vector<Index_DoIdMap> ParseFile(
 | 
			
		||||
                                   true /*write_to_disk*/));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return result;
 | 
			
		||||
  QueueManager::instance()->do_id_map.EnqueueAll(std::move(result));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool IndexMain_DoParse(
 | 
			
		||||
@ -343,18 +365,9 @@ bool IndexMain_DoParse(
 | 
			
		||||
  Project::Entry entry;
 | 
			
		||||
  entry.filename = request->path;
 | 
			
		||||
  entry.args = request->args;
 | 
			
		||||
  std::vector<Index_DoIdMap> responses =
 | 
			
		||||
      ParseFile(config, working_files, file_consumer_shared, timestamp_manager,
 | 
			
		||||
                modification_timestamp_fetcher, import_manager, cache_manager,
 | 
			
		||||
                indexer, request->is_interactive, entry, request->contents);
 | 
			
		||||
 | 
			
		||||
  // Don't bother sending an IdMap request if there are no responses. This
 | 
			
		||||
  // avoids a lock.
 | 
			
		||||
  if (responses.empty())
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  // EnqueueAll will clear |responses|.
 | 
			
		||||
  queue->do_id_map.EnqueueAll(std::move(responses));
 | 
			
		||||
  ParseFile(config, working_files, file_consumer_shared, timestamp_manager,
 | 
			
		||||
            modification_timestamp_fetcher, import_manager, cache_manager,
 | 
			
		||||
            indexer, request->is_interactive, entry, request->contents);
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -132,6 +132,9 @@ struct ThreadedQueue : public BaseThreadQueue {
 | 
			
		||||
 | 
			
		||||
  // Add a set of elements to the queue.
 | 
			
		||||
  void EnqueueAll(std::vector<T>&& elements) {
 | 
			
		||||
    if (elements.empty())
 | 
			
		||||
      return;
 | 
			
		||||
 | 
			
		||||
    std::lock_guard<std::mutex> lock(mutex_);
 | 
			
		||||
 | 
			
		||||
    total_count_ += elements.size();
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user