From 2248a3f5451adf6c098fc871fb61fae6cc2b5979 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 17 Apr 2024 11:34:09 -0400 Subject: [PATCH] Create no-op Window pathlocks implementation This keeps the call sites simple, eventually this should be filled in. --- src/build-remote/build-remote.cc | 1 + src/libstore/gc.cc | 1 + src/libstore/local-store.cc | 2 + src/libstore/local.mk | 3 + src/libstore/lock.cc | 2 + src/libstore/pathlocks.cc | 152 ----------------------- src/libstore/pathlocks.hh | 32 +---- src/libstore/unix/pathlocks-impl.hh | 38 ++++++ src/libstore/unix/pathlocks.cc | 165 +++++++++++++++++++++++++ src/libstore/windows/pathlocks-impl.hh | 2 + src/libstore/windows/pathlocks.cc | 16 +++ 11 files changed, 232 insertions(+), 182 deletions(-) create mode 100644 src/libstore/unix/pathlocks-impl.hh create mode 100644 src/libstore/unix/pathlocks.cc create mode 100644 src/libstore/windows/pathlocks-impl.hh create mode 100644 src/libstore/windows/pathlocks.cc diff --git a/src/build-remote/build-remote.cc b/src/build-remote/build-remote.cc index 18eee830b..2a4723643 100644 --- a/src/build-remote/build-remote.cc +++ b/src/build-remote/build-remote.cc @@ -22,6 +22,7 @@ #include "experimental-features.hh" using namespace nix; +using namespace nix::unix; using std::cin; static void handleAlarm(int sig) { diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index 88e943263..9b2e6d525 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -29,6 +29,7 @@ namespace nix { +using namespace nix::unix; static std::string gcSocketPath = "/gc-socket/socket"; static std::string gcRootsDir = "gcroots"; diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index a32b349a1..1593affd6 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -52,6 +52,8 @@ namespace nix { +using namespace nix::unix; + std::string LocalStoreConfig::doc() { return diff --git a/src/libstore/local.mk b/src/libstore/local.mk index ccb7aeee2..0a91fce4b 100644 --- a/src/libstore/local.mk +++ b/src/libstore/local.mk @@ -36,6 +36,9 @@ INCLUDE_libstore := -I $(d) -I $(d)/build ifdef HOST_UNIX INCLUDE_libstore += -I $(d)/unix endif +ifdef HOST_WINDOWS + INCLUDE_libstore += -I $(d)/windows +endif libstore_CXXFLAGS += \ $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libstore) \ diff --git a/src/libstore/lock.cc b/src/libstore/lock.cc index 023c74e34..fd7af171f 100644 --- a/src/libstore/lock.cc +++ b/src/libstore/lock.cc @@ -9,6 +9,8 @@ namespace nix { +using namespace nix::unix; + #if __linux__ static std::vector get_group_list(const char *username, gid_t group_id) diff --git a/src/libstore/pathlocks.cc b/src/libstore/pathlocks.cc index 2b5b8dfe7..37793db5b 100644 --- a/src/libstore/pathlocks.cc +++ b/src/libstore/pathlocks.cc @@ -6,69 +6,9 @@ #include #include -#include -#include -#include -#include - namespace nix { - -AutoCloseFD openLockFile(const Path & path, bool create) -{ - AutoCloseFD fd; - - fd = open(path.c_str(), O_CLOEXEC | O_RDWR | (create ? O_CREAT : 0), 0600); - if (!fd && (create || errno != ENOENT)) - throw SysError("opening lock file '%1%'", path); - - return fd; -} - - -void deleteLockFile(const Path & path, int fd) -{ - /* Get rid of the lock file. Have to be careful not to introduce - races. Write a (meaningless) token to the file to indicate to - other processes waiting on this lock that the lock is stale - (deleted). */ - unlink(path.c_str()); - writeFull(fd, "d"); - /* Note that the result of unlink() is ignored; removing the lock - file is an optimisation, not a necessity. */ -} - - -bool lockFile(int fd, LockType lockType, bool wait) -{ - int type; - if (lockType == ltRead) type = LOCK_SH; - else if (lockType == ltWrite) type = LOCK_EX; - else if (lockType == ltNone) type = LOCK_UN; - else abort(); - - if (wait) { - while (flock(fd, type) != 0) { - checkInterrupt(); - if (errno != EINTR) - throw SysError("acquiring/releasing lock"); - else - return false; - } - } else { - while (flock(fd, type | LOCK_NB) != 0) { - checkInterrupt(); - if (errno == EWOULDBLOCK) return false; - if (errno != EINTR) - throw SysError("acquiring/releasing lock"); - } - } - - return true; -} - - PathLocks::PathLocks() : deletePaths(false) { @@ -82,68 +22,6 @@ PathLocks::PathLocks(const PathSet & paths, const std::string & waitMsg) } -bool PathLocks::lockPaths(const PathSet & paths, - const std::string & waitMsg, bool wait) -{ - assert(fds.empty()); - - /* Note that `fds' is built incrementally so that the destructor - will only release those locks that we have already acquired. */ - - /* Acquire the lock for each path in sorted order. This ensures - that locks are always acquired in the same order, thus - preventing deadlocks. */ - for (auto & path : paths) { - checkInterrupt(); - Path lockPath = path + ".lock"; - - debug("locking path '%1%'", path); - - AutoCloseFD fd; - - while (1) { - - /* Open/create the lock file. */ - fd = openLockFile(lockPath, true); - - /* Acquire an exclusive lock. */ - if (!lockFile(fd.get(), ltWrite, false)) { - if (wait) { - if (waitMsg != "") printError(waitMsg); - lockFile(fd.get(), ltWrite, true); - } else { - /* Failed to lock this path; release all other - locks. */ - unlock(); - return false; - } - } - - 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_size != 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 - break; - } - - /* Use borrow so that the descriptor isn't closed. */ - fds.push_back(FDPair(fd.release(), lockPath)); - } - - return true; -} - - PathLocks::~PathLocks() { try { @@ -154,40 +32,10 @@ PathLocks::~PathLocks() } -void PathLocks::unlock() -{ - for (auto & i : fds) { - if (deletePaths) deleteLockFile(i.second, i.first); - - if (close(i.first) == -1) - printError( - "error (ignored): cannot close lock file on '%1%'", - i.second); - - debug("lock released on '%1%'", i.second); - } - - fds.clear(); -} - - void PathLocks::setDeletion(bool deletePaths) { this->deletePaths = deletePaths; } -FdLock::FdLock(int fd, LockType lockType, bool wait, std::string_view waitMsg) - : fd(fd) -{ - if (wait) { - if (!lockFile(fd, lockType, false)) { - printInfo("%s", waitMsg); - acquired = lockFile(fd, lockType, true); - } - } else - acquired = lockFile(fd, lockType, false); -} - - } diff --git a/src/libstore/pathlocks.hh b/src/libstore/pathlocks.hh index 7fcfa2e40..b97fbecb9 100644 --- a/src/libstore/pathlocks.hh +++ b/src/libstore/pathlocks.hh @@ -5,22 +5,6 @@ namespace nix { -/** - * Open (possibly create) a lock file and return the file descriptor. - * -1 is returned if create is false and the lock could not be opened - * because it doesn't exist. Any other error throws an exception. - */ -AutoCloseFD openLockFile(const Path & path, bool create); - -/** - * Delete an open lock file. - */ -void deleteLockFile(const Path & path, int fd); - -enum LockType { ltRead, ltWrite, ltNone }; - -bool lockFile(int fd, LockType lockType, bool wait); - class PathLocks { private: @@ -40,18 +24,6 @@ public: void setDeletion(bool deletePaths); }; -struct FdLock -{ - int fd; - bool acquired = false; - - FdLock(int fd, LockType lockType, bool wait, std::string_view waitMsg); - - ~FdLock() - { - if (acquired) - lockFile(fd, ltNone, false); - } -}; - } + +#include "pathlocks-impl.hh" diff --git a/src/libstore/unix/pathlocks-impl.hh b/src/libstore/unix/pathlocks-impl.hh new file mode 100644 index 000000000..31fe968bb --- /dev/null +++ b/src/libstore/unix/pathlocks-impl.hh @@ -0,0 +1,38 @@ +#pragma once +///@file + +#include "file-descriptor.hh" + +namespace nix::unix { + +/** + * Open (possibly create) a lock file and return the file descriptor. + * -1 is returned if create is false and the lock could not be opened + * because it doesn't exist. Any other error throws an exception. + */ +AutoCloseFD openLockFile(const Path & path, bool create); + +/** + * Delete an open lock file. + */ +void deleteLockFile(const Path & path, int fd); + +enum LockType { ltRead, ltWrite, ltNone }; + +bool lockFile(int fd, LockType lockType, bool wait); + +struct FdLock +{ + int fd; + bool acquired = false; + + FdLock(int fd, LockType lockType, bool wait, std::string_view waitMsg); + + ~FdLock() + { + if (acquired) + lockFile(fd, ltNone, false); + } +}; + +} diff --git a/src/libstore/unix/pathlocks.cc b/src/libstore/unix/pathlocks.cc new file mode 100644 index 000000000..32c1b9ff4 --- /dev/null +++ b/src/libstore/unix/pathlocks.cc @@ -0,0 +1,165 @@ +#include "pathlocks.hh" +#include "util.hh" +#include "sync.hh" +#include "signals.hh" + +#include +#include + +#include +#include +#include +#include + + +namespace nix { + +using namespace nix::unix; + +AutoCloseFD unix::openLockFile(const Path & path, bool create) +{ + AutoCloseFD fd; + + fd = open(path.c_str(), O_CLOEXEC | O_RDWR | (create ? O_CREAT : 0), 0600); + if (!fd && (create || errno != ENOENT)) + throw SysError("opening lock file '%1%'", path); + + return fd; +} + + +void unix::deleteLockFile(const Path & path, int fd) +{ + /* Get rid of the lock file. Have to be careful not to introduce + races. Write a (meaningless) token to the file to indicate to + other processes waiting on this lock that the lock is stale + (deleted). */ + unlink(path.c_str()); + writeFull(fd, "d"); + /* Note that the result of unlink() is ignored; removing the lock + file is an optimisation, not a necessity. */ +} + + +bool unix::lockFile(int fd, LockType lockType, bool wait) +{ + int type; + if (lockType == ltRead) type = LOCK_SH; + else if (lockType == ltWrite) type = LOCK_EX; + else if (lockType == ltNone) type = LOCK_UN; + else abort(); + + if (wait) { + while (flock(fd, type) != 0) { + checkInterrupt(); + if (errno != EINTR) + throw SysError("acquiring/releasing lock"); + else + return false; + } + } else { + while (flock(fd, type | LOCK_NB) != 0) { + checkInterrupt(); + if (errno == EWOULDBLOCK) return false; + if (errno != EINTR) + throw SysError("acquiring/releasing lock"); + } + } + + return true; +} + + +bool PathLocks::lockPaths(const PathSet & paths, + const std::string & waitMsg, bool wait) +{ + assert(fds.empty()); + + /* Note that `fds' is built incrementally so that the destructor + will only release those locks that we have already acquired. */ + + /* Acquire the lock for each path in sorted order. This ensures + that locks are always acquired in the same order, thus + preventing deadlocks. */ + for (auto & path : paths) { + checkInterrupt(); + Path lockPath = path + ".lock"; + + debug("locking path '%1%'", path); + + AutoCloseFD fd; + + while (1) { + + /* Open/create the lock file. */ + fd = openLockFile(lockPath, true); + + /* Acquire an exclusive lock. */ + if (!lockFile(fd.get(), ltWrite, false)) { + if (wait) { + if (waitMsg != "") printError(waitMsg); + lockFile(fd.get(), ltWrite, true); + } else { + /* Failed to lock this path; release all other + locks. */ + unlock(); + return false; + } + } + + 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_size != 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 + break; + } + + /* Use borrow so that the descriptor isn't closed. */ + fds.push_back(FDPair(fd.release(), lockPath)); + } + + return true; +} + + +void PathLocks::unlock() +{ + for (auto & i : fds) { + if (deletePaths) deleteLockFile(i.second, i.first); + + if (close(i.first) == -1) + printError( + "error (ignored): cannot close lock file on '%1%'", + i.second); + + debug("lock released on '%1%'", i.second); + } + + fds.clear(); +} + + +FdLock::FdLock(int fd, LockType lockType, bool wait, std::string_view waitMsg) + : fd(fd) +{ + if (wait) { + if (!lockFile(fd, lockType, false)) { + printInfo("%s", waitMsg); + acquired = lockFile(fd, lockType, true); + } + } else + acquired = lockFile(fd, lockType, false); +} + + +} diff --git a/src/libstore/windows/pathlocks-impl.hh b/src/libstore/windows/pathlocks-impl.hh new file mode 100644 index 000000000..ba3ad28d9 --- /dev/null +++ b/src/libstore/windows/pathlocks-impl.hh @@ -0,0 +1,2 @@ +#pragma once +///@file Needed because Unix-specific counterpart diff --git a/src/libstore/windows/pathlocks.cc b/src/libstore/windows/pathlocks.cc new file mode 100644 index 000000000..ab4294c2a --- /dev/null +++ b/src/libstore/windows/pathlocks.cc @@ -0,0 +1,16 @@ +#include "logging.hh" +#include "pathlocks.hh" + +namespace nix { + +bool PathLocks::lockPaths(const PathSet & _paths, const std::string & waitMsg, bool wait) +{ + return true; +} + +void PathLocks::unlock() +{ + warn("PathLocks::unlock: not yet implemented"); +} + +}