//===--- ThreadsafeFS.cpp -------------------------------------------------===// // // 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 // //===----------------------------------------------------------------------===// #include "support/ThreadsafeFS.h" #include "Logger.h" #include "llvm/ADT/None.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Path.h" #include "llvm/Support/VirtualFileSystem.h" #include namespace clang { namespace clangd { namespace { /// Always opens files in the underlying filesystem as "volatile", meaning they /// won't be memory-mapped. Memory-mapping isn't desirable for clangd: /// - edits to the underlying files change contents MemoryBuffers owned by // SourceManager, breaking its invariants and leading to crashes /// - it locks files on windows, preventing edits class VolatileFileSystem : public llvm::vfs::ProxyFileSystem { public: explicit VolatileFileSystem(llvm::IntrusiveRefCntPtr FS) : ProxyFileSystem(std::move(FS)) {} llvm::ErrorOr> openFileForRead(const llvm::Twine &InPath) override { llvm::SmallString<128> Path; InPath.toVector(Path); auto File = getUnderlyingFS().openFileForRead(Path); if (!File) return File; // Try to guess preamble files, they can be memory-mapped even on Windows as // clangd has exclusive access to those and nothing else should touch them. llvm::StringRef FileName = llvm::sys::path::filename(Path); if (FileName.startswith("preamble-") && FileName.endswith(".pch")) return File; return std::unique_ptr(new VolatileFile(std::move(*File))); } private: class VolatileFile : public llvm::vfs::File { public: VolatileFile(std::unique_ptr Wrapped) : Wrapped(std::move(Wrapped)) { assert(this->Wrapped); } virtual llvm::ErrorOr> getBuffer(const llvm::Twine &Name, int64_t FileSize, bool RequiresNullTerminator, bool /*IsVolatile*/) override { return Wrapped->getBuffer(Name, FileSize, RequiresNullTerminator, /*IsVolatile=*/true); } llvm::ErrorOr status() override { return Wrapped->status(); } llvm::ErrorOr getName() override { return Wrapped->getName(); } std::error_code close() override { return Wrapped->close(); } private: std::unique_ptr Wrapped; }; }; } // namespace llvm::IntrusiveRefCntPtr ThreadsafeFS::view(PathRef CWD) const { auto FS = view(llvm::None); if (auto EC = FS->setCurrentWorkingDirectory(CWD)) elog("VFS: failed to set CWD to {0}: {1}", CWD, EC.message()); return FS; } llvm::IntrusiveRefCntPtr RealThreadsafeFS::viewImpl() const { // Avoid using memory-mapped files. // FIXME: Try to use a similar approach in Sema instead of relying on // propagation of the 'isVolatile' flag through all layers. return new VolatileFileSystem(llvm::vfs::createPhysicalFileSystem()); } } // namespace clangd } // namespace clang