Skip to content

Conversation

@saikishor
Copy link
Member

This PR adds a few new methods that could be really helpful if we want to catch the information of the exported interfaces to improve the performance of the setting and getting of handles

@saikishor saikishor added backport-jazzy Triggers PR backport to ROS 2 jazzy. backport-kilted Triggers PR backport to ROS 2 kilted. labels Nov 18, 2025
@saikishor saikishor force-pushed the add/state_and_command_interface_handle_getters branch from 81b73e1 to 3472e37 Compare November 18, 2025 22:32
@codecov
Copy link

codecov bot commented Nov 18, 2025

Codecov Report

❌ Patch coverage is 50.64935% with 38 lines in your changes missing coverage. Please review.
✅ Project coverage is 89.40%. Comparing base (38124b7) to head (8e3fe4e).

Files with missing lines Patch % Lines
...re_interface/include/hardware_interface/handle.hpp 37.14% 19 Missing and 3 partials ⚠️
...ardware_interface/hardware_component_interface.hpp 61.90% 10 Missing and 6 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #2831      +/-   ##
==========================================
- Coverage   89.55%   89.40%   -0.16%     
==========================================
  Files         152      152              
  Lines       18087    18126      +39     
  Branches     1470     1474       +4     
==========================================
+ Hits        16198    16205       +7     
- Misses       1301     1329      +28     
- Partials      588      592       +4     
Flag Coverage Δ
unittests 89.40% <50.64%> (-0.16%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
...ardware_interface/hardware_component_interface.hpp 75.00% <61.90%> (-2.86%) ⬇️
...re_interface/include/hardware_interface/handle.hpp 75.88% <37.14%> (-10.79%) ⬇️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@saikishor saikishor marked this pull request as draft November 19, 2025 08:55
Copy link
Member

@christophfroehlich christophfroehlich left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You suggest storing the interface handles once inside the hardware components, instead of having to lookup the maps at every set/get call?

@saikishor
Copy link
Member Author

You suggest storing the interface handles once inside the hardware components, instead of having to lookup the maps at every set/get call?

Yes, this way the users will be able to cache the command interfaces and will be able to implement the logic they want. I think this way we have less execution time, so you might not need to do a lookup everytime.

@saikishor
Copy link
Member Author

However, I'm rethinking some part of API right now. I'll update with more API

@saikishor saikishor marked this pull request as ready for review November 19, 2025 17:29
Copy link
Member

@christophfroehlich christophfroehlich left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The API seems to be very useful from the user perspective, but I'm not sure about the compiler optimization I commented below..

@mhubii
Copy link

mhubii commented Nov 19, 2025

Thanks @saikishor! This should address #2816

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR enhances the HardwareComponentInterface by adding methods to retrieve state and command interface handles, along with overloaded getter/setter methods that accept handles directly. These additions enable caching of interface handles to improve performance by avoiding repeated map lookups during interface access operations.

Key Changes:

  • Added get_state_interface_handle() and get_command_interface_handle() methods to retrieve interface handles by name
  • Introduced overloaded get_state(), set_state(), get_command(), and set_command() methods that accept interface handles directly
  • Added get_value() methods in the Handle class that use output parameters instead of return values for better performance with large data types

Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
hardware_interface/include/hardware_interface/hardware_component_interface.hpp Added handle retrieval methods and handle-based getter/setter overloads with ensure_get/ensure_set parameters for state and command interfaces
hardware_interface/include/hardware_interface/handle.hpp Added get_value() methods with output parameters to support thread-safe value retrieval without copying return values

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

* @param lock The lock to access the value.
* @param value The variable to store the retrieved value.
* @return true if the value is retrieved successfully, false otherwise.
* @note The method is thread-safe and non-blocking.
Copy link

Copilot AI Nov 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation states the method is 'non-blocking', but the method uses std::shared_lock which can block if a writer holds the mutex. Consider clarifying this as 'non-blocking only if the lock is successfully acquired' or removing the non-blocking claim.

Suggested change
* @note The method is thread-safe and non-blocking.
* @note The method is thread-safe and non-blocking only if the lock is already acquired.
* Acquiring the lock itself may block if a writer holds the mutex.

Copilot uses AI. Check for mistakes.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we expose this method in the interface? I would make it protected. The Handle should be a thread save entity in my point of view and locking/unlocking should be handled internally. I think exposing of bool get_value(T & value) const should be enough or am i missing something?

And am i mistaken or is the function actually thradunsafe because if you create mutex_2 lock it with some std::shared_lock<std::shared_mutex> lock2(handle_mutex_, std::try_to_lock); and pass it i would break thread safety or not?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not, this is useful to avoid the copies and then you want to do it in a thread safe approach right?. Moreover, it has similar schema as set_value method

And am i mistaken or is the function actually thradunsafe because if you create mutex_2 lock it with some std::shared_lockstd::shared_mutex lock2(handle_mutex_, std::try_to_lock); and pass it i would break thread safety or not?

If you do that and if it doesn't own the lock, it would enter the condition !lock.owns_lock() immediately and then returns

* @tparam T The type of the value to be retrieved.
* @param value The variable to store the retrieved value.
* @return true if the value is retrieved successfully, false otherwise.
* @note The method is thread-safe and non-blocking.
Copy link

Copilot AI Nov 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation incorrectly states the method is 'non-blocking'. The method uses std::try_to_lock which makes it non-blocking in terms of lock acquisition, but it returns false if the lock cannot be acquired rather than guaranteeing non-blocking behavior. The documentation should clarify that the method attempts to acquire the lock without blocking and returns false if unsuccessful.

Copilot uses AI. Check for mistakes.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Come onnnnn copilot 😅😅

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it has a point in being more specific what "retrieved successfully" means?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so. Atleast, I wrote it as per my understanding

* @param lock The lock to access the value.
* @param value The variable to store the retrieved value.
* @return true if the value is retrieved successfully, false otherwise.
* @note The method is thread-safe and non-blocking.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we expose this method in the interface? I would make it protected. The Handle should be a thread save entity in my point of view and locking/unlocking should be handled internally. I think exposing of bool get_value(T & value) const should be enough or am i missing something?

And am i mistaken or is the function actually thradunsafe because if you create mutex_2 lock it with some std::shared_lock<std::shared_mutex> lock2(handle_mutex_, std::try_to_lock); and pass it i would break thread safety or not?

}
if (ensure_set)
{
std::unique_lock<std::shared_mutex> lock(interface_handle->get_mutex());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think id rather move this to the handle. have a blocking and a non blocking method where the blocking grantees to set the value and the non blocking is the current.

Then in the user code we can just directly call the Shared pointer instance and call either the blocking or non blocking method

*/
template <typename T>
void set_state(const std::string & interface_name, const T & value)
void set_state(const std::string & interface_name, const T & value, bool ensure_set = true)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i know it is written in the comment but i think ensure_set masks a bit the fact that we now have a different api.

I would maybe just distinguish on api level

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

creating different API is sometimes confusing for beginners, better to have one API and add proper documentation for the users to understand. With VSCode, you can understand the args while coding from the docs

* \return True if the value was retrieved successfully, false otherwise.
*/
template <typename T>
bool get_state(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For get_state basically the same reasoning applies as for set_state

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I modified a bit

@saikishor saikishor requested a review from mamueluth November 27, 2025 23:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport-jazzy Triggers PR backport to ROS 2 jazzy. backport-kilted Triggers PR backport to ROS 2 kilted.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants