libstore: add async path locking

just single path locks for now since we don't need more.

Change-Id: I0f1be156e762fbe7355dc1381295a7b276bad720
This commit is contained in:
eldritch horrors
2025-02-08 12:45:15 +00:00
parent ec44c8bd6c
commit 6e12f8922c
3 changed files with 39 additions and 14 deletions
+1 -1
View File
@@ -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 */
+36 -13
View File
@@ -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>
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<Result<PathLock>> 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));
+2
View File
@@ -33,6 +33,7 @@ void unlockFile(int fd);
class PathLock
{
friend kj::Promise<Result<PathLock>> lockPathAsync(const Path & path, std::string_view waitMsg);
friend PathLock lockPath(const Path & path, std::string_view waitMsg);
friend std::optional<PathLock> tryLockPath(const Path & path);
@@ -52,6 +53,7 @@ public:
void unlock();
};
kj::Promise<Result<PathLock>> lockPathAsync(const Path & path, std::string_view waitMsg = "");
PathLock lockPath(const Path & path, std::string_view waitMsg = "");
std::optional<PathLock> tryLockPath(const Path & path);