diff --git a/realtime_tools/include/realtime_tools/async_function_handler.hpp b/realtime_tools/include/realtime_tools/async_function_handler.hpp index 07535754..d815c109 100644 --- a/realtime_tools/include/realtime_tools/async_function_handler.hpp +++ b/realtime_tools/include/realtime_tools/async_function_handler.hpp @@ -122,6 +122,7 @@ class AsyncSchedulingPolicy * the async callback method. If true, the async callback method will not be called until the trigger predicate * returns true for the first time. Very useful when the type is DETACHED. * @param print_warnings Whether to print warnings when the async callback method is not triggered due to any reason. + * @param thread_name The custom name for the async thread. Defaults to the component name. Will be truncated to 15 characters. */ struct AsyncFunctionHandlerParams { @@ -178,6 +179,7 @@ struct AsyncFunctionHandlerParams * before starting the async callback method. Default is true. * - print_warnings (bool): Whether to print warnings when the async callback method is not triggered * due to any reason. Default is true. + * - thread_name (string): Name applied to the async thread. Defaults to component name. Truncated to 15 chars. * @param node The node that is used to get the parameters. * @param prefix Parameter prefix to use when accessing node parameters. */ @@ -216,6 +218,9 @@ struct AsyncFunctionHandlerParams if (node->has_parameter(prefix + "print_warnings")) { print_warnings = node->get_parameter(prefix + "print_warnings").as_bool(); } + if (node->has_parameter(prefix + "thread_name")) { + thread_name = node->get_parameter(prefix + "thread_name").as_string(); + } } int thread_priority = 50; @@ -227,6 +232,7 @@ struct AsyncFunctionHandlerParams std::function trigger_predicate = []() { return true; }; bool wait_until_initial_trigger = true; bool print_warnings = true; + std::string thread_name = ""; }; /** @@ -566,6 +572,17 @@ class AsyncFunctionHandler params_.logger, affinity_result.first, "Async worker thread is successfully pinned to the requested CPU cores!"); } + if (!params_.thread_name.empty()) { + const auto rename_result = realtime_tools::set_current_thread_name(params_.thread_name); + + if (!rename_result.first) { + RCLCPP_WARN( + params_.logger, "Could not set thread name for the async worker thread. Error: %s", + rename_result.second.c_str()); + } else { + RCLCPP_INFO(params_.logger, "%s", rename_result.second.c_str()); + } + } if (params_.scheduling_policy == AsyncSchedulingPolicy::SYNCHRONIZED) { execute_synchronized_callback(); } else { diff --git a/realtime_tools/include/realtime_tools/realtime_helpers.hpp b/realtime_tools/include/realtime_tools/realtime_helpers.hpp index e1685e82..ec42ab96 100644 --- a/realtime_tools/include/realtime_tools/realtime_helpers.hpp +++ b/realtime_tools/include/realtime_tools/realtime_helpers.hpp @@ -130,6 +130,15 @@ std::pair set_current_thread_affinity(int core); */ std::pair set_current_thread_affinity(const std::vector & cores); +/** + * Configure the current thread name. + * \param[in] name The name for the thread. + * On Linux, it will be automatically truncated to 15 characters. + * \returns a pair of a boolean indicating whether the operation succeeded or not + * and a message describing the result of the operation +*/ +std::pair set_current_thread_name(const std::string & name); + /** * Method to get the amount of available cpu cores * \ref https://man7.org/linux/man-pages/man3/sysconf.3.html diff --git a/realtime_tools/src/realtime_helpers.cpp b/realtime_tools/src/realtime_helpers.cpp index b840b01e..0cda5833 100644 --- a/realtime_tools/src/realtime_helpers.cpp +++ b/realtime_tools/src/realtime_helpers.cpp @@ -253,6 +253,39 @@ std::pair set_current_thread_affinity(const std::vector #endif } +std::pair set_current_thread_name(const std::string & name) +{ + if (name.empty()) { + return std::make_pair(false, "Thread name cannot be empty. This should not happen!"); + } + +#ifdef _WIN32 + std::wstring wname(name.begin(), name.end()); + HRESULT hr = SetThreadDescription(GetCurrentThread(), wname.c_str()); + if (SUCCEEDED(hr)) { + return std::make_pair(true, "Thread name: " + name); + } + return std::make_pair(false, "Failed to set thread name on Windows."); +#elif defined(__APPLE__) + std::string t_name = name.substr(0, 63); + std::string msg = + (name.length() > 63) ? "Thread name (truncated): " + t_name : "Thread name: " + t_name; + if (pthread_setname_np(t_name.c_str()) == 0) { + return std::make_pair(true, msg); + } + return std::make_pair(false, "Failed to set thread name on macOS."); +#else + std::string t_name = name.substr(0, 15); + std::string msg = + (name.length() > 15) ? "Thread name (truncated): " + t_name : "Thread name: " + t_name; + int rc = pthread_setname_np(pthread_self(), t_name.c_str()); + if (rc == 0) { + return std::make_pair(true, msg); + } + return std::make_pair(false, "Failed to set thread name. Error code: " + std::to_string(rc)); +#endif +} + int64_t get_number_of_available_processors() { #ifdef _WIN32