ilya-biryukov updated this revision to Diff 109524.
ilya-biryukov added a comment.

Added a very rough prototype of vfs::RealFileSystem not using openat.
Not ready for submission yet, wanted to start discussion about Windows 
implementation.


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
@@ -189,59 +189,88 @@
 /// \brief The file system according to your operating system.
 class RealFileSystem : public FileSystem {
 public:
+  RealFileSystem();
+  ~RealFileSystem();
+
   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:
+  std::string WorkingDir;
+  int WorkingDirFD;
 };
 } // end anonymous namespace
 
+RealFileSystem::RealFileSystem() {
+  SmallString<128> CWD;
+
+  auto Err = llvm::sys::fs::openDirectoryForAt(".", /*ref*/WorkingDirFD, &CWD);
+  // TODO: handle errors gracefully.
+  assert(!Err && "couldn't open CWD'");
+
+  WorkingDir = CWD.str();
+}
+
+RealFileSystem::~RealFileSystem() {
+  sys::Process::SafelyCloseFileDescriptor(WorkingDirFD);
+}
+
 ErrorOr<Status> RealFileSystem::status(const Twine &Path) {
   sys::fs::file_status RealStatus;
-  if (std::error_code EC = sys::fs::status(Path, RealStatus))
+  // TODO: handle errors gracefully.
+  if (std::error_code EC = sys::fs::status_at(WorkingDirFD, Path, RealStatus))
     return EC;
   return Status::copyWithNewName(RealStatus, Path.str());
 }
 
 ErrorOr<std::unique_ptr<File>>
 RealFileSystem::openFileForRead(const Twine &Name) {
   int FD;
   SmallString<256> RealName;
-  if (std::error_code EC = sys::fs::openFileForRead(Name, FD, &RealName))
+  // TODO: handle errors gracefully.
+  if (std::error_code EC = sys::fs::openFileForRead(WorkingDirFD, Name, FD, &RealName))
     return EC;
   return std::unique_ptr<File>(new RealFile(FD, Name.str(), RealName.str()));
 }
 
 llvm::ErrorOr<std::string> RealFileSystem::getCurrentWorkingDirectory() const {
-  SmallString<256> Dir;
-  if (std::error_code EC = llvm::sys::fs::current_path(Dir))
-    return EC;
-  return Dir.str().str();
+  // TODO: Handle errors.
+  return WorkingDir;
+  // SmallString<256> Dir;
+  // if (std::error_code EC = llvm::sys::fs::current_path(Dir))
+  //   return EC;
+  // return Dir.str().str();
 }
 
 std::error_code RealFileSystem::setCurrentWorkingDirectory(const Twine &Path) {
-  // FIXME: chdir is thread hostile; on the other hand, creating the same
-  // behavior as chdir is complex: chdir resolves the path once, thus
-  // guaranteeing that all subsequent relative path operations work
-  // on the same path the original chdir resulted in. This makes a
-  // difference for example on network filesystems, where symlinks might be
-  // switched during runtime of the tool. Fixing this depends on having a
-  // file system abstraction that allows openat() style interactions.
-  return llvm::sys::fs::set_current_path(Path);
+  SmallString<128> CWD;
+  auto Err = llvm::sys::fs::openFileForRead(Path, /*ref*/WorkingDirFD, &CWD);
+  if (Err)
+    return Err;
+
+  // TODO: handle errors
+  WorkingDir = CWD.str();
+  return std::error_code();
 }
 
 IntrusiveRefCntPtr<FileSystem> vfs::getRealFileSystem() {
   static IntrusiveRefCntPtr<FileSystem> FS = new RealFileSystem();
   return FS;
 }
 
+IntrusiveRefCntPtr<FileSystem> vfs::createThreadFriendlyRealFS() {
+  return new RealFileSystem();
+}
+
 namespace {
 class RealFSDirIter : public clang::vfs::detail::DirIterImpl {
   llvm::sys::fs::directory_iterator Iter;
 public:
-  RealFSDirIter(const Twine &Path, std::error_code &EC) : Iter(Path, EC) {
+  RealFSDirIter(int WorkingDirFD, const Twine &Path, std::error_code &EC) : Iter(WorkingDirFD, Path, EC) {
     if (!EC && Iter != llvm::sys::fs::directory_iterator()) {
       llvm::sys::fs::file_status S;
       EC = Iter->status(S);
@@ -268,7 +297,7 @@
 
 directory_iterator RealFileSystem::dir_begin(const Twine &Dir,
                                              std::error_code &EC) {
-  return directory_iterator(std::make_shared<RealFSDirIter>(Dir, EC));
+  return directory_iterator(std::make_shared<RealFSDirIter>(WorkingDirFD, Dir, EC));
 }
 
 //===-----------------------------------------------------------------------===/
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