diff --git a/libpkg/data/lockable.h b/libpkg/data/lockable.h index 27e4e30..309d51e 100644 --- a/libpkg/data/lockable.h +++ b/libpkg/data/lockable.h @@ -9,6 +9,8 @@ namespace LibPkg { struct Lockable { [[nodiscard]] std::shared_lock lockToRead() const; [[nodiscard]] std::unique_lock lockToWrite(); + [[nodiscard]] std::shared_lock tryLockToRead() const; + [[nodiscard]] std::unique_lock tryLockToWrite(); [[nodiscard]] std::unique_lock lockToWrite(std::shared_lock &readLock); private: @@ -25,6 +27,16 @@ inline std::unique_lock Lockable::lockToWrite() return std::unique_lock(m_mutex); } +inline std::shared_lock Lockable::tryLockToRead() const +{ + return std::shared_lock(m_mutex, std::try_to_lock); +} + +inline std::unique_lock Lockable::tryLockToWrite() +{ + return std::unique_lock(m_mutex, std::try_to_lock); +} + inline std::unique_lock Lockable::lockToWrite(std::shared_lock &readLock) { readLock.unlock(); diff --git a/librepomgr/buildactions/buildaction.h b/librepomgr/buildactions/buildaction.h index 8737a24..b48fb54 100644 --- a/librepomgr/buildactions/buildaction.h +++ b/librepomgr/buildactions/buildaction.h @@ -60,6 +60,8 @@ class Session; struct InternalBuildAction; +using AssociatedLocks = std::vector, std::unique_lock>>; + struct LIBREPOMGR_EXPORT PackageBuildData : public ReflectiveRapidJSON::JsonSerializable, public ReflectiveRapidJSON::BinarySerializable { std::string existingVersion; @@ -200,7 +202,8 @@ public: void setStopHandler(std::function &&stopHandler); void setConcludeHandler(std::function &&concludeHandler); std::shared_ptr findBuildProcess(const std::string &filePath); - std::shared_ptr makeBuildProcess(std::string &&displayName, std::string &&logFilePath, ProcessHandler &&handler); + std::shared_ptr makeBuildProcess( + std::string &&displayName, std::string &&logFilePath, ProcessHandler &&handler, AssociatedLocks &&locks = AssociatedLocks()); void terminateOngoingBuildProcesses(); void streamFile(const WebAPI::Params ¶ms, const std::string &filePath, std::string_view fileMimeType); void streamOutput(const WebAPI::Params ¶ms, std::size_t offset = 0); diff --git a/librepomgr/buildactions/buildactionlivestreaming.cpp b/librepomgr/buildactions/buildactionlivestreaming.cpp index 5cd5eaa..7d37f0d 100644 --- a/librepomgr/buildactions/buildactionlivestreaming.cpp +++ b/librepomgr/buildactions/buildactionlivestreaming.cpp @@ -328,7 +328,8 @@ void BufferSearch::operator()(const BuildProcessSession::BufferType &buffer, std } } -std::shared_ptr BuildAction::makeBuildProcess(std::string &&displayName, std::string &&logFilePath, ProcessHandler &&handler) +std::shared_ptr BuildAction::makeBuildProcess( + std::string &&displayName, std::string &&logFilePath, ProcessHandler &&handler, AssociatedLocks &&locks) { const auto processesLock = std::lock_guard(m_processesMutex); auto &process = m_ongoingProcesses[logFilePath]; @@ -342,8 +343,8 @@ std::shared_ptr BuildAction::makeBuildProcess(std::string & logfiles.emplace_back(logFilePath); } buildLock.unlock(); - return process - = make_shared(this, m_setup->building.ioContext, std::move(displayName), std::move(logFilePath), std::move(handler)); + return process = make_shared( + this, m_setup->building.ioContext, std::move(displayName), std::move(logFilePath), std::move(handler), std::move(locks)); } void BuildAction::terminateOngoingBuildProcesses() diff --git a/librepomgr/buildactions/buildactionprivate.h b/librepomgr/buildactions/buildactionprivate.h index a2d9a4c..fd58c13 100644 --- a/librepomgr/buildactions/buildactionprivate.h +++ b/librepomgr/buildactions/buildactionprivate.h @@ -133,11 +133,12 @@ public: using BufferPoolType = BufferPool; using BufferType = BufferPoolType::BufferType; - explicit BuildProcessSession( - BuildAction *buildAction, boost::asio::io_context &ioContext, std::string &&displayName, std::string &&logFilePath, Handler &&handler); + explicit BuildProcessSession(BuildAction *buildAction, boost::asio::io_context &ioContext, std::string &&displayName, std::string &&logFilePath, + Handler &&handler, AssociatedLocks &&locks = AssociatedLocks()); template void launch(ChildArgs &&...childArgs); void registerWebSession(std::shared_ptr &&webSession); void registerNewDataHandler(std::function &&handler); + void assignLocks(AssociatedLocks &&locks = AssociatedLocks()); bool hasExited() const; private: @@ -186,6 +187,7 @@ private: BuffersToWrite m_logFileBuffers; std::unordered_map, std::unique_ptr> m_registeredWebSessions; std::function m_newDataHandler; + AssociatedLocks m_locks; std::atomic_bool m_exited = false; }; @@ -201,17 +203,23 @@ inline std::size_t BuildProcessSession::DataForWebSession::bytesToSendFromFile() } inline BuildProcessSession::BuildProcessSession(BuildAction *buildAction, boost::asio::io_context &ioContext, std::string &&displayName, - std::string &&logFilePath, BaseProcessSession::Handler &&handler) + std::string &&logFilePath, BaseProcessSession::Handler &&handler, AssociatedLocks &&locks) : BaseProcessSession(ioContext, std::move(handler)) , m_buildAction(buildAction ? buildAction->weak_from_this() : std::weak_ptr()) , m_pipe(ioContext) , m_bufferPool(bufferSize) - , m_displayName(displayName) + , m_displayName(std::move(displayName)) , m_logFilePath(std::move(logFilePath)) , m_logFileDescriptor(ioContext) + , m_locks(std::move(locks)) { } +inline void BuildProcessSession::assignLocks(AssociatedLocks &&locks) +{ + m_locks = std::move(locks); +} + inline bool BuildProcessSession::hasExited() const { return m_exited.load(); diff --git a/librepomgr/serversetup.cpp b/librepomgr/serversetup.cpp index 24d793e..98f0d1f 100644 --- a/librepomgr/serversetup.cpp +++ b/librepomgr/serversetup.cpp @@ -343,6 +343,10 @@ void ServiceSetup::loadConfigFiles(bool restoreStateAndDiscardDatabases) deduplicateVector(db.mirrors); } + // clear unused locks because locks might not be useful anymore with the new config (e.g. the lock was for a + // repository directory which has been removed) + locks.clear(); // FIXME: Do this regularly to avoid the locks table growing potentially endlessly? + // log the most important config values cerr << Phrases::InfoMessage << "Working directory: " << workingDirectory << Phrases::EndFlush; cerr << Phrases::InfoMessage << "Package cache directory: " << building.packageCacheDir << Phrases::EndFlush; @@ -648,6 +652,17 @@ void ServiceSetup::run() saveState(); } +void ServiceSetup::Locks::clear() +{ + const auto lock = std::lock_guard(m_mutex); + for (auto i = m_locksByName.begin(), end = m_locksByName.end(); i != end; ++i) { + if (auto lock2 = i->second.tryLockToWrite()) { // check whether nobody holds the lock anymore + lock2.unlock(); // ~shared_mutex(): The behavior is undefined if the mutex is owned by any thread [...]. + m_locksByName.erase(i); // we can be sure no other thead aquires i->second in the meantime because we're holding m_mutex + } + } +} + ServiceStatus::ServiceStatus(const ServiceSetup &setup) : version(applicationInfo.version) , config(setup.config.computeStatus()) diff --git a/librepomgr/serversetup.h b/librepomgr/serversetup.h index f50e7b6..c6d7b23 100644 --- a/librepomgr/serversetup.h +++ b/librepomgr/serversetup.h @@ -120,6 +120,17 @@ struct LIBREPOMGR_EXPORT ServiceSetup : public LibPkg::Lockable { UserPermissions authenticate(std::string_view authorizationHeader) const; } auth; + struct LIBREPOMGR_EXPORT Locks { + [[nodiscard]] std::shared_lock acquireToRead(const std::string &lockName); + [[nodiscard]] std::unique_lock acquireToWrite(const std::string &lockName); + [[nodiscard]] std::unique_lock acquireToWrite(std::shared_lock &readLock, const std::string &lockName); + void clear(); + + private: + std::mutex m_mutex; + std::unordered_map m_locksByName; + } locks; + void loadConfigFiles(bool restoreStateAndDiscardDatabases); void printDatabases(); std::string_view cacheFilePath() const; @@ -136,6 +147,25 @@ inline std::shared_ptr ServiceSetup::BuildSetup::getBuildAction(Bui return id < actions.size() ? actions[id] : nullptr; } +inline std::shared_lock ServiceSetup::Locks::acquireToRead(const std::string &lockName) +{ + const auto lock = std::lock_guard(m_mutex); + return m_locksByName[lockName].lockToRead(); +} + +inline std::unique_lock ServiceSetup::Locks::acquireToWrite(const std::string &lockName) +{ + const auto lock = std::lock_guard(m_mutex); + return m_locksByName[lockName].lockToWrite(); +} + +inline std::unique_lock ServiceSetup::Locks::acquireToWrite( + std::shared_lock &readLock, const std::string &lockName) +{ + readLock.unlock(); + return acquireToWrite(lockName); +} + struct LIBREPOMGR_EXPORT ServiceStatus : public ReflectiveRapidJSON::JsonSerializable { ServiceStatus(const ServiceSetup &setup);