diff --git a/llvm/include/llvm/Support/Error.h b/llvm/include/llvm/Support/Error.h --- a/llvm/include/llvm/Support/Error.h +++ b/llvm/include/llvm/Support/Error.h @@ -53,12 +53,7 @@ virtual void log(raw_ostream &OS) const = 0; /// Return the error message as a string. - virtual std::string message() const { - std::string Msg; - raw_string_ostream OS(Msg); - log(OS); - return OS.str(); - } + virtual std::string message() const; /// Convert this error to a std::error_code. /// @@ -705,19 +700,7 @@ /// /// cantFail(foo(false)); /// @endcode -inline void cantFail(Error Err, const char *Msg = nullptr) { - if (Err) { - if (!Msg) - Msg = "Failure value returned from cantFail wrapped call"; -#ifndef NDEBUG - std::string Str; - raw_string_ostream OS(Str); - OS << Msg << "\n" << Err; - Msg = OS.str().c_str(); -#endif - llvm_unreachable(Msg); - } -} +void cantFail(Error Err, const char *Msg = nullptr); /// Report a fatal error if ValOrErr is a failure value, otherwise unwraps and /// returns the contained value. diff --git a/llvm/include/llvm/Support/FileSystem.h b/llvm/include/llvm/Support/FileSystem.h --- a/llvm/include/llvm/Support/FileSystem.h +++ b/llvm/include/llvm/Support/FileSystem.h @@ -1174,6 +1174,33 @@ /// means that the filesystem may have failed to perform some buffered writes. std::error_code closeFile(file_t &F); +/// RAII class that facilitates file locking. +class FileLocker { + int FD; ///< Locked file handle. +public: + FileLocker(int FD) : FD(FD) {} + FileLocker(const FileLocker &L) = delete; + FileLocker(FileLocker &&L) : FD(L.FD) { L.FD = -1; } + ~FileLocker() { + if (FD != -1) + sys::fs::unlockFile(FD); + } + FileLocker &operator=(FileLocker &&L) { + FD = L.FD; + L.FD = -1; + return *this; + } + FileLocker &operator=(const FileLocker &L) = delete; + std::error_code unlock() { + if (FD != -1) { + std::error_code Result = sys::fs::unlockFile(FD); + FD = -1; + return Result; + } + return std::error_code(); + } +}; + std::error_code getUniqueID(const Twine Path, UniqueID &Result); /// Get disk space usage information. diff --git a/llvm/include/llvm/Support/raw_ostream.h b/llvm/include/llvm/Support/raw_ostream.h --- a/llvm/include/llvm/Support/raw_ostream.h +++ b/llvm/include/llvm/Support/raw_ostream.h @@ -30,12 +30,14 @@ class FormattedString; class FormattedNumber; class FormattedBytes; +template class LLVM_NODISCARD Expected; namespace sys { namespace fs { enum FileAccess : unsigned; enum OpenFlags : unsigned; enum CreationDisposition : unsigned; +class FileLocker; } // end namespace fs } // end namespace sys @@ -458,7 +460,7 @@ /// fsync. void close(); - bool supportsSeeking() { return SupportsSeeking; } + bool supportsSeeking() const { return SupportsSeeking; } /// Flushes the stream and repositions the underlying file descriptor position /// to the offset specified from the beginning of the file. @@ -494,6 +496,23 @@ /// - from The Zen of Python, by Tim Peters /// void clear_error() { EC = std::error_code(); } + + /// Tries to lock the file. + /// + /// @returns RAII object that releases the lock upon leaving the scope. + /// + /// Possible use of this function is as follows: + /// + /// @code{.cpp} + /// if (auto L = stream.tryToLock()) { + /// // ... do action that require file to be locked. + /// } else { + /// handleAllErrors(std::move(L.takeError()), [&](ErrorInfoBase &EIB) { + /// // ... handle lock error. + /// }); + /// } + /// @endcode + Expected tryToLock(); }; /// This returns a reference to a raw_ostream for standard output. Use it like: diff --git a/llvm/lib/Support/Error.cpp b/llvm/lib/Support/Error.cpp --- a/llvm/lib/Support/Error.cpp +++ b/llvm/lib/Support/Error.cpp @@ -68,6 +68,26 @@ }); } +void cantFail(Error Err, const char *Msg) { + if (Err) { + if (!Msg) + Msg = "Failure value returned from cantFail wrapped call"; +#ifndef NDEBUG + std::string Str; + raw_string_ostream OS(Str); + OS << Msg << "\n" << Err; + Msg = OS.str().c_str(); +#endif + llvm_unreachable(Msg); + } +} + +std::string ErrorInfoBase::message() const { + std::string Msg; + raw_string_ostream OS(Msg); + log(OS); + return OS.str(); +} std::error_code ErrorList::convertToErrorCode() const { return std::error_code(static_cast(ErrorErrorCode::MultipleErrors), diff --git a/llvm/lib/Support/raw_ostream.cpp b/llvm/lib/Support/raw_ostream.cpp --- a/llvm/lib/Support/raw_ostream.cpp +++ b/llvm/lib/Support/raw_ostream.cpp @@ -858,6 +858,13 @@ return sys::Process::FileDescriptorHasColors(FD); } +Expected raw_fd_ostream::tryToLock() { + std::error_code EC = sys::fs::tryLockFile(FD); + if (!EC) + return sys::fs::FileLocker(FD); + return errorCodeToError(EC); +} + void raw_fd_ostream::anchor() {} //===----------------------------------------------------------------------===// diff --git a/llvm/unittests/Support/Path.cpp b/llvm/unittests/Support/Path.cpp --- a/llvm/unittests/Support/Path.cpp +++ b/llvm/unittests/Support/Path.cpp @@ -2150,4 +2150,46 @@ #endif } +TEST_F(FileSystemTest, FileLocker) { + int FD; + std::error_code EC; + SmallString<64> TempPath; + EC = fs::createTemporaryFile("test", "temp", FD, TempPath); + ASSERT_NO_ERROR(EC); + FileRemover Cleanup(TempPath); + raw_fd_ostream Stream(TempPath, EC); + + EC = fs::tryLockFile(FD); + ASSERT_NO_ERROR(EC); + EC = fs::unlockFile(FD); + ASSERT_NO_ERROR(EC); + + if (auto L = Stream.tryToLock()) { + ASSERT_ERROR(fs::tryLockFile(FD)); + ASSERT_NO_ERROR(L->unlock()); + ASSERT_NO_ERROR(fs::tryLockFile(FD)); + ASSERT_NO_ERROR(fs::unlockFile(FD)); + } else { + EXPECT_THAT_EXPECTED(L, Failed()); + handleAllErrors(std::move(L.takeError()), [&](ErrorInfoBase &EIB) {}); + } + + ASSERT_NO_ERROR(fs::tryLockFile(FD)); + ASSERT_NO_ERROR(fs::unlockFile(FD)); + + { + Expected L1 = Stream.tryToLock(); + ASSERT_THAT_EXPECTED(L1, Succeeded()); + raw_fd_ostream Stream2(FD, false); + Expected L2 = Stream2.tryToLock(); + ASSERT_THAT_EXPECTED(L2, Failed()); + ASSERT_NO_ERROR(L1->unlock()); + Expected L3 = Stream.tryToLock(); + ASSERT_THAT_EXPECTED(L3, Succeeded()); + } + + ASSERT_NO_ERROR(fs::tryLockFile(FD)); + ASSERT_NO_ERROR(fs::unlockFile(FD)); +} + } // anonymous namespace