diff --git a/librepomgr/buildactions/buildactionprivate.h b/librepomgr/buildactions/buildactionprivate.h index 03526cf..c96ba30 100644 --- a/librepomgr/buildactions/buildactionprivate.h +++ b/librepomgr/buildactions/buildactionprivate.h @@ -381,7 +381,16 @@ struct LIBREPOMGR_EXPORT CustomCommand : public InternalBuildAction { void run(); private: + void acquireNextSharedLock(); + void acquireNextExclusiveLock(); + std::string m_workingDirectory; + const std::string *m_command; + std::shared_ptr m_process; + std::set m_sharedLockNames; + std::set m_exclusiveLockNames; + std::set::iterator m_sharedLockNamesIterator; + std::set::iterator m_exclusiveLockNamesIterator; }; struct LIBREPOMGR_EXPORT ReloadDatabase : public InternalBuildAction { diff --git a/librepomgr/buildactions/customcommand.cpp b/librepomgr/buildactions/customcommand.cpp index 06b6cd0..6ccbea4 100644 --- a/librepomgr/buildactions/customcommand.cpp +++ b/librepomgr/buildactions/customcommand.cpp @@ -16,6 +16,7 @@ namespace LibRepoMgr { CustomCommand::CustomCommand(ServiceSetup &setup, const std::shared_ptr &buildAction) : InternalBuildAction(setup, buildAction) + , m_command(nullptr) { } @@ -57,43 +58,73 @@ void CustomCommand::run() m_buildAction->appendOutput(Phrases::InfoMessage, "Running custom command: ", command, '\n'); // prepare process and finish handler - auto process - = m_buildAction->makeBuildProcess("command", m_workingDirectory + "/the.log", [this](boost::process::child &&, ProcessResult &&result) { - if (result.errorCode) { - m_buildAction->appendOutput(Phrases::InfoMessage, "Unable to invoke command: ", result.errorCode.message()); - reportError(result.errorCode.message()); - return; - } - m_buildAction->appendOutput( - result.exitCode == 0 ? Phrases::InfoMessage : Phrases::ErrorMessage, "Command exited with return code ", result.exitCode); - if (result.exitCode != 0) { - reportError(argsToString("non-zero exit code ", result.exitCode)); - return; - } - const auto buildLock = m_setup.building.lockToWrite(); - reportSuccess(); - }); + m_command = &command; + m_process = m_buildAction->makeBuildProcess("command", m_workingDirectory + "/the.log", [this](boost::process::child &&, ProcessResult &&result) { + if (result.errorCode) { + m_buildAction->appendOutput(Phrases::InfoMessage, "Unable to invoke command: ", result.errorCode.message()); + reportError(result.errorCode.message()); + return; + } + m_buildAction->appendOutput( + result.exitCode == 0 ? Phrases::InfoMessage : Phrases::ErrorMessage, "Command exited with return code ", result.exitCode); + if (result.exitCode != 0) { + reportError(argsToString("non-zero exit code ", result.exitCode)); + return; + } + const auto buildLock = m_setup.building.lockToWrite(); + reportSuccess(); + }); // acquire locks // note: Using an std::set here (instead of a std::vector) to ensure we don't attempt to acquire the same lock twice and to ensure // locks are always acquired in the same order (to prevent deadlocks). - const auto sharedLockNames = splitStringSimple>(findSetting(sharedLocksSetting), ","); - const auto exclusiveLockNames = splitStringSimple>(findSetting(exclusiveLocksSetting), ","); - auto &locks = process->locks(); - auto &log = m_buildAction->log(); - locks.reserve(sharedLockNames.size() + exclusiveLockNames.size()); - for (const auto &lockName : sharedLockNames) { - if (!lockName.empty()) { - locks.emplace_back(m_setup.locks.acquireToRead(log, std::string(lockName))); - } - } - for (const auto &lockName : exclusiveLockNames) { - if (!lockName.empty()) { - locks.emplace_back(m_setup.locks.acquireToWrite(log, std::string(lockName))); - } - } + m_sharedLockNames = splitStringSimple>(findSetting(sharedLocksSetting), ","); + m_sharedLockNamesIterator = m_sharedLockNames.begin(); + m_exclusiveLockNames = splitStringSimple>(findSetting(exclusiveLocksSetting), ","); + m_exclusiveLockNamesIterator = m_exclusiveLockNames.begin(); + m_process->locks().reserve(m_sharedLockNames.size() + m_exclusiveLockNames.size()); + acquireNextSharedLock(); +} - process->launch(boost::process::start_dir(m_workingDirectory), boost::process::search_path("bash"), "-ec", command); +void LibRepoMgr::CustomCommand::acquireNextSharedLock() +{ + if (m_sharedLockNamesIterator == m_sharedLockNames.end()) { + // continue acquiring exclusive locks once all shared locks are acquired + acquireNextExclusiveLock(); + return; + } + if (m_sharedLockNamesIterator->empty()) { + ++m_sharedLockNamesIterator; + acquireNextSharedLock(); + return; + } + m_setup.locks.acquireToRead( + m_buildAction->log(), std::string(*m_sharedLockNamesIterator), [this, buildAction = m_buildAction](SharedLoggingLock &&lock) { + m_process->locks().emplace_back(std::move(lock)); + ++m_sharedLockNamesIterator; + acquireNextSharedLock(); + }); +} + +void LibRepoMgr::CustomCommand::acquireNextExclusiveLock() +{ + if (m_exclusiveLockNamesIterator == m_exclusiveLockNames.end()) { + // execute process once all locks are acquired + m_process->launch(boost::process::start_dir(m_workingDirectory), boost::process::search_path("bash"), "-ec", *m_command); + m_process.reset(); + return; + } + if (m_exclusiveLockNamesIterator->empty()) { + ++m_exclusiveLockNamesIterator; + acquireNextExclusiveLock(); + return; + } + m_setup.locks.acquireToWrite( + m_buildAction->log(), std::string(*m_exclusiveLockNamesIterator), [this, buildAction = m_buildAction](UniqueLoggingLock &&lock) { + m_process->locks().emplace_back(std::move(lock)); + ++m_exclusiveLockNamesIterator; + acquireNextExclusiveLock(); + }); } } // namespace LibRepoMgr