Loading include/client/rpc/repair_journal_lock.hpp +43 −0 Changes for include/client/rpc/repair_journal_lock.hpp: 43 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -17,12 +17,21 @@ #include <unistd.h> #include <cerrno> #include <chrono> #include <cstdint> #include <cstring> #include <stdexcept> #include <string> #include <sys/types.h> namespace gkfs::rpc { struct repair_journal_owner { std::string hostname; pid_t pid{}; uint64_t acquired_at{}; }; /** * @brief Owns the advisory process lock for a client-local repair journal. * Loading Loading @@ -68,7 +77,34 @@ public: journal_path + "': " + ::strerror(error)); } repair_journal_owner owner; owner.hostname.resize(255); if(::gethostname(owner.hostname.data(), owner.hostname.size()) != 0) { owner.hostname = "unknown"; } else { owner.hostname.resize(std::strlen(owner.hostname.c_str())); } owner.pid = ::getpid(); owner.acquired_at = static_cast<uint64_t>( std::chrono::duration_cast<std::chrono::seconds>( std::chrono::system_clock::now().time_since_epoch()) .count()); const auto record = "hostname=" + owner.hostname + "\n" + "pid=" + std::to_string(owner.pid) + "\n" + "acquired_at=" + std::to_string(owner.acquired_at) + "\n"; if(::ftruncate(candidate.get(), 0) != 0 || ::write(candidate.get(), record.data(), record.size()) != static_cast<ssize_t>(record.size())) { const auto error = errno; throw std::runtime_error( "Unable to write replica repair journal owner record '" + journal_path + "': " + ::strerror(error)); } fd_ = std::move(candidate); owner_ = std::move(owner); } void Loading @@ -78,6 +114,7 @@ public: fd_.reset(); } lock_path_.clear(); owner_ = {}; } [[nodiscard]] bool Loading @@ -90,9 +127,15 @@ public: return lock_path_; } [[nodiscard]] const repair_journal_owner& owner() const noexcept { return owner_; } private: gkfs::utils::unique_fd fd_; std::string lock_path_; repair_journal_owner owner_; }; } // namespace gkfs::rpc Loading tests/unit/test_repair_journal_lock.cpp +12 −0 Changes for tests/unit/test_repair_journal_lock.cpp: 12 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -12,6 +12,8 @@ #include <catch2/catch_all.hpp> #include <filesystem> #include <fstream> #include <sstream> #include <unistd.h> namespace { Loading @@ -35,6 +37,15 @@ TEST_CASE("repair journal lock has exclusive process ownership", first.acquire(journal.string()); REQUIRE(first.owns_lock()); REQUIRE(first.path() == lock_path); REQUIRE(first.owner().pid == ::getpid()); REQUIRE_FALSE(first.owner().hostname.empty()); REQUIRE(first.owner().acquired_at > 0); std::ifstream owner_record(lock_path); std::stringstream contents; contents << owner_record.rdbuf(); REQUIRE(contents.str().find("pid=" + std::to_string(::getpid())) != std::string::npos); gkfs::rpc::repair_journal_lock second; REQUIRE_THROWS_WITH(second.acquire(journal.string()), Loading @@ -55,4 +66,5 @@ TEST_CASE("empty repair journal path does not create a lock", gkfs::rpc::repair_journal_lock lock; REQUIRE_NOTHROW(lock.acquire("")); REQUIRE_FALSE(lock.owns_lock()); REQUIRE(lock.owner().hostname.empty()); } Loading
include/client/rpc/repair_journal_lock.hpp +43 −0 Changes for include/client/rpc/repair_journal_lock.hpp: 43 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -17,12 +17,21 @@ #include <unistd.h> #include <cerrno> #include <chrono> #include <cstdint> #include <cstring> #include <stdexcept> #include <string> #include <sys/types.h> namespace gkfs::rpc { struct repair_journal_owner { std::string hostname; pid_t pid{}; uint64_t acquired_at{}; }; /** * @brief Owns the advisory process lock for a client-local repair journal. * Loading Loading @@ -68,7 +77,34 @@ public: journal_path + "': " + ::strerror(error)); } repair_journal_owner owner; owner.hostname.resize(255); if(::gethostname(owner.hostname.data(), owner.hostname.size()) != 0) { owner.hostname = "unknown"; } else { owner.hostname.resize(std::strlen(owner.hostname.c_str())); } owner.pid = ::getpid(); owner.acquired_at = static_cast<uint64_t>( std::chrono::duration_cast<std::chrono::seconds>( std::chrono::system_clock::now().time_since_epoch()) .count()); const auto record = "hostname=" + owner.hostname + "\n" + "pid=" + std::to_string(owner.pid) + "\n" + "acquired_at=" + std::to_string(owner.acquired_at) + "\n"; if(::ftruncate(candidate.get(), 0) != 0 || ::write(candidate.get(), record.data(), record.size()) != static_cast<ssize_t>(record.size())) { const auto error = errno; throw std::runtime_error( "Unable to write replica repair journal owner record '" + journal_path + "': " + ::strerror(error)); } fd_ = std::move(candidate); owner_ = std::move(owner); } void Loading @@ -78,6 +114,7 @@ public: fd_.reset(); } lock_path_.clear(); owner_ = {}; } [[nodiscard]] bool Loading @@ -90,9 +127,15 @@ public: return lock_path_; } [[nodiscard]] const repair_journal_owner& owner() const noexcept { return owner_; } private: gkfs::utils::unique_fd fd_; std::string lock_path_; repair_journal_owner owner_; }; } // namespace gkfs::rpc Loading
tests/unit/test_repair_journal_lock.cpp +12 −0 Changes for tests/unit/test_repair_journal_lock.cpp: 12 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -12,6 +12,8 @@ #include <catch2/catch_all.hpp> #include <filesystem> #include <fstream> #include <sstream> #include <unistd.h> namespace { Loading @@ -35,6 +37,15 @@ TEST_CASE("repair journal lock has exclusive process ownership", first.acquire(journal.string()); REQUIRE(first.owns_lock()); REQUIRE(first.path() == lock_path); REQUIRE(first.owner().pid == ::getpid()); REQUIRE_FALSE(first.owner().hostname.empty()); REQUIRE(first.owner().acquired_at > 0); std::ifstream owner_record(lock_path); std::stringstream contents; contents << owner_record.rdbuf(); REQUIRE(contents.str().find("pid=" + std::to_string(::getpid())) != std::string::npos); gkfs::rpc::repair_journal_lock second; REQUIRE_THROWS_WITH(second.acquire(journal.string()), Loading @@ -55,4 +66,5 @@ TEST_CASE("empty repair journal path does not create a lock", gkfs::rpc::repair_journal_lock lock; REQUIRE_NOTHROW(lock.acquire("")); REQUIRE_FALSE(lock.owns_lock()); REQUIRE(lock.owner().hostname.empty()); }