diff --git a/librepomgr/buildactions/buildactionmeta.cpp b/librepomgr/buildactions/buildactionmeta.cpp index 3101cda..6917721 100644 --- a/librepomgr/buildactions/buildactionmeta.cpp +++ b/librepomgr/buildactions/buildactionmeta.cpp @@ -306,6 +306,16 @@ BuildActionMetaInfo::BuildActionMetaInfo() .desc = "The command to execute via Bash", .param = "cmd", }, + BuildActionSettingMetaInfo{ + .name = "Shared locks", + .desc = "A comma-separated list of shared lock names to acquire", + .param = "shared-locks", + }, + BuildActionSettingMetaInfo{ + .name = "Unique locks", + .desc = "A comma-separated list of exclusive lock names to acquire", + .param = "exclusive-locks", + }, }, .directory = true, .sourceDb = false, diff --git a/librepomgr/buildactions/buildactionmeta.h b/librepomgr/buildactions/buildactionmeta.h index 2b165f9..edb1bdc 100644 --- a/librepomgr/buildactions/buildactionmeta.h +++ b/librepomgr/buildactions/buildactionmeta.h @@ -81,7 +81,7 @@ enum class CleanRepositoryFlags : BuildActionFlagType { enum class CheckForProblemsSettings : std::size_t { IgnoreDeps, IgnoreLibDeps }; enum class PrepareBuildSettings : std::size_t { PKGBUILDsDirs }; enum class ConductBuildSettings : std::size_t { ChrootDir, ChrootDefaultUser, CCacheDir, PackageCacheDir, TestFilesDir }; -enum class CustomCommandSettings : std::size_t { Command }; +enum class CustomCommandSettings : std::size_t { Command, SharedLocks, ExclusiveLocks }; struct LIBREPOMGR_EXPORT BuildActionFlagMetaInfo : public ReflectiveRapidJSON::JsonSerializable { const BuildActionFlagType id = 0; diff --git a/librepomgr/buildactions/customcommand.cpp b/librepomgr/buildactions/customcommand.cpp index 6fa0f3d..05dedb2 100644 --- a/librepomgr/buildactions/customcommand.cpp +++ b/librepomgr/buildactions/customcommand.cpp @@ -34,6 +34,8 @@ void CustomCommand::run() auto metaInfoLock = metaInfo.lockToRead(); const auto &typeInfo = metaInfo.typeInfoForId(BuildActionType::CustomCommand); const auto commandSetting = typeInfo.settings[static_cast(CustomCommandSettings::Command)].param; + const auto sharedLocksSetting = typeInfo.settings[static_cast(CustomCommandSettings::SharedLocks)].param; + const auto exclusiveLocksSetting = typeInfo.settings[static_cast(CustomCommandSettings::ExclusiveLocks)].param; metaInfoLock.unlock(); const auto &command = findSetting(commandSetting); if (command.empty()) { @@ -54,7 +56,7 @@ void CustomCommand::run() m_buildAction->appendOutput(Phrases::InfoMessage, "Running custom command: ", command, '\n'); - // launch process, pass finish handler + // prepare process and finish handler auto process = m_buildAction->makeBuildProcess("command", m_workingDirectory + "/the.log", [this](boost::process::child &&, ProcessResult &&result) { if (result.errorCode) { @@ -71,6 +73,25 @@ void CustomCommand::run() 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(); + locks.reserve(sharedLockNames.size() + exclusiveLockNames.size()); + for (const auto &lockName : sharedLockNames) { + if (!lockName.empty()) { + locks.emplace_back(m_setup.locks.acquireToRead(lockName)); + } + } + for (const auto &lockName : exclusiveLockNames) { + if (!lockName.empty()) { + locks.emplace_back(m_setup.locks.acquireToWrite(lockName)); + } + } + process->launch(boost::process::start_dir(m_workingDirectory), boost::process::search_path("bash"), "-ec", command); }