//===--- BackgroundIndexRebuild.h - when to rebuild the bg index--*- C++-*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file contains an implementation detail of the background indexer
// (Background.h), which is exposed for testing.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_BACKGROUND_INDEX_REBUILD_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_BACKGROUND_INDEX_REBUILD_H

#include "index/FileIndex.h"
#include "index/Index.h"
#include "llvm/Support/Threading.h"

namespace clang {
namespace clangd {

// The BackgroundIndexRebuilder builds the serving data structures periodically
// in response to events in the background indexer. The goal is to ensure the
// served data stays fairly fresh, without wasting lots of CPU rebuilding it
// often.
//
// The index is always built after a set of shards are loaded from disk.
// This happens when clangd discovers a compilation database that we've
// previously built an index for. It's a fairly fast process that yields lots
// of data, so we wait to get all of it.
//
// The index is built after indexing a few translation units, if it wasn't built
// already. This ensures quick startup if there's no existing index.
// Waiting for a few random TUs yields coverage of the most common headers.
//
// The index is rebuilt every N TUs, to keep if fresh as files are indexed.
//
// The index is rebuilt every time the queue goes idle, if it's stale.
//
// All methods are threadsafe. They're called after FileSymbols is updated
// etc. Without external locking, the rebuilt index may include more updates
// than intended, which is fine.
//
// This class is exposed in the header so it can be tested.
class BackgroundIndexRebuilder {
public:
  BackgroundIndexRebuilder(SwapIndex *Target, FileSymbols *Source,
                           unsigned Threads)
      : TUsBeforeFirstBuild(Threads), Target(Target), Source(Source) {}

  // Called to indicate a TU has been indexed.
  // May rebuild, if enough TUs have been indexed.
  void indexedTU();
  // Called to indicate that all worker threads are idle.
  // May reindex, if the index is not up to date.
  void idle();
  // Called to indicate we're going to load a batch of shards from disk.
  // startLoading() and doneLoading() must be paired, but multiple loading
  // sessions may happen concurrently.
  void startLoading();
  // Called to indicate some shards were actually loaded from disk.
  void loadedTU();
  // Called to indicate we're finished loading shards from disk.
  // May rebuild (if any were loaded).
  void doneLoading();

  // Ensures we won't start any more rebuilds.
  void shutdown();

  // Thresholds for rebuilding as TUs get indexed.
  const unsigned TUsBeforeFirstBuild; // Typically one per worker thread.
  const unsigned TUsBeforeRebuild = 100;

private:
  // Run Check under the lock, and rebuild if it returns true.
  void maybeRebuild(const char *Reason, std::function<bool()> Check);
  bool enoughTUsToRebuild() const;

  // All transient state is guarded by the mutex.
  std::mutex Mu;
  bool ShouldStop = false;
  // Index builds are versioned. ActiveVersion chases StartedVersion.
  unsigned StartedVersion = 0;
  unsigned ActiveVersion = 0;
  // How many TUs have we indexed so far since startup?
  unsigned IndexedTUs = 0;
  unsigned IndexedTUsAtLastRebuild = 0;
  // Are we loading shards? May be multiple concurrent sessions.
  unsigned Loading = 0;
  unsigned LoadedTUs; // In the current loading session.

  SwapIndex *Target;
  FileSymbols *Source;
};

} // namespace clangd
} // namespace clang

#endif
