Skip to content

Conversation

@MichaelOrlov
Copy link
Contributor

Description

This PR adds a new API to be able to disable and enable subscription's callbacks.

Is this user-facing behavior change?

Yes. The new API disable_callbacks() and enable_callbacks() are available for the SubscriptionBase class.

Did you use Generative AI?

Yes, I am using GitHub Copilot, GPT-5.0 in my workflow to help with Doxygen comments and unit tests.

Additional Information

This PR can't be backported due to the ABI breaking changes. It was added two new virtual functions.

@MichaelOrlov MichaelOrlov linked an issue Nov 15, 2025 that may be closed by this pull request
@MichaelOrlov MichaelOrlov marked this pull request as ready for review November 15, 2025 02:28
@MichaelOrlov
Copy link
Contributor Author

@ros-pull-request-builder retest this please

@MichaelOrlov MichaelOrlov requested a review from ahcorde November 17, 2025 20:52
@ahcorde
Copy link
Contributor

ahcorde commented Nov 18, 2025

Pulls: #2985
Gist: https://gist.githubusercontent.com/ahcorde/a411230422d2605afd32e975ecd65fad/raw/905ba8c783b44ae6c3ff1615db00916120eb0d5b/ros2.repos
BUILD args: --packages-above-and-dependencies ‎rclcpp
TEST args: --packages-above ‎rclcpp
ROS Distro: rolling
Job: ci_launcher
ci_launcher ran: https://ci.ros2.org/job/ci_launcher/17519

  • Linux Build Status
  • Linux-aarch64 Build Status
  • Linux-rhel Build Status
  • Windows Build Status

@MichaelOrlov
Copy link
Contributor Author

Interesting. The CI failed due to the

Package '‎rclcpp' specified with --packages-above-and-dependencies was not found

Believe me or not, I didn't delete it in this PR ;)

@ahcorde
Copy link
Contributor

ahcorde commented Nov 18, 2025

Pulls: #2985
Gist: https://gist.githubusercontent.com/ahcorde/107f28b5a40704d3cc70d5a20fe428d9/raw/905ba8c783b44ae6c3ff1615db00916120eb0d5b/ros2.repos
BUILD args: --packages-above-and-dependencies rclcpp
TEST args: --packages-above rclcpp
ROS Distro: rolling
Job: ci_launcher
ci_launcher ran: https://ci.ros2.org/job/ci_launcher/17524

  • Linux Build Status
  • Linux-aarch64 Build Status
  • Linux-rhel Build Status
  • Windows Build Status

@ahcorde
Copy link
Contributor

ahcorde commented Nov 18, 2025

Interesting. The CI failed due to the

Package '‎rclcpp' specified with --packages-above-and-dependencies was not found

Believe me or not, I didn't delete it in this PR ;)

😄

My terminal introduced a wierd character, relaunching again

Copy link
Collaborator

@jmachowinski jmachowinski left a comment

Choose a reason for hiding this comment

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

Thanks for the patch, I wanted to look into this myself for ages, but just did not find any time...

Copy link
Collaborator

@fujitatomoya fujitatomoya left a comment

Choose a reason for hiding this comment

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

overall, lgtm.

@jmachowinski
Copy link
Collaborator

I had a second thought about the current approach, and I think it does not fix the issue, just makes it less likely. The subscriber can be deleted at any point in time, even during the execution of callback.
I guess to solve this we will need to hold a lock during callback and we will need to acquire the lock during disabling.

@MichaelOrlov
Copy link
Contributor Author

MichaelOrlov commented Nov 19, 2025

I had a second thought about the current approach, and I think it does not fix the issue, just makes it less likely. The subscriber can be deleted at any point in time, even during the execution of callback. I guess to solve this we will need to hold a lock during callback and we will need to acquire the lock during disabling.

