mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-04-29 14:59:39 -04:00
util: Add RAII directory lock
This makes it easier for a class or a struct to own a lock on a directory for the duration of its lifetime. It is used in the next commit.
This commit is contained in:
parent
de844c79d4
commit
21300478d9
3 changed files with 86 additions and 0 deletions
|
@ -1291,6 +1291,30 @@ BOOST_AUTO_TEST_CASE(test_LockDirectory)
|
|||
BOOST_CHECK_EQUAL(processstatus, 0);
|
||||
BOOST_CHECK_EQUAL(util::LockDirectory(dirname, lockname, true), util::LockResult::Success);
|
||||
|
||||
{
|
||||
auto lock{DirectoryLock(dirname, "test")};
|
||||
BOOST_CHECK_THROW(DirectoryLock(dirname, "test"), std::runtime_error);
|
||||
}
|
||||
{
|
||||
BOOST_CHECK_NO_THROW(DirectoryLock(dirname, "test"));
|
||||
}
|
||||
|
||||
{
|
||||
DirectoryLock lock1(dirname, "test");
|
||||
DirectoryLock lock2(std::move(lock1));
|
||||
BOOST_CHECK_THROW(DirectoryLock(dirname, "test"), std::runtime_error);
|
||||
}
|
||||
|
||||
{
|
||||
auto dirname_move = dirname / "move";
|
||||
fs::create_directories(dirname_move);
|
||||
DirectoryLock lock1(dirname, "test");
|
||||
DirectoryLock lock2(dirname_move, "test");
|
||||
lock2 = std::move(lock1);
|
||||
BOOST_CHECK_THROW(DirectoryLock(dirname, "test"), std::runtime_error);
|
||||
BOOST_CHECK_NO_THROW(DirectoryLock(dirname_move, "test"));
|
||||
}
|
||||
|
||||
// Restore SIGCHLD
|
||||
signal(SIGCHLD, old_handler);
|
||||
BOOST_CHECK_EQUAL(close(fd[1]), 0); // Close our side of the socketpair
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <sync.h>
|
||||
#include <util/fs.h>
|
||||
#include <util/syserror.h>
|
||||
#include <util/translation.h>
|
||||
|
||||
#include <cerrno>
|
||||
#include <fstream>
|
||||
|
@ -91,6 +92,50 @@ void ReleaseDirectoryLocks()
|
|||
dir_locks.clear();
|
||||
}
|
||||
|
||||
DirectoryLock::DirectoryLock(fs::path dir_path, std::string name)
|
||||
: m_path{dir_path},
|
||||
m_name{name}
|
||||
{
|
||||
// Ensures only a single lock is taken on the provided directory.
|
||||
switch (util::LockDirectory(m_path, ".lock", false)) {
|
||||
case util::LockResult::ErrorWrite:
|
||||
throw std::runtime_error(strprintf(_("Cannot write to %s directory '%s'; check permissions."), m_name, fs::PathToString(m_path)).original);
|
||||
case util::LockResult::ErrorLock:
|
||||
throw std::runtime_error(strprintf(_("Cannot obtain a lock on %s directory %s. %s is probably already running."), m_name, fs::PathToString(m_path), CLIENT_NAME).original);
|
||||
case util::LockResult::Success:
|
||||
return;
|
||||
} // no default case, so the compiler can warn about missing cases
|
||||
assert(false);
|
||||
}
|
||||
|
||||
DirectoryLock::DirectoryLock(DirectoryLock&& other) noexcept
|
||||
: m_path{std::move(other.m_path)},
|
||||
m_name{std::move(other.m_name)}
|
||||
{
|
||||
other.m_path.clear();
|
||||
other.m_name.clear();
|
||||
}
|
||||
|
||||
DirectoryLock& DirectoryLock::operator=(DirectoryLock&& other) noexcept
|
||||
{
|
||||
if (this != &other) {
|
||||
if (!m_path.empty()) {
|
||||
UnlockDirectory(m_path, ".lock");
|
||||
}
|
||||
m_path = std::move(other.m_path);
|
||||
other.m_path.clear();
|
||||
m_name = std::move(other.m_name);
|
||||
other.m_name.clear();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
DirectoryLock::~DirectoryLock()
|
||||
{
|
||||
if (!m_path.empty()) UnlockDirectory(m_path, ".lock");
|
||||
}
|
||||
|
||||
bool CheckDiskSpace(const fs::path& dir, uint64_t additional_bytes)
|
||||
{
|
||||
constexpr uint64_t min_disk_space = 52428800; // 50 MiB
|
||||
|
|
|
@ -45,6 +45,23 @@ enum class LockResult {
|
|||
[[nodiscard]] LockResult LockDirectory(const fs::path& directory, const fs::path& lockfile_name, bool probe_only = false);
|
||||
} // namespace util
|
||||
void UnlockDirectory(const fs::path& directory, const fs::path& lockfile_name);
|
||||
|
||||
class DirectoryLock
|
||||
{
|
||||
fs::path m_path;
|
||||
std::string m_name;
|
||||
|
||||
public:
|
||||
explicit DirectoryLock(fs::path dir_path, std::string name);
|
||||
~DirectoryLock();
|
||||
|
||||
DirectoryLock(const DirectoryLock&) = delete;
|
||||
DirectoryLock& operator=(const DirectoryLock&) = delete;
|
||||
|
||||
DirectoryLock(DirectoryLock&& other) noexcept;
|
||||
DirectoryLock& operator=(DirectoryLock&& other) noexcept;
|
||||
};
|
||||
|
||||
bool CheckDiskSpace(const fs::path& dir, uint64_t additional_bytes = 0);
|
||||
|
||||
/** Get the size of a file by scanning it.
|
||||
|
|
Loading…
Add table
Reference in a new issue