ilya-biryukov created this revision.

Allows to have multiple instances of RealFileSystem that have
different working directories.


https://reviews.llvm.org/D36226

Files:
  include/clang/Basic/VirtualFileSystem.h
  lib/Basic/VirtualFileSystem.cpp
  unittests/Basic/VirtualFileSystemTest.cpp

Index: unittests/Basic/VirtualFileSystemTest.cpp
===================================================================
--- unittests/Basic/VirtualFileSystemTest.cpp
+++ unittests/Basic/VirtualFileSystemTest.cpp
@@ -12,6 +12,7 @@
 #include "llvm/Support/Errc.h"
 #include "llvm/Support/Host.h"
 #include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
 #include "llvm/Support/SourceMgr.h"
 #include "gtest/gtest.h"
 #include <map>
@@ -477,6 +478,75 @@
 }
 #endif
 
+TEST(VirtualFileSystemTest, ThreadFriendlyRealFSWorkingDirTest) {
+  ScopedDir TestDirectory("thread-friendly-vfs-test", /*Unique*/ true);
+
+  SmallString<128> PathA = TestDirectory.Path;
+  llvm::sys::path::append(PathA, "a");
+  SmallString<128> PathAC = PathA;
+  llvm::sys::path::append(PathAC, "c");
+  SmallString<128> PathB = TestDirectory.Path;
+  llvm::sys::path::append(PathB, "b");
+  SmallString<128> PathBD = PathB;
+  llvm::sys::path::append(PathBD, "d");
+
+  ScopedDir A(PathA);
+  ScopedDir AC(PathAC);
+  ScopedDir B(PathB);
+  ScopedDir BD(PathBD);
+
+  IntrusiveRefCntPtr<vfs::FileSystem> FSA = vfs::createThreadFriendlyRealFS();
+  // setCurrentWorkingDirectory should fininsh without error
+  ASSERT_TRUE(!FSA->setCurrentWorkingDirectory(Twine(A)));
+
+  IntrusiveRefCntPtr<vfs::FileSystem> FSB = vfs::createThreadFriendlyRealFS();
+  // setCurrentWorkingDirectory should fininsh without error
+  ASSERT_TRUE(!FSB->setCurrentWorkingDirectory(Twine(B)));
+
+  ASSERT_TRUE(FSA->getCurrentWorkingDirectory());
+  EXPECT_EQ(*FSA->getCurrentWorkingDirectory(), StringRef(A));
+
+  ASSERT_TRUE(FSB->getCurrentWorkingDirectory());
+  EXPECT_EQ(*FSB->getCurrentWorkingDirectory(), StringRef(B));
+
+  auto C = FSA->status("c");
+  // a/c should be found in FSA
+  ASSERT_TRUE(!!C);
+  // name of 'c' must be relative
+  EXPECT_EQ(C->getName(), "c");
+  EXPECT_TRUE(C->isDirectory());
+
+  // "a", "b" and "d" should not be found in FSA.
+  EXPECT_FALSE(!!FSA->status("a"));
+  EXPECT_FALSE(!!FSA->status("b"));
+  EXPECT_FALSE(!!FSA->status("d"));
+
+  auto D = FSB->status("d");
+  // b/d should be found in FSB
+  ASSERT_TRUE(!!D);
+  // name of 'd' must be relative
+  EXPECT_EQ(D->getName(), "d");
+  EXPECT_TRUE(D->isDirectory());
+
+  // "a", "b" and "c" should not be found in FSB.
+  EXPECT_FALSE(!!FSB->status("a"));
+  EXPECT_FALSE(!!FSB->status("b"));
+  EXPECT_FALSE(!!FSB->status("c"));
+
+  SmallString<128> RelPathC = StringRef("..");
+  llvm::sys::path::append(RelPathC, "a", "c");
+  auto RelCFromFSA = FSA->status(RelPathC);
+  ASSERT_TRUE(!!RelCFromFSA);
+  EXPECT_EQ(RelCFromFSA->getName(), StringRef(RelPathC));
+
+  auto RelCFromFSB = FSB->status(RelPathC);
+  ASSERT_TRUE(!!RelCFromFSB);
+  EXPECT_EQ(RelCFromFSB->getName(), StringRef(RelPathC));
+
+  EXPECT_TRUE(C->equivalent(*RelCFromFSA));
+  EXPECT_TRUE(C->equivalent(*RelCFromFSB));
+}
+
 template <typename DirIter>
 static void checkContents(DirIter I, ArrayRef<StringRef> ExpectedOut) {
   std::error_code EC;
Index: lib/Basic/VirtualFileSystem.cpp
===================================================================
--- lib/Basic/VirtualFileSystem.cpp
+++ lib/Basic/VirtualFileSystem.cpp
@@ -136,6 +136,7 @@
   Status S;
   std::string RealName;
   friend class RealFileSystem;
+  friend class ThreadFriendlyRealFileSystem;
   RealFile(int FD, StringRef NewName, StringRef NewRealPathName)
       : FD(FD), S(NewName, {}, {}, {}, {}, {},
                   llvm::sys::fs::file_type::status_error, {}),
@@ -239,7 +240,9 @@
 
 namespace {
 class RealFSDirIter : public clang::vfs::detail::DirIterImpl {
+protected:
   llvm::sys::fs::directory_iterator Iter;
+
 public:
   RealFSDirIter(const Twine &Path, std::error_code &EC) : Iter(Path, EC) {
     if (!EC && Iter != llvm::sys::fs::directory_iterator()) {
@@ -271,6 +274,131 @@
   return directory_iterator(std::make_shared<RealFSDirIter>(Dir, EC));
 }
 
+//===-----------------------------------------------------------------------===/
+// ThreadFriendlyRealFileSystem implementation
+//===-----------------------------------------------------------------------===/
+
+namespace {
+/// \brief A thread-friendly version of RealFileSystem. Does not call
+/// llvm::sys::fs::set_current_path, stores CurrentWorkingDirectory as a field
+/// instead. This class is not thread-safe, though, it merely gets rid of
+/// manipulating global state.
+class ThreadFriendlyRealFileSystem : public RealFileSystem {
+public:
+  ThreadFriendlyRealFileSystem()
+      : CurrentWorkingDir(RealFileSystem::getCurrentWorkingDirectory()) {}
+
+  ErrorOr<Status> status(const Twine &Path) override;
+  ErrorOr<std::unique_ptr<File>> openFileForRead(const Twine &Path) override;
+  directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override;
+
+  llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override;
+  std::error_code setCurrentWorkingDirectory(const Twine &Path) override;
+
+private:
+  llvm::ErrorOr<std::string> CurrentWorkingDir;
+};
+} // end anonymous namespace
+
+ErrorOr<Status> ThreadFriendlyRealFileSystem::status(const Twine &Path) {
+  SmallString<256> AbsPath;
+  Path.toVector(AbsPath);
+  if (std::error_code Err = makeAbsolute(AbsPath))
+    return Err;
+
+  auto Stat = RealFileSystem::status(AbsPath);
+  if (!Stat)
+    return Stat;
+  return Status::copyWithNewName(Stat.get(), Path.str());
+}
+
+ErrorOr<std::unique_ptr<File>>
+ThreadFriendlyRealFileSystem::openFileForRead(const Twine &Name) {
+  SmallString<256> AbsPath;
+  Name.toVector(AbsPath);
+  if (std::error_code Err = makeAbsolute(AbsPath))
+    return Err;
+
+  // Slightly modified copy of RealFileSystem::openFileForRead.
+  // We use AbsPath here instead of name as an argument to
+  // sys::fs::openFileForRead().
+  // If you change  this function, please update RealFileSystem::openFileForRead
+  // accordingly .
+  int FD;
+  SmallString<256> RealName;
+  if (std::error_code EC = sys::fs::openFileForRead(AbsPath, FD, &RealName))
+    return EC;
+  return std::unique_ptr<File>(new RealFile(FD, Name.str(), RealName.str()));
+}
+
+llvm::ErrorOr<std::string>
+ThreadFriendlyRealFileSystem::getCurrentWorkingDirectory() const {
+  return CurrentWorkingDir;
+}
+
+std::error_code
+ThreadFriendlyRealFileSystem::setCurrentWorkingDirectory(const Twine &Path) {
+  SmallString<256> AbsPath;
+  Path.toVector(AbsPath);
+  if (std::error_code EC = makeAbsolute(AbsPath))
+    return EC;
+
+  CurrentWorkingDir = AbsPath.str().str();
+  return std::error_code();
+}
+
+IntrusiveRefCntPtr<FileSystem> vfs::createThreadFriendlyRealFS() {
+  return new ThreadFriendlyRealFileSystem();
+}
+
+namespace {
+class ThreadFriendlyRealFSDirIter : public RealFSDirIter {
+  std::string OriginalPath;
+
+public:
+  ThreadFriendlyRealFSDirIter(const Twine &OriginalPath, const Twine &AbsPath,
+                              std::error_code &EC)
+      : RealFSDirIter(AbsPath, EC), OriginalPath(OriginalPath.str()) {
+    if (!EC) {
+      fixupCurrentEntryName();
+    }
+  }
+
+  std::error_code increment() override {
+    std::error_code EC = RealFSDirIter::increment();
+    if (EC)
+      return EC;
+
+    fixupCurrentEntryName();
+    return EC;
+  }
+
+private:
+  void fixupCurrentEntryName() {
+    if (Iter == llvm::sys::fs::directory_iterator())
+      return;
+
+    llvm::SmallString<256> ModifiedPath = StringRef(OriginalPath);
+
+    assert(llvm::sys::path::has_filename(Iter->path()));
+    auto FileName = llvm::sys::path::filename(Iter->path());
+    llvm::sys::path::append(ModifiedPath, FileName);
+
+    CurrentEntry = Status::copyWithNewName(CurrentEntry, ModifiedPath);
+  }
+};
+} // namespace
+
+directory_iterator
+ThreadFriendlyRealFileSystem::dir_begin(const Twine &Dir, std::error_code &EC) {
+  llvm::SmallString<256> AbsPath;
+  Dir.toVector(AbsPath);
+  EC = makeAbsolute(AbsPath);
+
+  return directory_iterator(
+      std::make_shared<ThreadFriendlyRealFSDirIter>(Dir, AbsPath, EC));
+}
+
 //===-----------------------------------------------------------------------===/
 // OverlayFileSystem implementation
 //===-----------------------------------------------------------------------===/
Index: include/clang/Basic/VirtualFileSystem.h
===================================================================
--- include/clang/Basic/VirtualFileSystem.h
+++ include/clang/Basic/VirtualFileSystem.h
@@ -265,6 +265,15 @@
 /// the operating system.
 IntrusiveRefCntPtr<FileSystem> getRealFileSystem();
 
+/// \brief Creates a \p vfs::FileSystem for the 'real' file system that does not
+/// change to global state of the program on calls to
+/// setCurrentWorkingDirectory(). Internally it has to do more paths
+/// manipulations than the vfs::FileSystem returned by getRealFileSystem(). Note
+/// that returned instance of vfs::FileSystem is not thread-safe.
+/// Thread-friendly refers to the fact that it does not use global
+/// WorkingDirectory.
+IntrusiveRefCntPtr<FileSystem> createThreadFriendlyRealFS();
+
 /// \brief A file system that allows overlaying one \p AbstractFileSystem on top
 /// of another.
 ///
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to