Clean up FileConsumer and improve pipeline

This commit is contained in:
Fangrui Song 2018-09-20 00:31:23 -07:00
parent 110023483a
commit d9f0de4719
7 changed files with 76 additions and 127 deletions

View File

@ -9,62 +9,17 @@
#include "platform.h" #include "platform.h"
#include "utils.h" #include "utils.h"
namespace { bool VFS::Loaded(const std::string &path) {
std::lock_guard lock(mutex);
std::optional<std::string> return state[path].loaded;
GetFileContents(const std::string &path,
std::unordered_map<std::string, FileContents> *file_contents) {
auto it = file_contents->find(path);
if (it == file_contents->end()) {
std::optional<std::string> content = ReadContent(path);
if (content)
(*file_contents)[path] = FileContents(path, *content);
return content;
}
return it->second.content;
} }
} // namespace bool VFS::Stamp(const std::string &path, int64_t ts, int step) {
FileContents::FileContents(const std::string &path, const std::string &content)
: path(path), content(content) {
line_offsets_.push_back(0);
for (size_t i = 0; i < content.size(); i++) {
if (content[i] == '\n')
line_offsets_.push_back(i + 1);
}
}
std::optional<int> FileContents::ToOffset(Position p) const {
if (0 <= p.line && size_t(p.line) < line_offsets_.size()) {
int ret = line_offsets_[p.line] + p.column;
if (size_t(ret) < content.size())
return ret;
}
return std::nullopt;
}
std::optional<std::string> FileContents::ContentsInRange(Range range) const {
std::optional<int> start_offset = ToOffset(range.start),
end_offset = ToOffset(range.end);
if (start_offset && end_offset && *start_offset < *end_offset)
return content.substr(*start_offset, *end_offset - *start_offset);
return std::nullopt;
}
VFS::State VFS::Get(const std::string &file) {
std::lock_guard<std::mutex> lock(mutex); std::lock_guard<std::mutex> lock(mutex);
auto it = state.find(file); State &st = state[path];
if (it != state.end()) if (st.timestamp < ts || (st.timestamp == ts && st.step < step)) {
return it->second; st.timestamp = ts;
return {0, 0}; st.step = step;
}
bool VFS::Stamp(const std::string &file, int64_t ts, int64_t offset) {
std::lock_guard<std::mutex> lock(mutex);
State &st = state[file];
if (st.timestamp < ts) {
st.timestamp = ts + offset;
return true; return true;
} else } else
return false; return false;
@ -75,29 +30,26 @@ FileConsumer::FileConsumer(VFS *vfs, const std::string &parse_file)
IndexFile *FileConsumer::TryConsumeFile( IndexFile *FileConsumer::TryConsumeFile(
const clang::FileEntry &File, const clang::FileEntry &File,
std::unordered_map<std::string, FileContents> *file_contents_map) { const std::unordered_map<llvm::sys::fs::UniqueID, FileConsumer::File>
&UID2File) {
auto UniqueID = File.getUniqueID(); auto UniqueID = File.getUniqueID();
{
auto it = local_.find(UniqueID); auto it = local_.find(UniqueID);
if (it != local_.end()) if (it != local_.end())
return it->second.get(); return it->second.get();
}
std::string file_name = FileName(File); auto it = UID2File.find(UniqueID);
int64_t tim = File.getModificationTime(); assert(it != UID2File.end());
assert(tim); assert(it->second.mtime);
if (!vfs_->Stamp(file_name, tim, 0)) { if (!vfs_->Stamp(it->second.path, it->second.mtime, 1)) {
local_[UniqueID] = nullptr; local_[UniqueID] = nullptr;
return nullptr; return nullptr;
} }
// Read the file contents, if we fail then we cannot index the file.
std::optional<std::string> contents =
GetFileContents(file_name, file_contents_map);
if (!contents)
return nullptr;
// Build IndexFile instance. // Build IndexFile instance.
local_[UniqueID] = local_[UniqueID] =
std::make_unique<IndexFile>(UniqueID, file_name, *contents); std::make_unique<IndexFile>(UniqueID, it->second.path, it->second.content);
return local_[UniqueID].get(); return local_[UniqueID].get();
} }

View File

@ -15,29 +15,17 @@
struct IndexFile; struct IndexFile;
struct FileContents {
FileContents() = default;
FileContents(const std::string &path, const std::string &content);
std::optional<int> ToOffset(Position p) const;
std::optional<std::string> ContentsInRange(Range range) const;
std::string path;
std::string content;
// {0, 1 + position of first newline, 1 + position of second newline, ...}
std::vector<int> line_offsets_;
};
struct VFS { struct VFS {
struct State { struct State {
int64_t timestamp; int64_t timestamp;
bool loaded = false; int step;
bool loaded;
}; };
mutable std::unordered_map<std::string, State> state; mutable std::unordered_map<std::string, State> state;
mutable std::mutex mutex; mutable std::mutex mutex;
State Get(const std::string &file); bool Loaded(const std::string &path);
bool Stamp(const std::string &file, int64_t ts, int64_t offset); bool Stamp(const std::string &path, int64_t ts, int step);
}; };
namespace std { namespace std {
@ -58,17 +46,18 @@ template <> struct hash<llvm::sys::fs::UniqueID> {
// The indexer does this because header files do not have their own translation // The indexer does this because header files do not have their own translation
// units but we still want to index them. // units but we still want to index them.
struct FileConsumer { struct FileConsumer {
struct File {
std::string path;
int64_t mtime;
std::string content;
};
FileConsumer(VFS *vfs, const std::string &parse_file); FileConsumer(VFS *vfs, const std::string &parse_file);
// Returns IndexFile for the file or nullptr. |is_first_ownership| is set // Returns IndexFile or nullptr for the file or nullptr.
// to true iff the function just took ownership over the file. Otherwise it IndexFile *TryConsumeFile(
// is set to false. const clang::FileEntry &file,
// const std::unordered_map<llvm::sys::fs::UniqueID, File> &);
// note: file_contents is passed as a parameter instead of as a member
// variable since it is large and we do not want to copy it.
IndexFile *
TryConsumeFile(const clang::FileEntry &file,
std::unordered_map<std::string, FileContents> *file_contents);
// Returns and passes ownership of all local state. // Returns and passes ownership of all local state.
std::vector<std::unique_ptr<IndexFile>> TakeLocalState(); std::vector<std::unique_ptr<IndexFile>> TakeLocalState();

View File

@ -37,10 +37,8 @@ constexpr int kInitializerMaxLines = 3;
GroupMatch *multiVersionMatcher; GroupMatch *multiVersionMatcher;
struct IndexParam { struct IndexParam {
std::unordered_map<llvm::sys::fs::UniqueID, std::string> SeenUniqueID; std::unordered_map<llvm::sys::fs::UniqueID, FileConsumer::File> UID2File;
std::unordered_map<llvm::sys::fs::UniqueID, bool> UID2multi; std::unordered_map<llvm::sys::fs::UniqueID, bool> UID2multi;
std::unordered_map<std::string, FileContents> file_contents;
std::unordered_map<std::string, int64_t> file2mtime;
struct DeclInfo { struct DeclInfo {
Usr usr; Usr usr;
std::string short_name; std::string short_name;
@ -56,17 +54,22 @@ struct IndexParam {
void SeenFile(const FileEntry &File) { void SeenFile(const FileEntry &File) {
// If this is the first time we have seen the file (ignoring if we are // If this is the first time we have seen the file (ignoring if we are
// generating an index for it): // generating an index for it):
auto [it, inserted] = SeenUniqueID.try_emplace(File.getUniqueID()); auto [it, inserted] = UID2File.try_emplace(File.getUniqueID());
if (inserted) { if (inserted) {
std::string file_name = FileName(File); std::string path = FileName(File);
it->second = file_name; it->second.path = path;
file2mtime[file_name] = File.getModificationTime(); it->second.mtime = File.getModificationTime();
if (!it->second.mtime)
if (auto tim = LastWriteTime(path))
it->second.mtime = *tim;
if (std::optional<std::string> content = ReadContent(path))
it->second.content = *content;
} }
} }
IndexFile *ConsumeFile(const FileEntry &FE) { IndexFile *ConsumeFile(const FileEntry &FE) {
SeenFile(FE); SeenFile(FE);
return file_consumer->TryConsumeFile(FE, &file_contents); return file_consumer->TryConsumeFile(FE, UID2File);
} }
bool UseMultiVersion(const FileEntry &FE) { bool UseMultiVersion(const FileEntry &FE) {
@ -1319,15 +1322,15 @@ Index(CompletionManager *completion, WorkingFiles *wfiles, VFS *vfs,
for (auto &it : entry->usr2var) for (auto &it : entry->usr2var)
Uniquify(it.second.uses); Uniquify(it.second.uses);
// Update file contents and modification time. // Update dependencies for the file.
entry->mtime = param.file2mtime[entry->path]; for (auto &[_, file] : param.UID2File) {
const std::string &path = file.path;
// Update dependencies for the file. Do not include the file in its own if (path == entry->path)
// dependency set. entry->mtime = file.mtime;
for (auto &[_, path] : param.SeenUniqueID) else if (path != entry->import_file)
if (path != entry->path && path != entry->import_file)
entry->dependencies[llvm::CachedHashStringRef(Intern(path))] = entry->dependencies[llvm::CachedHashStringRef(Intern(path))] =
param.file2mtime[path]; file.mtime;
}
} }
return result; return result;

View File

@ -194,16 +194,21 @@ bool Indexer_Parse(CompletionManager *completion, WorkingFiles *wfiles,
std::optional<int64_t> write_time = LastWriteTime(path_to_index); std::optional<int64_t> write_time = LastWriteTime(path_to_index);
if (!write_time) if (!write_time)
return true; return true;
int reparse = vfs->Stamp(path_to_index, *write_time, -1); int reparse = vfs->Stamp(path_to_index, *write_time, 0);
if (request.path != path_to_index) { if (request.path != path_to_index) {
std::optional<int64_t> mtime1 = LastWriteTime(request.path); std::optional<int64_t> mtime1 = LastWriteTime(request.path);
if (!mtime1) if (!mtime1)
return true; return true;
if (vfs->Stamp(request.path, *mtime1, -1)) if (vfs->Stamp(request.path, *mtime1, 0))
reparse = 1; reparse = 1;
} }
if (g_config->index.onChange) if (g_config->index.onChange) {
reparse = 2; reparse = 2;
std::lock_guard lock(vfs->mutex);
vfs->state[path_to_index].step = 0;
if (request.path != path_to_index)
vfs->state[request.path].step = 0;
}
if (!reparse) if (!reparse)
return true; return true;
@ -230,6 +235,8 @@ bool Indexer_Parse(CompletionManager *completion, WorkingFiles *wfiles,
LOG_S(INFO) << "load cache for " << path_to_index; LOG_S(INFO) << "load cache for " << path_to_index;
auto dependencies = prev->dependencies; auto dependencies = prev->dependencies;
if (reparse) { if (reparse) {
if (vfs->Loaded(path_to_index))
return true;
IndexUpdate update = IndexUpdate::CreateDelta(nullptr, prev.get()); IndexUpdate update = IndexUpdate::CreateDelta(nullptr, prev.get());
on_indexed->PushBack(std::move(update), on_indexed->PushBack(std::move(update),
request.mode != IndexMode::NonInteractive); request.mode != IndexMode::NonInteractive);
@ -297,12 +304,7 @@ bool Indexer_Parse(CompletionManager *completion, WorkingFiles *wfiles,
<< ")"; << ")";
{ {
std::lock_guard lock(mutexes[std::hash<std::string>()(path) % N_MUTEXES]); std::lock_guard lock(mutexes[std::hash<std::string>()(path) % N_MUTEXES]);
bool loaded; if (vfs->Loaded(path))
{
std::lock_guard lock1(vfs->mutex);
loaded = vfs->state[path].loaded;
}
if (loaded)
prev = RawCacheLoad(path); prev = RawCacheLoad(path);
else else
prev.reset(); prev.reset();
@ -531,13 +533,6 @@ void Index(const std::string &path, const std::vector<const char *> &args,
index_request->PushBack({path, args, mode, id}, mode != IndexMode::NonInteractive); index_request->PushBack({path, args, mode, id}, mode != IndexMode::NonInteractive);
} }
std::optional<int64_t> LastWriteTime(const std::string &path) {
sys::fs::file_status Status;
if (sys::fs::status(path, Status))
return {};
return sys::toTimeT(Status.getLastModificationTime());
}
std::optional<std::string> LoadIndexedContent(const std::string &path) { std::optional<std::string> LoadIndexedContent(const std::string &path) {
if (g_config->cacheDirectory.empty()) { if (g_config->cacheDirectory.empty()) {
std::shared_lock lock(g_index_mutex); std::shared_lock lock(g_index_mutex);

View File

@ -49,7 +49,6 @@ void MainLoop();
void Index(const std::string &path, const std::vector<const char *> &args, void Index(const std::string &path, const std::vector<const char *> &args,
IndexMode mode, lsRequestId id = {}); IndexMode mode, lsRequestId id = {});
std::optional<int64_t> LastWriteTime(const std::string &path);
std::optional<std::string> LoadIndexedContent(const std::string& path); std::optional<std::string> LoadIndexedContent(const std::string& path);
void WriteStdout(MethodType method, lsBaseOutMessage &response); void WriteStdout(MethodType method, lsBaseOutMessage &response);
} // namespace pipeline } // namespace pipeline

View File

@ -8,7 +8,10 @@
#include <siphash.h> #include <siphash.h>
#include "llvm/ADT/StringRef.h" #include <llvm/ADT/StringRef.h>
#include <llvm/Support/FileSystem.h>
#include <llvm/Support/Path.h>
using namespace llvm;
#include <algorithm> #include <algorithm>
#include <assert.h> #include <assert.h>
@ -112,6 +115,13 @@ std::string EscapeFileName(std::string path) {
return path; return path;
} }
std::optional<int64_t> LastWriteTime(const std::string &path) {
sys::fs::file_status Status;
if (sys::fs::status(path, Status))
return {};
return sys::toTimeT(Status.getLastModificationTime());
}
std::optional<std::string> ReadContent(const std::string &filename) { std::optional<std::string> ReadContent(const std::string &filename) {
char buf[4096]; char buf[4096];
std::string ret; std::string ret;

View File

@ -61,6 +61,7 @@ void EnsureEndsInSlash(std::string &path);
// e.g. foo/bar.c => foo_bar.c // e.g. foo/bar.c => foo_bar.c
std::string EscapeFileName(std::string path); std::string EscapeFileName(std::string path);
std::optional<int64_t> LastWriteTime(const std::string &path);
std::optional<std::string> ReadContent(const std::string &filename); std::optional<std::string> ReadContent(const std::string &filename);
void WriteToFile(const std::string &filename, const std::string &content); void WriteToFile(const std::string &filename, const std::string &content);