From b2cd185e8b9e51af3ab0691b299d0eaeedb0dc55 Mon Sep 17 00:00:00 2001 From: shegazyy Date: Mon, 6 Jul 2026 14:58:30 +0300 Subject: [PATCH 1/3] Extend cpu affinity mask to support up to 64 cores The cpu_mask_ field in OsalConfig and the setaffinity() function used uint32_t, limiting process affinity to at most 32 cores. Systems with more than 32 cores would silently use only the lower 32 bits. Change the mask type to uint64_t throughout the affected chain: - OsalConfig::cpu_mask_ (iprocess.hpp) - setaffinity() signature (set_affinity.hpp and both platform impls) - kDefaultProcessorAffinityMask() return type (configuration_manager) - kDefaultNumCores cap raised from 32 to 64 (num_cores.hpp) On Linux the loop variable and shift literal are updated to uint64_t / 1ULL to avoid the undefined behaviour for i >= 32 noted in the original review discussion (PR #113). On QNX the 32-bit ThreadCtl(_NTO_TCTL_RUNMASK_GET_AND_SET) is replaced with _NTO_TCTL_RUNMASK64_GET_AND_SET which accepts a uint64_t mask. kDefaultProcessorAffinityMask() guards against the n == 64 edge case (where 1ULL << 64 would itself be UB) by returning UINT64_MAX instead. The XML configuration reader is updated to use std::stoull so the full 64-bit coremask value is parsed without truncation. Closes #122 --- .../configuration/configuration_manager.cpp | 592 ++++++++++++------ .../configuration/configuration_manager.hpp | 161 +++-- .../src/osal/details/linux/set_affinity.cpp | 28 +- .../src/osal/details/qnx/set_affinity.cpp | 19 +- .../src/daemon/src/osal/num_cores.hpp | 17 +- .../src/daemon/src/osal/set_affinity.hpp | 24 +- .../src/process_group_manager/iprocess.hpp | 124 ++-- 7 files changed, 619 insertions(+), 346 deletions(-) diff --git a/score/launch_manager/src/daemon/src/configuration/configuration_manager.cpp b/score/launch_manager/src/daemon/src/configuration/configuration_manager.cpp index 28147d701..2a20367d4 100644 --- a/score/launch_manager/src/daemon/src/configuration/configuration_manager.cpp +++ b/score/launch_manager/src/daemon/src/configuration/configuration_manager.cpp @@ -11,12 +11,12 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -#include #include "score/mw/lifecycle/execution_error.h" +#include -#include "score/mw/launch_manager/configuration/configuration_manager.hpp" -#include "score/mw/launch_manager/common/process_group_state_id.hpp" #include "score/mw/launch_manager/common/log.hpp" +#include "score/mw/launch_manager/common/process_group_state_id.hpp" +#include "score/mw/launch_manager/configuration/configuration_manager.hpp" #include "score/mw/launch_manager/osal/num_cores.hpp" #include @@ -24,12 +24,14 @@ using namespace std; using namespace LMFlatBuffer; -namespace { +namespace +{ /// @brief Retrieves the resource limits configuration from the given config /// node. bool setResourceLimits(const LMFlatBuffer::ProcessStartupConfig& startup_config_node, - score::lcm::internal::OsProcess& instance) { + score::lcm::internal::OsProcess& instance) +{ // not supported currently instance.startup_config_.resource_limits_.stack_ = 0U; // don't set the stack limit instance.startup_config_.resource_limits_.cpu_ = 0U; // no limit for cpu time @@ -42,15 +44,16 @@ bool setResourceLimits(const LMFlatBuffer::ProcessStartupConfig& startup_config_ instance.startup_config_.resource_limits_.as_ = startup_config_node.memoryUsage(); return true; - } -std::unique_ptr read_flatbuffer_file(const std::string& f_filename_r) { +std::unique_ptr read_flatbuffer_file(const std::string& f_filename_r) +{ const std::string configFilePath = std::string("etc/") + f_filename_r.c_str(); std::ifstream infile; infile.open(configFilePath, std::ios::binary | std::ios::in); - if (!infile.is_open()) { + if (!infile.is_open()) + { return nullptr; } infile.seekg(0, std::ios::end); @@ -64,79 +67,99 @@ std::unique_ptr read_flatbuffer_file(const std::string& f_filename_r) { } // namespace -namespace score { +namespace score +{ -namespace lcm { +namespace lcm +{ -namespace internal { +namespace internal +{ const char* ConfigurationManager::PROCESS_RUNNING_STATE = "Running"; const char* ConfigurationManager::PROCESS_TERMINATED_STATE = "Terminated"; -constexpr const char* kEnvVarName = "ECUCFG_ENV_VAR_ROOTFOLDER"; ///< Environment variable name +constexpr const char* kEnvVarName = "ECUCFG_ENV_VAR_ROOTFOLDER"; ///< Environment variable name constexpr const char* kEnvVarDefaultValue = "/opt/internal/launch_manager/etc/ecu-cfg"; ///< Environment variable value const uint32_t ConfigurationManager::kDefaultProcessExecutionError = 1U; -uint32_t ConfigurationManager::kDefaultProcessorAffinityMask() { - return static_cast((1ULL << osal::getNumCores()) - 1ULL); +uint64_t ConfigurationManager::kDefaultProcessorAffinityMask() +{ + const auto n = osal::getNumCores(); + return (n >= 64U) ? UINT64_MAX : ((1ULL << n) - 1ULL); } const int32_t ConfigurationManager::kDefaultSchedulingPolicy = SCHED_OTHER; const int32_t ConfigurationManager::kDefaultRealtimeSchedulingPriority = 99; const int32_t ConfigurationManager::kDefaultNormalSchedulingPriority = 0; -std::optional ConfigurationManager::getNumberOfOsProcesses(const IdentifierHash& pg_name) const { +std::optional ConfigurationManager::getNumberOfOsProcesses(const IdentifierHash& pg_name) const +{ std::optional numberOfProcesses = std::nullopt; auto pg = getProcessGroupByID(pg_name); - if (pg) { + if (pg) + { numberOfProcesses = std::optional(pg->processes_.size()); } return numberOfProcesses; } -IdentifierHash ConfigurationManager::getNameOfOffState(const IdentifierHash& pg_name) const { +IdentifierHash ConfigurationManager::getNameOfOffState(const IdentifierHash& pg_name) const +{ IdentifierHash nameOfOffState{"Off"}; auto pg = getProcessGroupByID(pg_name); - if (pg) { + if (pg) + { nameOfOffState = pg->off_state_; } return nameOfOffState; } -IdentifierHash ConfigurationManager::getNameOfRecoveryState(const IdentifierHash& pg_name) const { +IdentifierHash ConfigurationManager::getNameOfRecoveryState(const IdentifierHash& pg_name) const +{ IdentifierHash nameOfRecoveryState{"Recovery"}; auto pg = getProcessGroupByID(pg_name); - if (pg) { + if (pg) + { nameOfRecoveryState = pg->recovery_state_; } return nameOfRecoveryState; } -std::optional ConfigurationManager::getMainPGStartupState() const { +std::optional ConfigurationManager::getMainPGStartupState() const +{ std::optional result = std::nullopt; auto pg = getProcessGroupByID(main_pg_startup_state_.pg_name_); - if (pg) { + if (pg) + { auto pg_state = getProcessGroupStateByID(*pg, main_pg_startup_state_.pg_state_name_); - if (pg_state) { + if (pg_state) + { result = &main_pg_startup_state_; - } else { + } + else + { LM_LOG_DEBUG() << "Process group state not found:" << main_pg_startup_state_.pg_state_name_; } - } else { + } + else + { LM_LOG_DEBUG() << "Process group not found:" << main_pg_startup_state_.pg_name_; } return result; } -std::optional*> ConfigurationManager::getListOfProcessGroups() const { +std::optional*> ConfigurationManager::getListOfProcessGroups() const +{ std::optional*> result = std::nullopt; - if (!process_group_names_.empty()) { + if (!process_group_names_.empty()) + { result = &process_group_names_; } @@ -144,14 +167,18 @@ std::optional*> ConfigurationManager::getListO } std::optional*> ConfigurationManager::getProcessIndexesList( - const ProcessGroupStateID& pg_state_id) const { + const ProcessGroupStateID& pg_state_id) const +{ std::optional*> result = std::nullopt; auto state = getProcessGroupStateByID(pg_state_id); - if (state) { + if (state) + { result = &state->process_indexes_; - } else { + } + else + { LM_LOG_DEBUG() << "Process group state '" << pg_state_id.pg_state_name_ << "' not found in group '" << pg_state_id.pg_name_ << "'."; } @@ -160,26 +187,34 @@ std::optional*> ConfigurationManager::getProcessInde } std::optional ConfigurationManager::getOsProcessConfiguration(const IdentifierHash& pg_name, - const uint32_t index) const { + const uint32_t index) const +{ std::optional result = std::nullopt; - if (auto pg = getProcessGroupByNameAndIndex(pg_name, index)) { + if (auto pg = getProcessGroupByNameAndIndex(pg_name, index)) + { result = &(*pg)->processes_[index]; - } else { - LM_LOG_DEBUG() << "Unable to retrieve process configuration for process group" << pg_name - << "with index" << index; + } + else + { + LM_LOG_DEBUG() << "Unable to retrieve process configuration for process group" << pg_name << "with index" + << index; } return result; } std::optional ConfigurationManager::getOsProcessDependencies(const IdentifierHash& pg_name, - const uint32_t index) const { + const uint32_t index) const +{ std::optional result = std::nullopt; - if (auto pg = getProcessGroupByNameAndIndex(pg_name, index)) { + if (auto pg = getProcessGroupByNameAndIndex(pg_name, index)) + { result = &(*pg)->processes_[index].dependencies_; - } else { + } + else + { LM_LOG_DEBUG() << "Unable to retrieve process dependencies for process group" << pg_name << "with index" << index; } @@ -187,35 +222,45 @@ std::optional ConfigurationManager::getOsProcessDependenc return result; } -bool ConfigurationManager::initialize() { +bool ConfigurationManager::initialize() +{ bool result = false; // Assume failure by default process_index_ = 0U; LM_LOG_DEBUG() << "Loading LCM Configurations..."; // Check or set the environment variable - if (checkOrSetFlatConfigEnvVar(score::lcm::internal::kEnvVarName, score::lcm::internal::kEnvVarDefaultValue)) { + if (checkOrSetFlatConfigEnvVar(score::lcm::internal::kEnvVarName, score::lcm::internal::kEnvVarDefaultValue)) + { LM_LOG_DEBUG() << "ECUCFG_ENV_VAR_ROOTFOLDER set successfully"; result = initializeSoftwareClusterConfigurations(); // Assign execution dependencies process based on index AssignOsProcessIndexesInDependencyList(); - } else { + } + else + { LM_LOG_DEBUG() << "Failed to set ECUCFG_ENV_VAR_ROOTFOLDER"; } return result; } -void ConfigurationManager::deinitialize() { - for (auto& process_group : process_groups_) { - for (auto& process : process_group.processes_) { +void ConfigurationManager::deinitialize() +{ + for (auto& process_group : process_groups_) + { + for (auto& process : process_group.processes_) + { for (size_t i = 0U; i < score::lcm::internal::kArgvArraySize && process.startup_config_.argv_[i] != nullptr; - ++i) { - // RULECHECKER_comment(1, 1, check_pointer_qualifier_cast_const, "Remove const for standard library with char type arguments.", true); + ++i) + { + // RULECHECKER_comment(1, 1, check_pointer_qualifier_cast_const, "Remove const for standard library with + // char type arguments.", true); free(const_cast(process.startup_config_.argv_[i])); process.startup_config_.argv_[i] = nullptr; } - for (size_t i = 0U; process.startup_config_.envp_[i] != nullptr; ++i) { + for (size_t i = 0U; process.startup_config_.envp_[i] != nullptr; ++i) + { // RULECHECKER_comment(1,1, check_stdlib_use_alloc, "compiler intrinsic calls", true_no_defect); free(process.startup_config_.envp_[i]); process.startup_config_.envp_[i] = nullptr; @@ -224,47 +269,60 @@ void ConfigurationManager::deinitialize() { } } -bool ConfigurationManager::initializeSoftwareClusterConfigurations() { +bool ConfigurationManager::initializeSoftwareClusterConfigurations() +{ bool result = false; // Load software clusters - if (loadListOfSWClusters()) { + if (loadListOfSWClusters()) + { result = true; - for (uint_least8_t i = 0U; i < sw_clusters_.size(); i++) { - if (loadSWClusterConfiguration(i)) { + for (uint_least8_t i = 0U; i < sw_clusters_.size(); i++) + { + if (loadSWClusterConfiguration(i)) + { LM_LOG_DEBUG() << "Loading SWCL Nr." << static_cast(i) << "Succeeded"; - } else { + } + else + { LM_LOG_ERROR() << "Failed to load SWCL Nr." << static_cast(i); result = false; } } - } else { + } + else + { LM_LOG_DEBUG() << "Failed to load software clusters"; } return result; } -bool ConfigurationManager::loadSWClusterConfiguration(uint8_t sw_cluster_index) { +bool ConfigurationManager::loadSWClusterConfiguration(uint8_t sw_cluster_index) +{ const auto data = read_flatbuffer_file("lm_demo.bin"); - if(!data) { + if (!data) + { return false; } // Extract root node from the loaded configuration auto root_node = LMFlatBuffer::GetLMEcuCfg(data.get()); - if(!root_node) { + if (!root_node) + { LM_LOG_DEBUG() << "Flatbuffer Parser: Empty configuration"; return false; } // Load machine configurations from the root node - if(!loadMachineConfigs(root_node, IdentifierHash(sw_clusters_[sw_cluster_index]))) { + if (!loadMachineConfigs(root_node, IdentifierHash(sw_clusters_[sw_cluster_index]))) + { LM_LOG_DEBUG() << "Flatbuffer Parser: Failed to read Machine Configuration"; return false; } - if(!loadProcessConfigs(root_node)) { + if (!loadProcessConfigs(root_node)) + { LM_LOG_DEBUG() << "Flatbuffer Parser: Failed to read Process Configuration"; return false; } @@ -272,66 +330,85 @@ bool ConfigurationManager::loadSWClusterConfiguration(uint8_t sw_cluster_index) return true; } -bool ConfigurationManager::loadMachineConfigs(const LMFlatBuffer::LMEcuCfg* root_node, const IdentifierHash& cluster) { +bool ConfigurationManager::loadMachineConfigs(const LMFlatBuffer::LMEcuCfg* root_node, const IdentifierHash& cluster) +{ bool result = false; const auto* mode_group = root_node ? root_node->ModeGroup() : nullptr; - if (mode_group != nullptr) { + if (mode_group != nullptr) + { result = true; // Assume success if we reach this point - for (uint32_t i = 0U; i < mode_group->size(); i++) { - if (!parseMachineConfigurations(mode_group->Get(i), cluster)) { + for (uint32_t i = 0U; i < mode_group->size(); i++) + { + if (!parseMachineConfigurations(mode_group->Get(i), cluster)) + { LM_LOG_WARN() << "Failed to parse mode group configurations"; result = false; // Set result to false on failure break; // Exit loop on failure } } - } else { + } + else + { LM_LOG_DEBUG() << "loadMachineConfigs ModeGroup is null"; } return result; } -bool ConfigurationManager::parseMachineConfigurations(const ModeGroup* node, const IdentifierHash& cluster) { +bool ConfigurationManager::parseMachineConfigurations(const ModeGroup* node, const IdentifierHash& cluster) +{ bool result = false; - if (node) { + if (node) + { ProcessGroup process_group_data; process_group_data.name_ = getStringViewFromFlatBuffer(node->identifier()); process_group_data.sw_cluster_ = cluster; - LM_LOG_DEBUG() << "FlatBufferParser::getModeGroupPgName:" << std::string_view{getStringFromFlatBuffer(node->identifier())} + LM_LOG_DEBUG() << "FlatBufferParser::getModeGroupPgName:" + << std::string_view{getStringFromFlatBuffer(node->identifier())} << "( IdentifierHash:" << process_group_data.name_.data() << ")"; - if (process_group_data.name_ != score::lcm::IdentifierHash(std::string_view(""))) { + if (process_group_data.name_ != score::lcm::IdentifierHash(std::string_view(""))) + { // Add process group name to the PG name list process_group_names_.push_back(process_group_data.name_); result = parseModeGroups(node, process_group_data); - } else { + } + else + { LM_LOG_WARN() << "parseMachineConfigurations: Process group name is empty"; } } return result; } -bool ConfigurationManager::parseModeGroups(const ModeGroup* node, ProcessGroup& process_group_data) { +bool ConfigurationManager::parseModeGroups(const ModeGroup* node, ProcessGroup& process_group_data) +{ bool result = false; const auto* mode_declaration_list = node ? node->modeDeclaration() : nullptr; - if (mode_declaration_list && (mode_declaration_list->size())) { + if (mode_declaration_list && (mode_declaration_list->size())) + { process_group_data.off_state_ = IdentifierHash("Off"); // default value if no other path is defined const flatbuffers::String* recovery_state_name = node->recoveryMode_name(); - if (recovery_state_name) { + if (recovery_state_name) + { process_group_data.recovery_state_ = getStringViewFromFlatBuffer(recovery_state_name); - } else { + } + else + { process_group_data.recovery_state_ = IdentifierHash("Recovery"); // default value if nothing defined } - for (const auto* mode_declaration_node : *mode_declaration_list) { + for (const auto* mode_declaration_node : *mode_declaration_list) + { const flatbuffers::String* flatbuffer_string = mode_declaration_node->identifier(); - if (flatbuffer_string) { + if (flatbuffer_string) + { ProcessGroupState pg_state; std::string string_name(flatbuffer_string->c_str(), flatbuffer_string->size()); pg_state.name_ = getStringViewFromFlatBuffer(flatbuffer_string); @@ -341,7 +418,8 @@ bool ConfigurationManager::parseModeGroups(const ModeGroup* node, ProcessGroup& process_group_data.states_.push_back(pg_state); // Is this the "Off" state, i.e. does it end with "/Off" ? auto str_len = string_name.length(); - if ((str_len > 3UL) && (0 == string_name.compare(str_len - 4UL, 4UL, "/Off"))) { + if ((str_len > 3UL) && (0 == string_name.compare(str_len - 4UL, 4UL, "/Off"))) + { process_group_data.off_state_ = pg_state.name_; } } @@ -350,74 +428,98 @@ bool ConfigurationManager::parseModeGroups(const ModeGroup* node, ProcessGroup& // Successfully parsed machine configurations process_groups_.push_back(process_group_data); result = true; - } else { + } + else + { LM_LOG_DEBUG() << "parseMachineConfigurations: Mode declarations are not available or list is null"; } return result; } -bool ConfigurationManager::loadProcessConfigs(const LMFlatBuffer::LMEcuCfg* root_node) { +bool ConfigurationManager::loadProcessConfigs(const LMFlatBuffer::LMEcuCfg* root_node) +{ bool result = false; const auto* process = root_node ? root_node->Process() : nullptr; - if (process != nullptr) { + if (process != nullptr) + { result = true; // Assume success if we reach this point - for (uint32_t i = 0U; i < process->size(); i++) { - if (!parseProcessConfigurations(process->Get(i))) { + for (uint32_t i = 0U; i < process->size(); i++) + { + if (!parseProcessConfigurations(process->Get(i))) + { LM_LOG_DEBUG() << "Failed to parse process configurations"; result = false; // Mark failure if parsing fails break; // Stop processing further if parsing fails } } - } else { + } + else + { LM_LOG_DEBUG() << "Process node is null"; } return result; } -static void setSchedulingParameters(const Process& node, const ProcessStartupConfig& config, OsProcess& instance) { +static void setSchedulingParameters(const Process& node, const ProcessStartupConfig& config, OsProcess& instance) +{ instance.startup_config_.cpu_mask_ = ConfigurationManager::kDefaultProcessorAffinityMask(); instance.startup_config_.scheduling_policy_ = ConfigurationManager::kDefaultSchedulingPolicy; instance.startup_config_.scheduling_priority_ = ConfigurationManager::kDefaultNormalSchedulingPriority; auto attribute = config.schedulingPolicy(); - if (attribute != nullptr) { - if (strcasecmp("SCHED_FIFO", attribute->c_str()) == 0) { + if (attribute != nullptr) + { + if (strcasecmp("SCHED_FIFO", attribute->c_str()) == 0) + { instance.startup_config_.scheduling_policy_ = SCHED_FIFO; instance.startup_config_.scheduling_priority_ = ConfigurationManager::kDefaultRealtimeSchedulingPriority; - } else if (strcasecmp("SCHED_RR", attribute->c_str()) == 0) { + } + else if (strcasecmp("SCHED_RR", attribute->c_str()) == 0) + { instance.startup_config_.scheduling_policy_ = SCHED_RR; instance.startup_config_.scheduling_priority_ = ConfigurationManager::kDefaultRealtimeSchedulingPriority; - } else if (strcasecmp("SCHED_OTHER", attribute->c_str()) == 0) { + } + else if (strcasecmp("SCHED_OTHER", attribute->c_str()) == 0) + { instance.startup_config_.scheduling_policy_ = SCHED_OTHER; - } else { - LM_LOG_WARN() << "scheduling policy" << std::string_view{attribute->c_str(), attribute->size()} << "is not supported, using default"; + } + else + { + LM_LOG_WARN() << "scheduling policy" << std::string_view{attribute->c_str(), attribute->size()} + << "is not supported, using default"; } } attribute = config.schedulingPriority(); - if (attribute != nullptr) { + if (attribute != nullptr) + { instance.startup_config_.scheduling_priority_ = std::stoi(attribute->c_str()); } attribute = node.coremask(); - if (attribute != nullptr) { - instance.startup_config_.cpu_mask_ = static_cast(std::stoul(attribute->c_str()) & 0XFFFFFFFFUL); + if (attribute != nullptr) + { + instance.startup_config_.cpu_mask_ = std::stoull(attribute->c_str()); } } -bool ConfigurationManager::parseProcessConfigurations(const Process* node) { +bool ConfigurationManager::parseProcessConfigurations(const Process* node) +{ bool result = false; const auto* startup_config_list = node ? node->startupConfig() : nullptr; - if (startup_config_list && (startup_config_list->size())) { + if (startup_config_list && (startup_config_list->size())) + { result = true; - for (const auto* startup_config_node : *startup_config_list) { + for (const auto* startup_config_node : *startup_config_list) + { // Populate instance details based on startup_config_node data OsProcess instance; instance.process_number_ = process_index_; // Each instance gets a unique number - if (process_index_ < 0XFFFFFFFFU) { + if (process_index_ < 0XFFFFFFFFU) + { process_index_++; } @@ -438,12 +540,15 @@ bool ConfigurationManager::parseProcessConfigurations(const Process* node) { // and assigning them to this particular startup config (aka OsProcess) auto supplementary_gids = node->sgids(); size_t supplementary_gids_number = supplementary_gids ? supplementary_gids->size() : 0U; - if (supplementary_gids_number > 0U) { + if (supplementary_gids_number > 0U) + { instance.startup_config_.supplementary_gids_.reserve(supplementary_gids_number); } - for (uint32_t i = 0U; i < (supplementary_gids_number & 0XFFFFFFFFU); i++) { + for (uint32_t i = 0U; i < (supplementary_gids_number & 0XFFFFFFFFU); i++) + { const ProcessSgid* sgid_conf = supplementary_gids->Get(i); - if (nullptr != sgid_conf) { + if (nullptr != sgid_conf) + { instance.startup_config_.supplementary_gids_.push_back(sgid_conf->sgid()); } } @@ -460,10 +565,13 @@ bool ConfigurationManager::parseProcessConfigurations(const Process* node) { std::chrono::milliseconds(startup_config_node->exitTimeoutValue()); auto execution_error_string = startup_config_node->executionError(); - if (execution_error_string) { + if (execution_error_string) + { instance.pgm_config_.execution_error_code_ = static_cast(std::stoi(execution_error_string->c_str())); - } else { + } + else + { // default value instance.pgm_config_.execution_error_code_ = kDefaultProcessExecutionError; } @@ -482,7 +590,9 @@ bool ConfigurationManager::parseProcessConfigurations(const Process* node) { // Parse ProcessGroup dependency parseProcessGroup(startup_config_node->processGroupStateDependency(), instance); } - } else { + } + else + { LM_LOG_DEBUG() << "parseProcessConfigurations: Startup configs are not available or list is null"; } @@ -491,7 +601,8 @@ bool ConfigurationManager::parseProcessConfigurations(const Process* node) { void ConfigurationManager::parseProcessArguments( const flatbuffers::Vector>* process_arg_list, - OsProcess& process_instance) { + OsProcess& process_instance) +{ // Initialize the argument index to 0 for setting the executable path size_t arg_index = 0U; @@ -504,26 +615,29 @@ void ConfigurationManager::parseProcessArguments( // Increment the argument index for the next argument ++arg_index; - if (process_arg_list) { + if (process_arg_list) + { // Convert the size of process_arg_list to size_t and log the number of arguments size_t arg_count = static_cast(process_arg_list->size()); // Calculate the maximum number of arguments to process, considering the argv size limit size_t max_args = std::min(arg_count, static_cast(score::lcm::internal::kMaxArg)); - // Check if the number of arguments exceeds the maximum allowed size and log a warning if it does - if (arg_count > static_cast(score::lcm::internal::kMaxArg)) { + if (arg_count > static_cast(score::lcm::internal::kMaxArg)) + { LM_LOG_DEBUG() << "Number of process arguments exceeds maximum allowed size (kMaxArg =" << static_cast(score::lcm::internal::kMaxArg) << "). Only the first" << static_cast(score::lcm::internal::kMaxArg) << "arguments will be processed."; } // Iterate through the process arguments and add them to the argv array - for (size_t i = 0U; i < max_args; ++i) { + for (size_t i = 0U; i < max_args; ++i) + { auto process_arg_node = process_arg_list->Get(static_cast(i)); - if (process_arg_node) { + if (process_arg_node) + { // Convert the flatbuffer argument to a C string and add it to the argv array auto argument = getStringFromFlatBuffer(process_arg_node->argument()); // coverity[autosar_cpp14_a4_7_1_violation:FALSE] Arg count is checked above - no risk of wraparound. @@ -538,10 +652,12 @@ void ConfigurationManager::parseProcessArguments( void ConfigurationManager::parseProcessEnvironmentVars( const flatbuffers::Vector>* env_var_list, - OsProcess& process_instance) { + OsProcess& process_instance) +{ size_t env_index = 0U; - if (env_var_list) { + if (env_var_list) + { // Convert the size of env_var_list to size_t and log the number of environment variables size_t env_count = static_cast(env_var_list->size()); // LM_LOG_DEBUG() << "Number of process environment variables:" << env_count; @@ -551,28 +667,34 @@ void ConfigurationManager::parseProcessEnvironmentVars( // LM_LOG_DEBUG() << "Number of process environment variables to process:" << max_env; // Check if the number of environment variables exceeds the maximum allowed size and log a warning if it does - if (env_count > static_cast(score::lcm::internal::kMaxEnv)) { + if (env_count > static_cast(score::lcm::internal::kMaxEnv)) + { LM_LOG_WARN() << "Number of process environment variables exceeds maximum allowed size (kMaxEnv =" << static_cast(score::lcm::internal::kMaxEnv) << "). Only the first" << static_cast(score::lcm::internal::kMaxEnv) << "variables will be processed."; } // Iterate through the process environment variables and add them to the envp array - for (size_t i = 0U; i < max_env; ++i) { + for (size_t i = 0U; i < max_env; ++i) + { auto env_var_node = env_var_list->Get(static_cast(i)); - if (env_var_node) { + if (env_var_node) + { auto key = getStringFromFlatBuffer(env_var_node->key()); auto value = getStringFromFlatBuffer(env_var_node->value()); // Format environment variable as "key=value" std::string env_str = std::string(key) + "=" + std::string(value); // TODO - // coverity[autosar_cpp14_a4_7_1_violation:FALSE] Environment count is checked above - no risk of wraparound. + // coverity[autosar_cpp14_a4_7_1_violation:FALSE] Environment count is checked above - no risk of + // wraparound. process_instance.startup_config_.envp_[env_index++] = strdup(env_str.c_str()); } } - } else { + } + else + { // Log if the process environment variables list is null // LM_LOG_DEBUG() << "Process environment variables list is not available"; } @@ -583,10 +705,14 @@ void ConfigurationManager::parseProcessEnvironmentVars( void ConfigurationManager::parseProcessGroup( const flatbuffers::Vector>* process_pg_list, - const OsProcess& process_instance) { - if (process_pg_list) { - for (const auto& process_pg_node : *process_pg_list) { - if (process_pg_node) { + const OsProcess& process_instance) +{ + if (process_pg_list) + { + for (const auto& process_pg_node : *process_pg_list) + { + if (process_pg_node) + { // Extract information from ProcessGroupStateDependency of flatconfig binary ProcessGroupStateID pg_info; pg_info.pg_name_ = getStringViewFromFlatBuffer(process_pg_node->stateMachine_name()); @@ -599,71 +725,93 @@ void ConfigurationManager::parseProcessGroup( } } // Successfully processed all process groups - } else { + } + else + { LM_LOG_DEBUG() << "ParseProcessProcessGroup: Process process groups are not available"; } } void ConfigurationManager::parseExecutionDependency( const flatbuffers::Vector>* process_dependency_list, - OsProcess& process_instance) { - if (process_dependency_list) { - for (const auto& process_dependency_node : *process_dependency_list) { - if (process_dependency_node) { + OsProcess& process_instance) +{ + if (process_dependency_list) + { + for (const auto& process_dependency_node : *process_dependency_list) + { + if (process_dependency_node) + { Dependency dep{}; auto state_name = getStringViewFromFlatBuffer(process_dependency_node->stateName()); dep.process_state_ = getProcessState(state_name); - dep.target_process_id_ = getStringViewFromFlatBuffer(process_dependency_node->targetProcess_identifier()); + dep.target_process_id_ = + getStringViewFromFlatBuffer(process_dependency_node->targetProcess_identifier()); LM_LOG_DEBUG() << "ParseProcessExecutionDependency: target process path:" - << std::string_view{getStringFromFlatBuffer(process_dependency_node->targetProcess_identifier())} - << "ID:" << dep.target_process_id_; + << std::string_view{getStringFromFlatBuffer( + process_dependency_node->targetProcess_identifier())} + << "ID:" << dep.target_process_id_; process_instance.dependencies_.push_back(dep); - } } - } else { + } + else + { LM_LOG_DEBUG() << "ParseProcessExecutionDependency: Process execution dependencies are not available"; } } -score::lcm::ProcessState ConfigurationManager::getProcessState(const IdentifierHash& state_name) { +score::lcm::ProcessState ConfigurationManager::getProcessState(const IdentifierHash& state_name) +{ score::lcm::ProcessState result = score::lcm::ProcessState::kIdle; - if (state_name == static_cast(PROCESS_RUNNING_STATE)) { + if (state_name == static_cast(PROCESS_RUNNING_STATE)) + { result = score::lcm::ProcessState::kRunning; - } else if (state_name == static_cast(PROCESS_TERMINATED_STATE)) { + } + else if (state_name == static_cast(PROCESS_TERMINATED_STATE)) + { result = score::lcm::ProcessState::kTerminated; } return result; } -ProcessGroup* ConfigurationManager::getProcessGroupByID(const IdentifierHash& pg_name) const { +ProcessGroup* ConfigurationManager::getProcessGroupByID(const IdentifierHash& pg_name) const +{ const ProcessGroup* result = nullptr; - if (!process_groups_.empty()) { - auto it = find_if(process_groups_.begin(), process_groups_.end(), - [&pg_name](const ProcessGroup& pg) { return pg.name_ == pg_name; }); + if (!process_groups_.empty()) + { + auto it = find_if(process_groups_.begin(), process_groups_.end(), [&pg_name](const ProcessGroup& pg) { + return pg.name_ == pg_name; + }); - if (it != process_groups_.end()) { + if (it != process_groups_.end()) + { result = &(*it); } } - // RULECHECKER_comment(1, 1, check_pointer_qualifier_cast_const, "Remove const for standard library with char type arguments.", true); + // RULECHECKER_comment(1, 1, check_pointer_qualifier_cast_const, "Remove const for standard library with char type + // arguments.", true); return const_cast(result); } -ProcessGroupState* ConfigurationManager::getProcessGroupStateByID(const ProcessGroupStateID& pg_id) const { +ProcessGroupState* ConfigurationManager::getProcessGroupStateByID(const ProcessGroupStateID& pg_id) const +{ ProcessGroupState* result = nullptr; ProcessGroup* pg = getProcessGroupByID(pg_id.pg_name_); - if (pg) { - auto it = find_if(pg->states_.begin(), pg->states_.end(), - [&pg_id](const ProcessGroupState& state) { return state.name_ == pg_id.pg_state_name_; }); + if (pg) + { + auto it = find_if(pg->states_.begin(), pg->states_.end(), [&pg_id](const ProcessGroupState& state) { + return state.name_ == pg_id.pg_state_name_; + }); - if (it != pg->states_.end()) { + if (it != pg->states_.end()) + { result = &(*it); } } @@ -672,13 +820,16 @@ ProcessGroupState* ConfigurationManager::getProcessGroupStateByID(const ProcessG } ProcessGroupState* ConfigurationManager::getProcessGroupStateByID(ProcessGroup& pg, - const IdentifierHash& state_name) const { + const IdentifierHash& state_name) const +{ ProcessGroupState* foundState = nullptr; - auto it = find_if(pg.states_.begin(), pg.states_.end(), - [&state_name](const ProcessGroupState& state) { return state.name_ == state_name; }); + auto it = find_if(pg.states_.begin(), pg.states_.end(), [&state_name](const ProcessGroupState& state) { + return state.name_ == state_name; + }); - if (it != pg.states_.end()) { + if (it != pg.states_.end()) + { foundState = &(*it); } @@ -686,58 +837,75 @@ ProcessGroupState* ConfigurationManager::getProcessGroupStateByID(ProcessGroup& } void ConfigurationManager::AssignOsProcessInstanceToProcessGroup(const ProcessGroupStateID& process_pg, - const OsProcess& process_instance) { + const OsProcess& process_instance) +{ // Find the process group by name auto pg = getProcessGroupByID(process_pg.pg_name_); - if (pg != nullptr) { + if (pg != nullptr) + { // Find the process group state by name within the process group auto state = getProcessGroupStateByID(*pg, process_pg.pg_state_name_); - if (state != nullptr) { + if (state != nullptr) + { uint32_t index_in_pg; // Add the process instance to the process group if it's not already there - auto it = find_if(pg->processes_.begin(), pg->processes_.end(), - [&process_instance](const OsProcess& inst) -> bool { - return process_instance.process_number_ == inst.process_number_; - }); - if (it == pg->processes_.end()) { + auto it = find_if( + pg->processes_.begin(), pg->processes_.end(), [&process_instance](const OsProcess& inst) -> bool { + return process_instance.process_number_ == inst.process_number_; + }); + if (it == pg->processes_.end()) + { // get index and insert new value index_in_pg = static_cast(pg->processes_.size() & 0XFFFFFFFFUL); pg->processes_.push_back(process_instance); - } else { + } + else + { // already there, calculate index of the entry index_in_pg = static_cast((it - pg->processes_.begin()) & 0X7FFFFFFFL); } // Add the process index if it doesn't already exist in the process group state if (find(state->process_indexes_.begin(), state->process_indexes_.end(), index_in_pg) == - state->process_indexes_.end()) { + state->process_indexes_.end()) + { state->process_indexes_.push_back(index_in_pg); } - } else { + } + else + { LM_LOG_WARN() << "Process group state not found:" << process_pg.pg_state_name_; } - } else { + } + else + { LM_LOG_WARN() << "Process group not found:" << process_pg.pg_name_; } } -void ConfigurationManager::AssignOsProcessIndexesInDependencyList() { - for (auto& pg : process_groups_) { - for (size_t process_index = 0U; process_index < pg.processes_.size(); ++process_index) { +void ConfigurationManager::AssignOsProcessIndexesInDependencyList() +{ + for (auto& pg : process_groups_) + { + for (size_t process_index = 0U; process_index < pg.processes_.size(); ++process_index) + { auto& process = pg.processes_[process_index]; UpdateOsProcessIndexInDependencyList(pg, process.dependencies_); } } } -void ConfigurationManager::UpdateOsProcessIndexInDependencyList(ProcessGroup& pg, DependencyList& dep_list) { +void ConfigurationManager::UpdateOsProcessIndexInDependencyList(ProcessGroup& pg, DependencyList& dep_list) +{ // Lambda to process each dependency auto processDependency = [&](Dependency& dependency) { // Lambda to match target process ID and update index auto matchAndUpdateOsProcessIndex = [&]() { - for (size_t process_index = 0U; process_index < pg.processes_.size(); ++process_index) { + for (size_t process_index = 0U; process_index < pg.processes_.size(); ++process_index) + { auto& os_process = pg.processes_[process_index]; - if (dependency.target_process_id_ == os_process.process_id_) { + if (dependency.target_process_id_ == os_process.process_id_) + { dependency.os_process_index_ = static_cast(process_index & 0xFFFFFFFFUL); return; // Exit the loop once the update is done @@ -749,22 +917,30 @@ void ConfigurationManager::UpdateOsProcessIndexInDependencyList(ProcessGroup& pg }; // Process each dependency in the dependency list - for (auto& dependency : dep_list) { + for (auto& dependency : dep_list) + { processDependency(dependency); } } -bool ConfigurationManager::checkOrSetFlatConfigEnvVar(const std::string& name, const std::string& path) { +bool ConfigurationManager::checkOrSetFlatConfigEnvVar(const std::string& name, const std::string& path) +{ bool result = false; const char* value = getenv(name.c_str()); - if (value && strlen(value)) { + if (value && strlen(value)) + { LM_LOG_DEBUG() << name << "already set. Current value:" << std::string_view{value}; result = true; - } else { - if (setenv(name.c_str(), path.c_str(), overwrite_) == 0) { + } + else + { + if (setenv(name.c_str(), path.c_str(), overwrite_) == 0) + { result = true; - } else { + } + else + { LM_LOG_DEBUG() << name.c_str() << "not set, so default flat config binary path loaded"; } } @@ -772,7 +948,8 @@ bool ConfigurationManager::checkOrSetFlatConfigEnvVar(const std::string& name, c return result; } -bool ConfigurationManager::loadListOfSWClusters() { +bool ConfigurationManager::loadListOfSWClusters() +{ bool result = false; // Default value for demonstration @@ -784,19 +961,27 @@ bool ConfigurationManager::loadListOfSWClusters() { } osal::CommsType ConfigurationManager::getfunctionClusterAffiliation(osal::CommsType current_comms, - const char* attribute) { + const char* attribute) +{ osal::CommsType comms_type = current_comms; - if (attribute && std::string_view(attribute) == "STATE_MANAGEMENT") { + if (attribute && std::string_view(attribute) == "STATE_MANAGEMENT") + { comms_type = osal::CommsType::kControlClient; LM_LOG_DEBUG() << "Process is STATE_MANAGEMENT function Cluster Affiliation"; - } else if (attribute && std::string_view(attribute) == "PLATFORM_HEALTH_MANAGEMENT") { - //TODO - example introduce PHM enum. + } + else if (attribute && std::string_view(attribute) == "PLATFORM_HEALTH_MANAGEMENT") + { + // TODO - example introduce PHM enum. LM_LOG_DEBUG() << "Process is PLATFORM_HEALTH_MANAGEMENT function Cluster Affiliation"; - } else if (attribute && std::string_view(attribute) == "LAUNCH_MANAGEMENT") { + } + else if (attribute && std::string_view(attribute) == "LAUNCH_MANAGEMENT") + { comms_type = osal::CommsType::kLaunchManager; LM_LOG_DEBUG() << "Process is LAUNCH_MANAGEMENT function Cluster Affiliation"; - } else { + } + else + { LM_LOG_DEBUG() << "Process is NOT associated with any function Cluster Affiliation"; } @@ -805,10 +990,12 @@ osal::CommsType ConfigurationManager::getfunctionClusterAffiliation(osal::CommsT // TODO - This is workaround solution for comms_type. Since reporting behaviour // and function cluster affiliation both are different config. we need to seperate it. -osal::CommsType ConfigurationManager::getCommsType(const Process* node, const char* short_name) { +osal::CommsType ConfigurationManager::getCommsType(const Process* node, const char* short_name) +{ osal::CommsType comms_type = osal::CommsType::kNoComms; - if (node != nullptr) { + if (node != nullptr) + { // Check reporting behavior comms_type = isReportingProcess(node->executable_reportingBehavior(), short_name); @@ -820,13 +1007,17 @@ osal::CommsType ConfigurationManager::getCommsType(const Process* node, const ch } osal::CommsType ConfigurationManager::isReportingProcess(const ExecutionStateReportingBehaviorEnum reporting_behaviour, - const std::string_view process_name) { + const std::string_view process_name) +{ osal::CommsType reporting_status = osal::CommsType::kNoComms; - if (reporting_behaviour == ExecutionStateReportingBehaviorEnum::ReportsExecutionState) { + if (reporting_behaviour == ExecutionStateReportingBehaviorEnum::ReportsExecutionState) + { reporting_status = osal::CommsType::kReporting; LM_LOG_DEBUG() << "Process" << process_name << "is Reporting execution state"; - } else { + } + else + { // ExecutionStateReportingBehaviorEnum::DoesNotReportExecutionState LM_LOG_DEBUG() << "Process" << process_name << "is NOT Reporting execution state"; } @@ -835,13 +1026,17 @@ osal::CommsType ConfigurationManager::isReportingProcess(const ExecutionStateRep } bool ConfigurationManager::isSelfTerminatingProcess(const TerminationBehaviorEnum termination_behavior, - const std::string_view process_name) { + const std::string_view process_name) +{ bool termination_status = false; - if (termination_behavior == TerminationBehaviorEnum::ProcessIsSelfTerminating) { + if (termination_behavior == TerminationBehaviorEnum::ProcessIsSelfTerminating) + { termination_status = true; LM_LOG_DEBUG() << "Process" << process_name << "is Self terminating"; - } else { + } + else + { // TerminationBehaviorEnum::ProcessIsNotSelfTerminating LM_LOG_DEBUG() << "Process" << process_name << "is NOT Self terminating"; } @@ -850,34 +1045,43 @@ bool ConfigurationManager::isSelfTerminatingProcess(const TerminationBehaviorEnu } std::optional ConfigurationManager::getProcessGroupByNameAndIndex(const IdentifierHash& pg_name, - const uint32_t index) const { + const uint32_t index) const +{ std::optional result = std::nullopt; auto pg = getProcessGroupByID(pg_name); - if (pg) { - if (index < pg->processes_.size()) { + if (pg) + { + if (index < pg->processes_.size()) + { result = pg; - } else { + } + else + { LM_LOG_DEBUG() << "Process index" << index << "is out of bounds in process group" << pg_name; } - } else { + } + else + { LM_LOG_DEBUG() << "Process group not found:" << pg_name; } return result; } -IdentifierHash ConfigurationManager::getStringViewFromFlatBuffer(const flatbuffers::String* flat_string) { +IdentifierHash ConfigurationManager::getStringViewFromFlatBuffer(const flatbuffers::String* flat_string) +{ return flat_string ? IdentifierHash{flat_string->c_str()} : IdentifierHash{}; } -const char* ConfigurationManager::getStringFromFlatBuffer(const flatbuffers::String* flat_string) { +const char* ConfigurationManager::getStringFromFlatBuffer(const flatbuffers::String* flat_string) +{ return flat_string ? flat_string->c_str() : ""; } -} // namespace lcm - } // namespace internal +} // namespace lcm + } // namespace score diff --git a/score/launch_manager/src/daemon/src/configuration/configuration_manager.hpp b/score/launch_manager/src/daemon/src/configuration/configuration_manager.hpp index 57ee09627..2320b6ca0 100644 --- a/score/launch_manager/src/daemon/src/configuration/configuration_manager.hpp +++ b/score/launch_manager/src/daemon/src/configuration/configuration_manager.hpp @@ -11,59 +11,70 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ - #ifndef CONFIGURATIONMANAGER_HPP_INCLUDED #define CONFIGURATIONMANAGER_HPP_INCLUDED +#include "score/mw/launch_manager/common/constants.hpp" +#include "score/mw/launch_manager/common/identifier_hash.hpp" +#include "score/mw/launch_manager/common/process_group_state_id.hpp" +#include "score/mw/launch_manager/process_group_manager/iprocess.hpp" +#include "score/mw/launch_manager/process_state_client/posix_process.hpp" #include #include #include #include #include #include -#include "score/mw/launch_manager/common/identifier_hash.hpp" -#include "score/mw/launch_manager/common/constants.hpp" -#include "score/mw/launch_manager/common/process_group_state_id.hpp" -#include "score/mw/launch_manager/process_group_manager/iprocess.hpp" -#include "score/mw/launch_manager/process_state_client/posix_process.hpp" #include "score/mw/launch_manager/configuration/lm_flatcfg_generated.h" -namespace score { +namespace score +{ -namespace lcm { +namespace lcm +{ -namespace internal { +namespace internal +{ -using IdentifierHash = score::lcm:: - IdentifierHash; ///< Defines a type alias 'IdentifierHash' for the type 'score::lcm::IdentifierHash'. Type that represents an identity or identifier. Usually this is a path to a short name. +using IdentifierHash = score::lcm::IdentifierHash; ///< Defines a type alias 'IdentifierHash' for the type + ///< 'score::lcm::IdentifierHash'. Type that represents an identity + ///< or identifier. Usually this is a path to a short name. /// @brief Represents the configuration settings for a process group manager. -// RULECHECKER_comment(1, 1, check_incomplete_data_member_construction, "wi 45913 - This struct is POD, which doesn't have user-declared constructor. The rule doesn’t apply.", false) -struct PgManagerConfig final { +// RULECHECKER_comment(1, 1, check_incomplete_data_member_construction, "wi 45913 - This struct is POD, which doesn't +// have user-declared constructor. The rule doesn’t apply.", false) +struct PgManagerConfig final +{ bool is_self_terminating_; ///< true if the adaptive application may terminate without being first asked std::chrono::milliseconds startup_timeout_ms_; ///< Number of milliseconds to wait for kRunning before flagging an error - std::chrono::milliseconds - termination_timeout_ms_; ///< Number of milliseconds to wait for process to terminate after requesting termination + std::chrono::milliseconds termination_timeout_ms_; ///< Number of milliseconds to wait for process to terminate + ///< after requesting termination uint32_t number_of_restart_attempts; ///< Number of times to attempt restart if the initial attempt fails uint32_t execution_error_code_; ///< Code to report if this process fails }; /// @brief Represents process dependency in a particular process group associated process. -// RULECHECKER_comment(1, 1, check_incomplete_data_member_construction, "wi 45913 - This struct is POD, which doesn't have user-declared constructor. The rule doesn’t apply.", false) -struct Dependency final { - score::lcm::ProcessState process_state_; ///< The state of the other process upon which starting of this process depends. +// RULECHECKER_comment(1, 1, check_incomplete_data_member_construction, "wi 45913 - This struct is POD, which doesn't +// have user-declared constructor. The rule doesn’t apply.", false) +struct Dependency final +{ + score::lcm::ProcessState + process_state_; ///< The state of the other process upon which starting of this process depends. IdentifierHash target_process_id_; ///< The ID of the target process this dependency is associated with. - uint32_t os_process_index_; ///< The index of the OS process in the target process list. + uint32_t os_process_index_; ///< The index of the OS process in the target process list. }; using DependencyList = std::vector; -/// @brief Represent configuration needed to start operating system process, plus identifier of a process for which this startup configuration was defined / configured. -// RULECHECKER_comment(1, 1, check_incomplete_data_member_construction, "wi 45913 - This struct is POD, which doesn't have user-declared constructor. The rule doesn’t apply.", false) -struct OsProcess final { - IdentifierHash process_id_; ///< id of a Process. +/// @brief Represent configuration needed to start operating system process, plus identifier of a process for which this +/// startup configuration was defined / configured. +// RULECHECKER_comment(1, 1, check_incomplete_data_member_construction, "wi 45913 - This struct is POD, which doesn't +// have user-declared constructor. The rule doesn’t apply.", false) +struct OsProcess final +{ + IdentifierHash process_id_; ///< id of a Process. uint32_t process_number_; ///< unique number for this process & startup configuration combination osal::OsalConfig startup_config_{}; ///< Startup configuration. PgManagerConfig pgm_config_; ///< Process group manager operations configuration. @@ -71,42 +82,50 @@ struct OsProcess final { }; /// @brief Represents configuration of a process group state. -// RULECHECKER_comment(1, 1, check_incomplete_data_member_construction, "wi 45913 - This struct is POD, which doesn't have user-declared constructor. The rule doesn’t apply.", false) -struct ProcessGroupState final { +// RULECHECKER_comment(1, 1, check_incomplete_data_member_construction, "wi 45913 - This struct is POD, which doesn't +// have user-declared constructor. The rule doesn’t apply.", false) +struct ProcessGroupState final +{ IdentifierHash name_; ///< Name of a process group state. std::vector - process_indexes_; ///< Processes that should be started / run in this process group state. It is an array of indexes (aka pointers) to the processes managed by a process group. + process_indexes_; ///< Processes that should be started / run in this process group state. It is an array of + ///< indexes (aka pointers) to the processes managed by a process group. }; /// @brief Represents a process group configuration. -// RULECHECKER_comment(1, 1, check_incomplete_data_member_construction, "wi 45913 - This struct is POD, which doesn't have user-declared constructor. The rule doesn’t apply.", false) -struct ProcessGroup final { - IdentifierHash name_; ///< Name of a process group. - IdentifierHash sw_cluster_; ///< Software cluster to which this process group belongs - IdentifierHash off_state_; ///< ID of the "Off" state for this process group - IdentifierHash recovery_state_; ///< ID of the recovery state for this process group +// RULECHECKER_comment(1, 1, check_incomplete_data_member_construction, "wi 45913 - This struct is POD, which doesn't +// have user-declared constructor. The rule doesn’t apply.", false) +struct ProcessGroup final +{ + IdentifierHash name_; ///< Name of a process group. + IdentifierHash sw_cluster_; ///< Software cluster to which this process group belongs + IdentifierHash off_state_; ///< ID of the "Off" state for this process group + IdentifierHash recovery_state_; ///< ID of the recovery state for this process group std::vector states_; ///< States configured for this process group. - std::vector - processes_; ///< Processes that are managed (started / stopped) by this process group. + std::vector processes_; ///< Processes that are managed (started / stopped) by this process group. }; /// /// @brief Manages the configuration of the machine. /// /// ConfigurationManager is responsible for the entire lifecycle of configuration. -/// It loads configuration from persistent storage, verify that configuration was not tampered with and then makes that data available (read only) to the rest of Launch Manager. -/// It is also responsible for rereading (updating) configuration during software update. -// RULECHECKER_comment(1, 1, check_incomplete_data_member_construction, "wi 45913 - This struct is POD, which doesn't have user-declared constructor. The rule doesn’t apply.", false) -class ConfigurationManager final { +/// It loads configuration from persistent storage, verify that configuration was not tampered with and then makes that +/// data available (read only) to the rest of Launch Manager. It is also responsible for rereading (updating) +/// configuration during software update. +// RULECHECKER_comment(1, 1, check_incomplete_data_member_construction, "wi 45913 - This struct is POD, which doesn't +// have user-declared constructor. The rule doesn’t apply.", false) +class ConfigurationManager final +{ // using namespace ::score::internal::ucm::ipc; - public: + public: /// @brief Initializes the configuration manager. /// /// This function is responsible for loading the configurations. /// It performs the following main tasks: /// - Checks or sets the FlatBuffer configuration environment variable. /// - Obtain the list of software clusters. If this is successful, load the first software cluster from the list. - /// - Gets every element in the configuration. keeps a local reference to every configuration element for easy access. + /// - Gets every element in the configuration. keeps a local reference to every configuration element for easy + /// access. /// TODO: Add more details about the memory allocation. /// /// @return Returns true if the configurations were loaded successfully, false otherwise. @@ -159,7 +178,8 @@ class ConfigurationManager final { std::optional getMainPGStartupState() const; /// @brief Get a list of processes configured to run in this process group state. - /// @param[in] process_group_state_id The ID of the process group state for which to retrieve the list of process IDs. + /// @param[in] process_group_state_id The ID of the process group state for which to retrieve the list of process + /// IDs. /// @return Returns a pointer to a vector of process indexes. /// Process group state 'Off' will have vector of size 0. /// If Process group does not exist then there will be no return value. @@ -172,7 +192,7 @@ class ConfigurationManager final { /// @return Returns a pointer to an OSProcess. /// If index/process group does not exist there will be no return value. std::optional getOsProcessConfiguration(const IdentifierHash& pg_name_, - const uint32_t index) const; + const uint32_t index) const; /// @brief Get dependencies of an OS process within a specific process group. /// @@ -185,13 +205,13 @@ class ConfigurationManager final { /// @return An optional vector of Dependency objects representing process dependencies, /// or an empty optional if the process group or process index does not exist. std::optional getOsProcessDependencies(const IdentifierHash& process_group_name, - const uint32_t index) const; + const uint32_t index) const; /// @brief default value for the process execution error, in case it is not defined in the configuration static const uint32_t kDefaultProcessExecutionError; /// @brief default value for processor affinity mask in case it is not defined in the configuration - static uint32_t kDefaultProcessorAffinityMask(); + static uint64_t kDefaultProcessorAffinityMask(); /// @brief default value for scheduling policy in case it is not defined in the configuration static const int32_t kDefaultSchedulingPolicy; @@ -204,7 +224,7 @@ class ConfigurationManager final { /// if the process is running in normal scheduling mode. static const int32_t kDefaultNormalSchedulingPriority; - private: + private: /// @brief Initializes the LCM configurations for the software clusters. /// This function loads the list of software clusters and then loads the LCM configurations /// for the specified software cluster index. It sets the success flag to true if all steps @@ -280,23 +300,25 @@ class ConfigurationManager final { const flatbuffers::Vector>* env_var_list, OsProcess& process_instance); - /// @brief Parse process group dependencies of processes from a list of FlatBuffer process process group state dependency nodes. - /// This function parses process group dependencies from the provided list of FlatBuffer process process group state dependency nodes - /// and associates the specified OsProcess instance with the relevant process groups. + /// @brief Parse process group dependencies of processes from a list of FlatBuffer process process group state + /// dependency nodes. This function parses process group dependencies from the provided list of FlatBuffer process + /// process group state dependency nodes and associates the specified OsProcess instance with the relevant process + /// groups. /// @param[in] process_pg_list Pointer to a vector of FlatBuffer process process group state dependency nodes. /// @param[out] process_instance Reference to the OsProcess instance where parsed environment will be stored. /// @return `true` if process group dependencies were successfully parsed and associated, `false` otherwise. void parseProcessGroup( - const flatbuffers::Vector>* - process_pg_list, + const flatbuffers::Vector>* process_pg_list, const OsProcess& process_instance); /// @brief Parses execution dependencies from a FlatBuffer list and updates the provided OsProcess instance. /// This function processes a list of `ProcessExecutionDependency` objects from the FlatBuffer and updates /// the specified `OsProcess` instance to reflect these dependencies. Dependencies are used to manage /// relationships between different processes. - /// @param[in] process_dependency_list Pointer to a vector of `ProcessExecutionDependency` objects from the FlatBuffer. - /// @param[out] process_instance Reference to the `OsProcess` instance that will be updated with the parsed dependencies. + /// @param[in] process_dependency_list Pointer to a vector of `ProcessExecutionDependency` objects from the + /// FlatBuffer. + /// @param[out] process_instance Reference to the `OsProcess` instance that will be updated with the parsed + /// dependencies. void parseExecutionDependency( const flatbuffers::Vector>* process_dependency_list, @@ -317,7 +339,8 @@ class ConfigurationManager final { ProcessGroup* getProcessGroupByID(const IdentifierHash& pg_name) const; /// @brief Get a pointer to a ProcessGroupState within a specified ProcessGroup by its ID. - /// This function retrieves a pointer to a ProcessGroupState identified by the state name ID within the given ProcessGroup pointer. + /// This function retrieves a pointer to a ProcessGroupState identified by the state name ID within the given + /// ProcessGroup pointer. /// @param[in] pg The ProcessGroup in which to search for the ProcessGroupState. /// @param[in] state_name The ID of the ProcessGroupState to retrieve. /// @return A pointer to the ProcessGroupState if found within the ProcessGroup, or nullptr if not found. @@ -339,7 +362,7 @@ class ConfigurationManager final { /// @return A pointer to the `ProcessGroup` if it exists, or `nullptr` if no matching process group is found /// with the given name and index. std::optional getProcessGroupByNameAndIndex(const IdentifierHash& pg_name, - const uint32_t index) const; + const uint32_t index) const; /// @brief Assign an OsProcess instance to a ProcessGroupState identified by ID. /// This function assigns the OsProcess instance to a ProcessGroup and process index to ProcessGroupState. @@ -357,13 +380,15 @@ class ConfigurationManager final { bool checkOrSetFlatConfigEnvVar(const std::string& name, const std::string& value); /// @brief Get the value of an environment variable by name. - /// This function is wrapper for getenv() system call and it retrieves the value of the environment variable identified by the specified name. + /// This function is wrapper for getenv() system call and it retrieves the value of the environment variable + /// identified by the specified name. /// @param[in] name The name of the environment variable to check or set /// @return The value of the environment variable as a String, or an empty string if not found. std::string getEnvVar(const std::string& name) const; /// @brief Set or update an environment variable with the specified name and value. - /// This function is wrapper for setenv() system call and it sets or updates an environment variable with the provided name and value. + /// This function is wrapper for setenv() system call and it sets or updates an environment variable with the + /// provided name and value. /// @param[in] name The name of the environment variable to set. /// @param[in] value The value to assign to the environment variable. /// @param[in] overwrite Flag indicating whether to overwrite an existing variable. @@ -386,7 +411,8 @@ class ConfigurationManager final { /// @brief Determines if the process reports its execution state. /// This function checks the reporting behavior of a process and logs an appropriate message. /// It returns `true` if the process reports its execution state and `false` otherwise. - /// @param[in] reporting_behaviour The reporting behavior of the process, represented as an enumerator of type LMFlatBuffer::ExecutionStateReportingBehaviorEnum. + /// @param[in] reporting_behaviour The reporting behavior of the process, represented as an enumerator of type + /// LMFlatBuffer::ExecutionStateReportingBehaviorEnum. /// @param[in] process_name The name of the process as a FlatBuffer string. /// @return `kReporting` if the process reports its execution state, `kNoComms` otherwise. osal::CommsType isReportingProcess(const LMFlatBuffer::ExecutionStateReportingBehaviorEnum reporting_behaviour, @@ -403,9 +429,10 @@ class ConfigurationManager final { osal::CommsType getCommsType(const LMFlatBuffer::Process* node, const char* short_name); /// @brief Determine the function cluster affiliation and update the communication type accordingly. - /// This function examines the given function cluster attribute and adjusts the current communication type if the process - /// belongs to a specific function cluster, such as "STATE_MANAGEMENT" or "PLATFORM_HEALTH_MANAGEMENT". - /// @param[in] current_comms The current communication type, which will be updated based on the function cluster affiliation. + /// This function examines the given function cluster attribute and adjusts the current communication type if the + /// process belongs to a specific function cluster, such as "STATE_MANAGEMENT" or "PLATFORM_HEALTH_MANAGEMENT". + /// @param[in] current_comms The current communication type, which will be updated based on the function cluster + /// affiliation. /// @param[in] attribute A C-style string representing the function cluster affiliation of the process. /// @return The updated communication type based on the function cluster affiliation. osal::CommsType getfunctionClusterAffiliation(osal::CommsType current_comms, const char* attribute); @@ -413,7 +440,8 @@ class ConfigurationManager final { /// @brief Determines if the process is self-terminating. /// This function checks the termination behavior of a process and logs an appropriate message. /// It returns `true` if the process is self-terminating and `false` otherwise. - /// @param[in] termination_behavior The termination behavior of the process, represented as an enumerator of type LMFlatBuffer::TerminationBehaviorEnum. + /// @param[in] termination_behavior The termination behavior of the process, represented as an enumerator of type + /// LMFlatBuffer::TerminationBehaviorEnum. /// @param[in] process_name The name of the process as a FlatBuffer string. /// @return `true` if the process is self-terminating, `false` otherwise. bool isSelfTerminatingProcess(const LMFlatBuffer::TerminationBehaviorEnum termination_behavior, @@ -451,10 +479,11 @@ class ConfigurationManager final { std::vector process_group_names_{}; /// @brief Default startup state identifier for a machine process group. - /// This member variable represents a predefined `ProcessGroupStateID` instance that identifies the startup state of a machine process group. - /// It is initialized with the names "MainPG" (process group name) and "Startup" (state name). + /// This member variable represents a predefined `ProcessGroupStateID` instance that identifies the startup state of + /// a machine process group. It is initialized with the names "MainPG" (process group name) and "Startup" (state + /// name). ProcessGroupStateID main_pg_startup_state_{static_cast("MainPG"), - static_cast("MainPG/Startup")}; + static_cast("MainPG/Startup")}; /// @brief Unique Process index for OS process instance for specific startup configs /// This member variable represents the index of the OS process instance with unique startup config. @@ -476,10 +505,10 @@ class ConfigurationManager final { static const char* PROCESS_TERMINATED_STATE; }; -} // namespace lcm - } // namespace internal +} // namespace lcm + } // namespace score #endif /// CONFIGURATIONMANAGER_HPP_INCLUDED diff --git a/score/launch_manager/src/daemon/src/osal/details/linux/set_affinity.cpp b/score/launch_manager/src/daemon/src/osal/details/linux/set_affinity.cpp index 9b7fee75d..2d639eb63 100644 --- a/score/launch_manager/src/daemon/src/osal/details/linux/set_affinity.cpp +++ b/score/launch_manager/src/daemon/src/osal/details/linux/set_affinity.cpp @@ -16,27 +16,35 @@ #include "score/mw/launch_manager/osal/set_affinity.hpp" #include -namespace score { +namespace score +{ -namespace lcm { +namespace lcm +{ -namespace internal { +namespace internal +{ -namespace osal { +namespace osal +{ -std::int32_t setaffinity(std::uint32_t cpumask) noexcept(true) { +std::int32_t setaffinity(std::uint64_t cpumask) noexcept(true) +{ cpu_set_t mask; CPU_ZERO(&mask); - for (uint32_t i = 0U; i < sizeof(cpumask) * 8U; ++i) { - if (cpumask & (1U << i)) { - // RULECHECKER_comment(1, 2, check_underlying_signedness_conversion, "This is the definition provided by the OS and does a signedness conversion.", true) - // RULECHECKER_comment(1, 1, check_c_style_cast, "This is the definition provided by the OS and does a C-style cast.", true) + for (uint64_t i = 0U; i < sizeof(cpumask) * 8U; ++i) + { + if (cpumask & (1ULL << i)) + { + // RULECHECKER_comment(1, 2, check_underlying_signedness_conversion, "This is the definition provided by the + // OS and does a signedness conversion.", true) RULECHECKER_comment(1, 1, check_c_style_cast, "This is the + // definition provided by the OS and does a C-style cast.", true) CPU_SET(i, &mask); } } return 0 == sched_setaffinity(0, sizeof(mask), &mask) ? 0 : -1; } } // namespace osal -} // namespace lcm } // namespace internal +} // namespace lcm } // namespace score diff --git a/score/launch_manager/src/daemon/src/osal/details/qnx/set_affinity.cpp b/score/launch_manager/src/daemon/src/osal/details/qnx/set_affinity.cpp index 22751c63f..0f94cb917 100644 --- a/score/launch_manager/src/daemon/src/osal/details/qnx/set_affinity.cpp +++ b/score/launch_manager/src/daemon/src/osal/details/qnx/set_affinity.cpp @@ -13,18 +13,23 @@ #include #include "score/mw/launch_manager/osal/set_affinity.hpp" -namespace score { +namespace score +{ -namespace lcm { +namespace lcm +{ -namespace internal { +namespace internal +{ -namespace osal { +namespace osal +{ -int32_t setaffinity(uint32_t cpumask) noexcept(true) { - return 0 == ThreadCtl(_NTO_TCTL_RUNMASK_GET_AND_SET, &cpumask) ? 0 : -1; +int32_t setaffinity(uint64_t cpumask) noexcept(true) +{ + return 0 == ThreadCtl(_NTO_TCTL_RUNMASK64_GET_AND_SET, &cpumask) ? 0 : -1; } } // namespace osal -} // namespace lcm } // namespace internal +} // namespace lcm } // namespace score diff --git a/score/launch_manager/src/daemon/src/osal/num_cores.hpp b/score/launch_manager/src/daemon/src/osal/num_cores.hpp index 7a06461b5..79aefe329 100644 --- a/score/launch_manager/src/daemon/src/osal/num_cores.hpp +++ b/score/launch_manager/src/daemon/src/osal/num_cores.hpp @@ -11,26 +11,29 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ - #ifndef OSAL_NUM_CORES_HPP_INCLUDED #define OSAL_NUM_CORES_HPP_INCLUDED #include -namespace score { -namespace lcm { -namespace internal { -namespace osal { +namespace score +{ +namespace lcm +{ +namespace internal +{ +namespace osal +{ // coverity[autosar_cpp14_m3_4_1_violation:INTENTIONAL] The value is used in a global context. -constexpr uint32_t kDefaultNumCores = 32U; // Default value if unable to determine number of cores +constexpr uint32_t kDefaultNumCores = 64U; // Default value if unable to determine number of cores /// @brief Get the number of CPU cores available on the system. /// @return Returns the number of CPU cores available. uint32_t getNumCores(); } // namespace osal -} // namespace lcm } // namespace internal +} // namespace lcm } // namespace score #endif diff --git a/score/launch_manager/src/daemon/src/osal/set_affinity.hpp b/score/launch_manager/src/daemon/src/osal/set_affinity.hpp index 1a396901e..5995ba84d 100644 --- a/score/launch_manager/src/daemon/src/osal/set_affinity.hpp +++ b/score/launch_manager/src/daemon/src/osal/set_affinity.hpp @@ -11,34 +11,36 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ - #ifndef SETAFFINITY_HPP_INCLUDED #define SETAFFINITY_HPP_INCLUDED #include -namespace score { +namespace score +{ -namespace lcm { +namespace lcm +{ -namespace internal { +namespace internal +{ -namespace osal { +namespace osal +{ /// @brief Set the processor affinity for the current thread /// @param cpumask a mask defining which of the available -/// processors to use. N.B. This function is restricted to systems -/// with no more than 64 cores available. Note that if more cores are -/// selected than exist, no error may be returned as long as at least -/// one core that does exist is selected. +/// processors to use. Supports systems with up to 64 cores. +/// Note that if more cores are selected than exist, no error may be +/// returned as long as at least one core that does exist is selected. /// @returns 0 on success, -1 on error with errno set appropriately: /// EINVAL The affinity bit mask mask contains no processors that are /// currently physically on the system and permitted to the /// thread according to any restrictions that may be imposed /// elsewhere. -[[nodiscard]] int32_t setaffinity(uint32_t cpumask) noexcept(true); +[[nodiscard]] int32_t setaffinity(uint64_t cpumask) noexcept(true); } // namespace osal -} // namespace lcm } // namespace internal +} // namespace lcm } // namespace score #endif diff --git a/score/launch_manager/src/daemon/src/process_group_manager/iprocess.hpp b/score/launch_manager/src/daemon/src/process_group_manager/iprocess.hpp index 50ce8d2ab..e50738951 100644 --- a/score/launch_manager/src/daemon/src/process_group_manager/iprocess.hpp +++ b/score/launch_manager/src/daemon/src/process_group_manager/iprocess.hpp @@ -11,33 +11,37 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ - #ifndef PROCESS_HPP_INCLUDED #define PROCESS_HPP_INCLUDED #include #include -#include #include "score/mw/launch_manager/common/constants.hpp" #include "score/mw/launch_manager/osal/ipc_comms.hpp" +#include #include #include #include #include -namespace score { +namespace score +{ -namespace lcm { +namespace lcm +{ -namespace internal { +namespace internal +{ -namespace osal { +namespace osal +{ /// @brief Represents process limits to be applied by setrlimit() -struct OsalLimits { +struct OsalLimits +{ rlim_t data_; ///< Maximum memory usage in bytes (heapUsage) rlim_t as_; ///< Maximum address space usage in bytes (systemMemoryUsage) // Note usage of the following may change when resource groups are implemented @@ -46,28 +50,32 @@ struct OsalLimits { }; /// @brief Represents process startup and other configurations consumed by OSAL. -// RULECHECKER_comment(1, 1, check_incomplete_data_member_construction, "wi 45913 - This struct is POD, which doesn't have user-declared constructor. The rule doesn’t apply.", false) -struct OsalConfig { - std::string executable_path_{}; ///< Path to the executable. - std::string short_name_; ///< Short name of the process - std::array argv_{}; ///< Command-line arguments. - char* envp_[static_cast(score::lcm::internal::kEnvArraySize)]; ///< Environment variables. +// RULECHECKER_comment(1, 1, check_incomplete_data_member_construction, "wi 45913 - This struct is POD, which doesn't +// have user-declared constructor. The rule doesn’t apply.", false) +struct OsalConfig +{ + std::string executable_path_{}; ///< Path to the executable. + std::string short_name_; ///< Short name of the process + std::array argv_{}; ///< Command-line arguments. + char* envp_[static_cast(score::lcm::internal::kEnvArraySize)]; ///< Environment variables. std::string security_policy_{}; ///< Security policy to apply to this process - uid_t uid_; ///< User ID. - gid_t gid_; ///< Group ID. + uid_t uid_; ///< User ID. + gid_t gid_; ///< Group ID. std::vector supplementary_gids_; ///< Supplementary group IDs. - CommsType comms_type_; ///< The type of communications required by this process - uint32_t cpu_mask_; ///< Mask for setting processor core affinity - int32_t scheduling_policy_; ///< Scheduling policy defined for this process - int32_t scheduling_priority_; ///< Scheduling priority for this process - OsalLimits resource_limits_; ///< Resource limits for this process + CommsType comms_type_; ///< The type of communications required by this process + uint64_t cpu_mask_; ///< Mask for setting processor core affinity + int32_t scheduling_policy_; ///< Scheduling policy defined for this process + int32_t scheduling_priority_; ///< Scheduling priority for this process + OsalLimits resource_limits_; ///< Resource limits for this process }; /// @brief Struct to hold configuration parameters for the child process. -// RULECHECKER_comment(1, 1, check_incomplete_data_member_construction, "wi 45913 - This struct is POD, which doesn't have user-declared constructor. The rule doesn’t apply.", false) -struct ChildProcessConfig { +// RULECHECKER_comment(1, 1, check_incomplete_data_member_construction, "wi 45913 - This struct is POD, which doesn't +// have user-declared constructor. The rule doesn’t apply.", false) +struct ChildProcessConfig +{ const OsalConfig* config; ///< child process startup configurations - int fd; /// Date: Mon, 6 Jul 2026 18:31:13 +0300 Subject: [PATCH 2/3] Use QNX 8 cluster-aware API for 64-core CPU affinity support _NTO_TCTL_RUNMASK_GET_AND_SET returns EINVAL on systems with more than 32 CPUs. Use _NTO_TCTL_RUNMASK_GET_AND_SET_INHERIT instead, which accepts a struct _thread_runmask with RMSK_SIZE/RMSK_SET macros to support up to 64 CPUs across multiple CPU clusters. Issue: #122 --- .../src/osal/details/qnx/set_affinity.cpp | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/score/launch_manager/src/daemon/src/osal/details/qnx/set_affinity.cpp b/score/launch_manager/src/daemon/src/osal/details/qnx/set_affinity.cpp index 0f94cb917..3729b5c2b 100644 --- a/score/launch_manager/src/daemon/src/osal/details/qnx/set_affinity.cpp +++ b/score/launch_manager/src/daemon/src/osal/details/qnx/set_affinity.cpp @@ -27,7 +27,26 @@ namespace osal int32_t setaffinity(uint64_t cpumask) noexcept(true) { - return 0 == ThreadCtl(_NTO_TCTL_RUNMASK64_GET_AND_SET, &cpumask) ? 0 : -1; + // _NTO_TCTL_RUNMASK_GET_AND_SET returns EINVAL on systems with more than 32 CPUs. + // Use _NTO_TCTL_RUNMASK_GET_AND_SET_INHERIT with struct _thread_runmask, which + // supports up to 64 CPUs across multiple clusters via the RMSK_SET macro. + constexpr int kSize = RMSK_SIZE(64); + struct + { + int size; + unsigned runmask[kSize]; + unsigned inherit_mask[kSize]; + } tm{kSize, {}, {}}; + + for (uint64_t i = 0U; i < 64U; ++i) + { + if (cpumask & (1ULL << i)) + { + RMSK_SET(static_cast(i), tm.runmask); + } + } + + return 0 == ThreadCtl(_NTO_TCTL_RUNMASK_GET_AND_SET_INHERIT, &tm) ? 0 : -1; } } // namespace osal } // namespace internal From 3600d79de5ff78514ee12d116839ee7af1ef5e44 Mon Sep 17 00:00:00 2001 From: shegazyy Date: Mon, 13 Jul 2026 17:17:21 +0300 Subject: [PATCH 3/3] Set inherit_mask so child threads get CPU affinity Without setting inherit_mask, only the main thread of the launched process gets the CPU affinity from _NTO_TCTL_RUNMASK_GET_AND_SET_INHERIT. Any threads spawned by the child will run unconstrained. Setting inherit_mask mirrors the Linux sched_setaffinity behavior where the affinity applies to all threads. Co-Authored-By: Claude Sonnet 4.6 --- .../src/daemon/src/osal/details/qnx/set_affinity.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/score/launch_manager/src/daemon/src/osal/details/qnx/set_affinity.cpp b/score/launch_manager/src/daemon/src/osal/details/qnx/set_affinity.cpp index 3729b5c2b..969c660dd 100644 --- a/score/launch_manager/src/daemon/src/osal/details/qnx/set_affinity.cpp +++ b/score/launch_manager/src/daemon/src/osal/details/qnx/set_affinity.cpp @@ -43,6 +43,7 @@ int32_t setaffinity(uint64_t cpumask) noexcept(true) if (cpumask & (1ULL << i)) { RMSK_SET(static_cast(i), tm.runmask); + RMSK_SET(static_cast(i), tm.inherit_mask); } }