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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "support/ThreadsafeFS.h" 10 #include "Logger.h" 11 #include "llvm/ADT/None.h" 12 #include "llvm/ADT/Optional.h" 13 #include "llvm/ADT/STLExtras.h" 14 #include "llvm/ADT/SmallString.h" 15 #include "llvm/ADT/StringRef.h" 16 #include "llvm/Support/Path.h" 17 #include "llvm/Support/VirtualFileSystem.h" 18 #include <memory> 19 20 namespace clang { 21 namespace clangd { 22 23 namespace { 24 /// Always opens files in the underlying filesystem as "volatile", meaning they 25 /// won't be memory-mapped. Memory-mapping isn't desirable for clangd: 26 /// - edits to the underlying files change contents MemoryBuffers owned by 27 // SourceManager, breaking its invariants and leading to crashes 28 /// - it locks files on windows, preventing edits 29 class VolatileFileSystem : public llvm::vfs::ProxyFileSystem { 30 public: 31 explicit VolatileFileSystem(llvm::IntrusiveRefCntPtr<FileSystem> FS) 32 : ProxyFileSystem(std::move(FS)) {} 33 34 llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>> 35 openFileForRead(const llvm::Twine &InPath) override { 36 llvm::SmallString<128> Path; 37 InPath.toVector(Path); 38 39 auto File = getUnderlyingFS().openFileForRead(Path); 40 if (!File) 41 return File; 42 // Try to guess preamble files, they can be memory-mapped even on Windows as 43 // clangd has exclusive access to those and nothing else should touch them. 44 llvm::StringRef FileName = llvm::sys::path::filename(Path); 45 if (FileName.startswith("preamble-") && FileName.endswith(".pch")) 46 return File; 47 return std::unique_ptr<VolatileFile>(new VolatileFile(std::move(*File))); 48 } 49 50 private: 51 class VolatileFile : public llvm::vfs::File { 52 public: 53 VolatileFile(std::unique_ptr<llvm::vfs::File> Wrapped) 54 : Wrapped(std::move(Wrapped)) { 55 assert(this->Wrapped); 56 } 57 58 virtual llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> 59 getBuffer(const llvm::Twine &Name, int64_t FileSize, 60 bool RequiresNullTerminator, bool /*IsVolatile*/) override { 61 return Wrapped->getBuffer(Name, FileSize, RequiresNullTerminator, 62 /*IsVolatile=*/true); 63 } 64 65 llvm::ErrorOr<llvm::vfs::Status> status() override { 66 return Wrapped->status(); 67 } 68 llvm::ErrorOr<std::string> getName() override { return Wrapped->getName(); } 69 std::error_code close() override { return Wrapped->close(); } 70 71 private: 72 std::unique_ptr<File> Wrapped; 73 }; 74 }; 75 } // namespace 76 77 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> 78 ThreadsafeFS::view(PathRef CWD) const { 79 auto FS = view(llvm::None); 80 if (auto EC = FS->setCurrentWorkingDirectory(CWD)) 81 elog("VFS: failed to set CWD to {0}: {1}", CWD, EC.message()); 82 return FS; 83 } 84 85 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> 86 RealThreadsafeFS::viewImpl() const { 87 // Avoid using memory-mapped files. 88 // FIXME: Try to use a similar approach in Sema instead of relying on 89 // propagation of the 'isVolatile' flag through all layers. 90 return new VolatileFileSystem(llvm::vfs::createPhysicalFileSystem()); 91 } 92 } // namespace clangd 93 } // namespace clang 94