From 6e12f8922ccbd022686f219ec530a8d00573cfb2 Mon Sep 17 00:00:00 2001 From: eldritch horrors Date: Wed, 5 Feb 2025 02:12:57 +0100 Subject: [PATCH] libstore: add async path locking just single path locks for now since we don't need more. Change-Id: I0f1be156e762fbe7355dc1381295a7b276bad720 --- lix/libstore/build/local-derivation-goal.cc | 2 +- lix/libstore/pathlocks.cc | 49 +++++++++++++++------ lix/libstore/pathlocks.hh | 2 + 3 files changed, 39 insertions(+), 14 deletions(-) diff --git a/lix/libstore/build/local-derivation-goal.cc b/lix/libstore/build/local-derivation-goal.cc index 960608041..dbd8bf4f9 100644 --- a/lix/libstore/build/local-derivation-goal.cc +++ b/lix/libstore/build/local-derivation-goal.cc @@ -2301,7 +2301,7 @@ try { worker.store.printStorePath(*optFixedPath) != finalDestPath) { assert(newInfo.ca); - dynamicOutputLock = lockPath(worker.store.toRealPath(finalDestPath)); + dynamicOutputLock = TRY_AWAIT(lockPathAsync(worker.store.toRealPath(finalDestPath))); } /* Move files, if needed */ diff --git a/lix/libstore/pathlocks.cc b/lix/libstore/pathlocks.cc index b0f991235..d4f84cbe4 100644 --- a/lix/libstore/pathlocks.cc +++ b/lix/libstore/pathlocks.cc @@ -132,6 +132,25 @@ void unlockFile(int fd) } +static bool isPathLockValid(AutoCloseFD & fd, const Path & lockPath) +{ + /* Check that the lock file hasn't become stale (i.e., + hasn't been unlinked). */ + struct stat st; + if (fstat(fd.get(), &st) == -1) + throw SysError("statting lock file '%1%'", lockPath); + if (st.st_nlink == 0) { + /* This lock file has been unlinked, so we're holding + a lock on a deleted file. This means that other + processes may create and acquire a lock on + `lockPath', and proceed. So we must retry. */ + debug("open lock file '%1%' has become stale", lockPath); + return false; + } else { + return true; + } +} + std::optional PathLock::lockImpl(const Path & path, std::string_view waitMsg, bool wait) { @@ -155,23 +174,27 @@ PathLock::lockImpl(const Path & path, std::string_view waitMsg, bool wait) } debug("lock acquired on '%1%'", lockPath); - - /* Check that the lock file hasn't become stale (i.e., - hasn't been unlinked). */ - struct stat st; - if (fstat(fd.get(), &st) == -1) - throw SysError("statting lock file '%1%'", lockPath); - if (st.st_nlink == 0) - /* This lock file has been unlinked, so we're holding - a lock on a deleted file. This means that other - processes may create and acquire a lock on - `lockPath', and proceed. So we must retry. */ - debug("open lock file '%1%' has become stale", lockPath); - else + if (isPathLockValid(fd, lockPath)) return PathLock{std::move(fd), lockPath}; } } +kj::Promise> lockPathAsync(const Path & path, std::string_view waitMsg) +try { + Path lockPath = path + ".lock"; + debug("locking path '%1%'", path); + + while (1) { + auto fd = openLockFile(lockPath, true); + TRY_AWAIT(lockFileAsync(fd.get(), ltWrite)); + debug("lock acquired on '%1%'", lockPath); + if (isPathLockValid(fd, lockPath)) + co_return PathLock{std::move(fd), lockPath}; + } +} catch (...) { + co_return result::current_exception(); +} + PathLock lockPath(const Path & path, std::string_view waitMsg) { return std::move(*PathLock::lockImpl(path, waitMsg, true)); diff --git a/lix/libstore/pathlocks.hh b/lix/libstore/pathlocks.hh index a3d6cf9e1..4d985fc94 100644 --- a/lix/libstore/pathlocks.hh +++ b/lix/libstore/pathlocks.hh @@ -33,6 +33,7 @@ void unlockFile(int fd); class PathLock { + friend kj::Promise> lockPathAsync(const Path & path, std::string_view waitMsg); friend PathLock lockPath(const Path & path, std::string_view waitMsg); friend std::optional tryLockPath(const Path & path); @@ -52,6 +53,7 @@ public: void unlock(); }; +kj::Promise> lockPathAsync(const Path & path, std::string_view waitMsg = ""); PathLock lockPath(const Path & path, std::string_view waitMsg = ""); std::optional tryLockPath(const Path & path);