91 lines · cpp
1//===--- ThreadsafeFS.cpp -------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "support/ThreadsafeFS.h"10#include "Logger.h"11#include "llvm/ADT/SmallString.h"12#include "llvm/ADT/StringRef.h"13#include "llvm/Support/Path.h"14#include "llvm/Support/VirtualFileSystem.h"15#include <memory>16 17namespace clang {18namespace clangd {19 20namespace {21/// Always opens files in the underlying filesystem as "volatile", meaning they22/// won't be memory-mapped. Memory-mapping isn't desirable for clangd:23/// - edits to the underlying files change contents MemoryBuffers owned by24// SourceManager, breaking its invariants and leading to crashes25/// - it locks files on windows, preventing edits26class VolatileFileSystem : public llvm::vfs::ProxyFileSystem {27public:28 explicit VolatileFileSystem(llvm::IntrusiveRefCntPtr<FileSystem> FS)29 : ProxyFileSystem(std::move(FS)) {}30 31 llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>32 openFileForRead(const llvm::Twine &InPath) override {33 llvm::SmallString<128> Path;34 InPath.toVector(Path);35 36 auto File = getUnderlyingFS().openFileForRead(Path);37 if (!File)38 return File;39 // Try to guess preamble files, they can be memory-mapped even on Windows as40 // clangd has exclusive access to those and nothing else should touch them.41 llvm::StringRef FileName = llvm::sys::path::filename(Path);42 if (FileName.starts_with("preamble-") && FileName.ends_with(".pch"))43 return File;44 return std::make_unique<VolatileFile>(std::move(*File));45 }46 47private:48 class VolatileFile : public llvm::vfs::File {49 public:50 VolatileFile(std::unique_ptr<llvm::vfs::File> Wrapped)51 : Wrapped(std::move(Wrapped)) {52 assert(this->Wrapped);53 }54 55 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>56 getBuffer(const llvm::Twine &Name, int64_t FileSize,57 bool RequiresNullTerminator, bool /*IsVolatile*/) override {58 return Wrapped->getBuffer(Name, FileSize, RequiresNullTerminator,59 /*IsVolatile=*/true);60 }61 62 llvm::ErrorOr<llvm::vfs::Status> status() override {63 return Wrapped->status();64 }65 llvm::ErrorOr<std::string> getName() override { return Wrapped->getName(); }66 std::error_code close() override { return Wrapped->close(); }67 68 private:69 std::unique_ptr<File> Wrapped;70 };71};72} // namespace73 74llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>75ThreadsafeFS::view(PathRef CWD) const {76 auto FS = view(std::nullopt);77 if (auto EC = FS->setCurrentWorkingDirectory(CWD))78 elog("VFS: failed to set CWD to {0}: {1}", CWD, EC.message());79 return FS;80}81 82llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>83RealThreadsafeFS::viewImpl() const {84 // Avoid using memory-mapped files.85 // FIXME: Try to use a similar approach in Sema instead of relying on86 // propagation of the 'isVolatile' flag through all layers.87 return new VolatileFileSystem(llvm::vfs::createPhysicalFileSystem());88}89} // namespace clangd90} // namespace clang91