libstore: add async FdLock constructor
the local store doesn't have to block the entire thread waiting for a lock with such support. we'll still block *some* thread, but there is currently no good way around that without e.g. a lock manager daemon. Change-Id: I037fdc749e1b56cf9e0f1ca7ac70274442fb2b8c
This commit is contained in:
@@ -1527,7 +1527,9 @@ try {
|
||||
/* Acquire the global GC lock to get a consistent snapshot of
|
||||
existing and valid paths. */
|
||||
auto fdGCLock = openGCLock();
|
||||
FdLock gcLock(fdGCLock, ltRead, "waiting for the big garbage collector lock...");
|
||||
auto gcLock = TRY_AWAIT(
|
||||
FdLock::lockAsync(fdGCLock, ltRead, "waiting for the big garbage collector lock...")
|
||||
);
|
||||
|
||||
StorePathSet validPaths;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "lix/libstore/pathlocks.hh"
|
||||
#include "lix/libutil/async.hh"
|
||||
#include "lix/libutil/file-descriptor.hh"
|
||||
#include "lix/libutil/logging.hh"
|
||||
#include "lix/libutil/signals.hh"
|
||||
@@ -55,6 +56,47 @@ void lockFile(int fd, LockType lockType)
|
||||
}
|
||||
}
|
||||
|
||||
static kj::Promise<Result<void>> lockFileAsyncInner(int fd, LockType lockType)
|
||||
try {
|
||||
// start a thread to lock the file synchronously, waiting for SIGUSR1 to signal
|
||||
// that the call was canceled. SIGUSR1 is already set aside for such signaling.
|
||||
|
||||
int type = convertLockType(lockType);
|
||||
auto pfp = kj::newPromiseAndCrossThreadFulfiller<Result<void>>();
|
||||
|
||||
std::thread locker([&] {
|
||||
while (flock(fd, type) != 0 && pfp.fulfiller->isWaiting()) {
|
||||
if (errno != EINTR) {
|
||||
pfp.fulfiller->fulfill(std::make_exception_ptr(SysError("acquiring lock")));
|
||||
return;
|
||||
}
|
||||
}
|
||||
pfp.fulfiller->fulfill(result::success());
|
||||
});
|
||||
auto cancel = kj::defer([&] {
|
||||
pthread_kill(locker.native_handle(), SIGUSR1);
|
||||
locker.join();
|
||||
});
|
||||
|
||||
TRY_AWAIT(makeInterruptible(std::move(pfp.promise)));
|
||||
locker.join();
|
||||
cancel.cancel();
|
||||
co_return result::success();
|
||||
} catch (...) {
|
||||
co_return result::current_exception();
|
||||
}
|
||||
|
||||
kj::Promise<Result<void>> lockFileAsync(int fd, LockType lockType)
|
||||
try {
|
||||
if (tryLockFile(fd, lockType)) {
|
||||
return {result::success()};
|
||||
}
|
||||
|
||||
return lockFileAsyncInner(fd, lockType);
|
||||
} catch (...) {
|
||||
return {result::current_exception()};
|
||||
}
|
||||
|
||||
bool unsafeLockFileSingleThreaded(int fd, LockType lockType, std::chrono::seconds timeout)
|
||||
{
|
||||
int type = convertLockType(lockType);
|
||||
@@ -225,5 +267,16 @@ FdLock::FdLock(AutoCloseFD & fd, LockType lockType, std::string_view waitMsg)
|
||||
}
|
||||
}
|
||||
|
||||
kj::Promise<Result<FdLock>>
|
||||
FdLock::lockAsync(AutoCloseFD & fd, LockType lockType, std::string_view waitMsg)
|
||||
try {
|
||||
if (!tryLockFile(fd.get(), lockType)) {
|
||||
printInfo("%s", waitMsg);
|
||||
TRY_AWAIT(lockFileAsyncInner(fd.get(), lockType));
|
||||
}
|
||||
co_return FdLock{fd};
|
||||
} catch (...) {
|
||||
co_return result::current_exception();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,7 +3,10 @@
|
||||
|
||||
#include "lix/libutil/error.hh"
|
||||
#include "lix/libutil/file-descriptor.hh"
|
||||
#include "lix/libutil/result.hh"
|
||||
#include <chrono>
|
||||
#include <kj/async.h>
|
||||
#include <kj/common.h>
|
||||
|
||||
namespace nix {
|
||||
|
||||
@@ -22,6 +25,7 @@ void deleteLockFile(const Path & path, int fd);
|
||||
enum LockType { ltRead, ltWrite };
|
||||
|
||||
void lockFile(int fd, LockType lockType);
|
||||
kj::Promise<Result<void>> lockFileAsync(int fd, LockType lockType);
|
||||
/**
|
||||
* Same as `lockFile`, but with a timeout. This timeout uses the POSIX `alarm`
|
||||
* facility and a `SIGALRM` handler. Using this function from multiple threads
|
||||
@@ -73,12 +77,17 @@ class FdLock
|
||||
|
||||
std::unique_ptr<AutoCloseFD, Unlocker> fd;
|
||||
|
||||
explicit FdLock(AutoCloseFD & fd): fd(&fd) {}
|
||||
|
||||
public:
|
||||
static constexpr struct DontWait { explicit DontWait() = default; } dont_wait;
|
||||
|
||||
FdLock(AutoCloseFD & fd, LockType lockType, DontWait);
|
||||
FdLock(AutoCloseFD & fd, LockType lockType, std::string_view waitMsg);
|
||||
|
||||
static kj::Promise<Result<FdLock>>
|
||||
lockAsync(AutoCloseFD & fd, LockType lockType, std::string_view waitMsg);
|
||||
|
||||
bool valid() const { return bool(fd); }
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user