But is it really a problem? The callback has its own liveliness time anyway.
Even if the subscription will be deleted at the time when the callback is executing (which shouldn't be by design since the callback is called from the executor via the dispatch method and we keep an instance of the subscription there), it will not cause any faulty situations since the callback is independent from the subscription.
The key point is to make sure that no callbacks are called after we call disable_callbacks().

@jmachowinski
Copy link
Collaborator

But is it really a problem? The callback has its own liveliness time anyway.

Yes, the problem is that you can't figure out when it is safe to delete the object the callback is pointing to.

Lets say you have something like class MyMessageDisapatcher that handles all your callbacks from the subscriber.
You live in a lifecycle world, and by policy you delete all objects used in the node on cycling from running -> configured-> inactive -> active on system error recovery. The command to cycle comes from a service call, using a different callback group, therefore the callback is executed in a second thread. In this scenario (and by the way this is not made up, this is our setup) you can't figure out when it is safe to delete MyMessageDisapatcher. If the disable_callback call does not block or somehow ensures, that no callback is going on, code that calls disable_callbacks and afterwards directly deletes MyMessageDisapatcher still triggers an invalid memory access.

@MichaelOrlov
Copy link
Contributor Author

@jmachowinski, I see your use case. In your scenario, the problematic part is that you need to delete MyMessageDisapatcher with the callbacks by the service request.
I will try to implement a lock as you described inside disable_callbacks and callback dispatch functions.
However, it seems your workflow is a bit suboptimal and not robust by design.
Maybe make sense to redesign it such a way that MyMessageDisapatcher is persistent and lives as long as the node is live, but deletes/recreates subscriptions by the service request, i.e., when the lifecycle node needs to change state.

@MichaelOrlov
Copy link
Contributor Author

Copy link
Collaborator

@jmachowinski jmachowinski left a comment

Choose a reason for hiding this comment

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

Rest looks fine.

@MichaelOrlov
Copy link
Contributor Author

@jmachowinski, kindly ping here to reiterate on the review of the last two commits.

@MichaelOrlov
Copy link
Contributor Author

Pulls: #2985
Gist: https://gist.githubusercontent.com/MichaelOrlov/858df3b489887929e03235892f22539a/raw/905ba8c783b44ae6c3ff1615db00916120eb0d5b/ros2.repos
BUILD args: --packages-above-and-dependencies rclcpp
TEST args: --packages-above rclcpp
ROS Distro: rolling
Job: ci_launcher
ci_launcher ran: https://ci.ros2.org/job/ci_launcher/17604

  • Linux Build Status
  • Linux-aarch64 Build Status
  • Linux-rhel Build Status
  • Windows Build Status

@MichaelOrlov
Copy link
Contributor Author

The CI is yellowish, but it seems it is unrelated.
Does anybody know if this is okay and expected to be this way?

@MichaelOrlov
Copy link
Contributor Author

Ahh, I've figured it out. The CI is yellowish due to the new warnings

‘void rclcpp::spin_some(Node::SharedPtr)’ is deprecated: use SingleThreadedExecutor::spin_some instead [-Wdeprecated-declarations] 346 | rclcpp::spin_some(node_);

That is because I was using a deprecated rclcpp::spin_some(node_) in the newly added tests.

I've just fixed it with the new commit by using spin_some() from the rclcpp::executors::SingleThreadedExecutor
directly.

@MichaelOrlov
Copy link
Contributor Author

Re-run CI jobs that had warnings

  • Linux Build Status
  • Linux-aarch64 Build Status
  • Linux-rhel Build Status

Reasoning:
 To avoid possible UB when callbacks disabled during callback
 execution and callback handler object deleted while execution hasn't
 been finished yet.

Signed-off-by: Michael Orlov <[email protected]>
Note: We can't reuse `on_new_event_callback_mutex_` from the
EventHandlerBase because those mutex used to protect another type of
callbacks.

Signed-off-by: Michael Orlov <[email protected]>
Note: While we are temporary removes the on new message callback and all
 on new event callbacks from the underlying rmw layer to prevent them
 from being called. We can't guarantee that the currently executing
 on_new_[message]event callbacks are finished before we exit from the
 disable_callbacks().

Signed-off-by: Michael Orlov <[email protected]>
- Use spin_some() from the rclcpp::executors::SingleThreadedExecutor
 directly.

Signed-off-by: Michael Orlov <[email protected]>
@MichaelOrlov MichaelOrlov force-pushed the morlov/add-disable-enable-callbacks-api-to-subscriptions branch from 0361906 to dcdb56d Compare November 26, 2025 01:46
@MichaelOrlov
Copy link
Contributor Author

@ros-pull-request-builder retest this please

@MichaelOrlov
Copy link
Contributor Author

Re-run Linux CI job to see if it will pass after addressing pub-sub tests failure

  • Linux Build Status

@MichaelOrlov
Copy link
Contributor Author

The Linux CI job passed successfully. Re-running another two

  • Linux-aarch64 Build Status
  • Linux-rhel Build Status

@MichaelOrlov
Copy link
Contributor Author

All CI jobs passed green. Merging then.

@MichaelOrlov MichaelOrlov merged commit d3c95a6 into rolling Nov 27, 2025
2 of 3 checks passed
@MichaelOrlov MichaelOrlov deleted the morlov/add-disable-enable-callbacks-api-to-subscriptions branch November 27, 2025 19:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add ability to disable subscription's callbacks

5 participants