-
Notifications
You must be signed in to change notification settings - Fork 255
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Gracefully handle SIGINT and SIGTERM in rosbag2 recorder #1301
Gracefully handle SIGINT and SIGTERM in rosbag2 recorder #1301
Conversation
ee6475f
to
1f2c53a
Compare
Gist: https://gist.githubusercontent.com/MichaelOrlov/95900a255259b630779ca19eff1f12d4/raw/db8ac815a5d0ab97586d75360a4a7b13d9f2786c/ros2.repos |
@emersonknapp @james-rms This PR is relatively small and ready for review |
@clalancette @emersonknapp @james-rms Kindly ping for review. |
@@ -190,16 +190,23 @@ class Player | |||
class Recorder | |||
{ | |||
private: | |||
std::unique_ptr<rclcpp::executors::SingleThreadedExecutor> exec_; | |||
static std::unique_ptr<rclcpp::executors::SingleThreadedExecutor> exec_; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this member static now? Because of the rclcpp::init/shutdown
in here, it doesn't really make sense to have multiple rclpy::Recorder
instances, but the semantics of this global variable don't really make sense to me. Would these be better as just a global variable, or a singleton object?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@emersonknapp Hmm, I didn't think about the situation when multiple instances of the rclpy::Recorder
and one static executor. This is a good point.
I made the executor static to be able to call its static exec_->cancel();
method from signal handlers to interrupt execution.
In the case of the situation when multiple instances of the rclpy::Recorder
exists it looks reasonable I would say even must have to make the executor as static to convey signal interruption event to all instances.
However, I agree that it would be better to wrap the static executor in the singleton pattern. I am sorry I missed it, - will fix.
Good catch!
48c870b
to
3197b6f
Compare
The build_and_test job failed with exactly the same error messages as I have seen recently in #1342
It looks like something got changed recently in underlying sources which is leading to those error. |
3197b6f
to
744b09a
Compare
@emersonknapp Well after a more detailed analysis and debug session, I wasn't able to prove that freeze happening inside I rolled back to the non-static executor. And the rationale for that is that it seems the previous implementation with static executor member looks a bit overengineering and doesn't align well with the design of the class since we have context init/shutdown in the constructor and destructor. But the existence of the executor doesn't make sense without a valid context. I think we can turn back to the version with static executor later on if we will find out that the current implementation doesn't satisfy our needs. Thoughts? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is fine for now. If we do want to support multiple Recorders from python we will need to do some other changes anyways.
744b09a
to
9819e5f
Compare
- Call recorder->stop() and exec_->cancel() instead of rclcpp::shutdown Signed-off-by: Michael Orlov <[email protected]>
Signed-off-by: Michael Orlov <[email protected]>
- In case when signal will arrive we will trigger our internal exit_ variable and wait while current exec_->spin_all(10msec) will exit. Signed-off-by: Michael Orlov <[email protected]>
- Run exec->spin() in a separate thread, because we need to call exec->cancel() after recorder->stop() to be able to send notifications about bag split and close. - Wait on conditional variable for exit_ flag Signed-off-by: Michael Orlov <[email protected]>
- Add `record_thread.join()` before trying to parse metadata. Signed-off-by: Michael Orlov <[email protected]>
45303b8
to
896d8b8
Compare
@emersonknapp I found a better way to handle executor spin() interruption via spinning it in a separate thread and wait on the conditional variable before triggering |
Re-run CI |
https://github.com/Mergifyio backport iron |
✅ Backports have been created
|
* Gracefully handle SIGINT and SIGTERM signal for rosbag2 recorder - Call recorder->stop() and exec_->cancel() instead of rclcpp::shutdown Signed-off-by: Michael Orlov <[email protected]> * Use singleton for static executor in the rosbag2_py::Recorder Signed-off-by: Michael Orlov <[email protected]> * Rollback to the non-static executor and don't call executor->cancel() - In case when signal will arrive we will trigger our internal exit_ variable and wait while current exec_->spin_all(10msec) will exit. Signed-off-by: Michael Orlov <[email protected]> * Spin recorder node in a separate thread for better handling exit - Run exec->spin() in a separate thread, because we need to call exec->cancel() after recorder->stop() to be able to send notifications about bag split and close. - Wait on conditional variable for exit_ flag Signed-off-by: Michael Orlov <[email protected]> * Address race condition in rosbag2_py.test_record_cancel - Add `record_thread.join()` before trying to parse metadata. Signed-off-by: Michael Orlov <[email protected]> --------- Signed-off-by: Michael Orlov <[email protected]> (cherry picked from commit 46a23e9)
https://github.com/Mergifyio backport humble |
✅ Backports have been created
|
* Gracefully handle SIGINT and SIGTERM signal for rosbag2 recorder - Call recorder->stop() and exec_->cancel() instead of rclcpp::shutdown Signed-off-by: Michael Orlov <[email protected]> * Use singleton for static executor in the rosbag2_py::Recorder Signed-off-by: Michael Orlov <[email protected]> * Rollback to the non-static executor and don't call executor->cancel() - In case when signal will arrive we will trigger our internal exit_ variable and wait while current exec_->spin_all(10msec) will exit. Signed-off-by: Michael Orlov <[email protected]> * Spin recorder node in a separate thread for better handling exit - Run exec->spin() in a separate thread, because we need to call exec->cancel() after recorder->stop() to be able to send notifications about bag split and close. - Wait on conditional variable for exit_ flag Signed-off-by: Michael Orlov <[email protected]> * Address race condition in rosbag2_py.test_record_cancel - Add `record_thread.join()` before trying to parse metadata. Signed-off-by: Michael Orlov <[email protected]> --------- Signed-off-by: Michael Orlov <[email protected]> (cherry picked from commit 46a23e9) # Conflicts: # rosbag2_py/src/rosbag2_py/_transport.cpp # rosbag2_py/test/test_transport.py
* Gracefully handle SIGINT and SIGTERM signal for rosbag2 recorder - Call recorder->stop() and exec_->cancel() instead of rclcpp::shutdown Signed-off-by: Michael Orlov <[email protected]> * Use singleton for static executor in the rosbag2_py::Recorder Signed-off-by: Michael Orlov <[email protected]> * Rollback to the non-static executor and don't call executor->cancel() - In case when signal will arrive we will trigger our internal exit_ variable and wait while current exec_->spin_all(10msec) will exit. Signed-off-by: Michael Orlov <[email protected]> * Spin recorder node in a separate thread for better handling exit - Run exec->spin() in a separate thread, because we need to call exec->cancel() after recorder->stop() to be able to send notifications about bag split and close. - Wait on conditional variable for exit_ flag Signed-off-by: Michael Orlov <[email protected]> * Address race condition in rosbag2_py.test_record_cancel - Add `record_thread.join()` before trying to parse metadata. Signed-off-by: Michael Orlov <[email protected]> --------- Signed-off-by: Michael Orlov <[email protected]> (cherry picked from commit 46a23e9) # Conflicts: # rosbag2_py/src/rosbag2_py/_transport.cpp # rosbag2_py/test/test_transport.py
* Gracefully handle SIGINT and SIGTERM signal for rosbag2 recorder - Call recorder->stop() and exec_->cancel() instead of rclcpp::shutdown Signed-off-by: Michael Orlov <[email protected]> * Use singleton for static executor in the rosbag2_py::Recorder Signed-off-by: Michael Orlov <[email protected]> * Rollback to the non-static executor and don't call executor->cancel() - In case when signal will arrive we will trigger our internal exit_ variable and wait while current exec_->spin_all(10msec) will exit. Signed-off-by: Michael Orlov <[email protected]> * Spin recorder node in a separate thread for better handling exit - Run exec->spin() in a separate thread, because we need to call exec->cancel() after recorder->stop() to be able to send notifications about bag split and close. - Wait on conditional variable for exit_ flag Signed-off-by: Michael Orlov <[email protected]> * Address race condition in rosbag2_py.test_record_cancel - Add `record_thread.join()` before trying to parse metadata. Signed-off-by: Michael Orlov <[email protected]> --------- Signed-off-by: Michael Orlov <[email protected]> (cherry picked from commit 46a23e9) Co-authored-by: Michael Orlov <[email protected]>
* Gracefully handle SIGINT and SIGTERM signal for rosbag2 recorder - Call recorder->stop() and exec_->cancel() instead of rclcpp::shutdown Signed-off-by: Michael Orlov <[email protected]> * Use singleton for static executor in the rosbag2_py::Recorder Signed-off-by: Michael Orlov <[email protected]> * Rollback to the non-static executor and don't call executor->cancel() - In case when signal will arrive we will trigger our internal exit_ variable and wait while current exec_->spin_all(10msec) will exit. Signed-off-by: Michael Orlov <[email protected]> * Spin recorder node in a separate thread for better handling exit - Run exec->spin() in a separate thread, because we need to call exec->cancel() after recorder->stop() to be able to send notifications about bag split and close. - Wait on conditional variable for exit_ flag Signed-off-by: Michael Orlov <[email protected]> * Address race condition in rosbag2_py.test_record_cancel - Add `record_thread.join()` before trying to parse metadata. Signed-off-by: Michael Orlov <[email protected]> --------- Signed-off-by: Michael Orlov <[email protected]> (cherry picked from commit 46a23e9) # Conflicts: # rosbag2_py/src/rosbag2_py/_transport.cpp # rosbag2_py/test/test_transport.py
…ckport #1301) (#1395) * Gracefully handle SIGINT and SIGTERM in rosbag2 recorder (#1301) * Gracefully handle SIGINT and SIGTERM signal for rosbag2 recorder - Call recorder->stop() and exec_->cancel() instead of rclcpp::shutdown Signed-off-by: Michael Orlov <[email protected]> * Use singleton for static executor in the rosbag2_py::Recorder Signed-off-by: Michael Orlov <[email protected]> * Rollback to the non-static executor and don't call executor->cancel() - In case when signal will arrive we will trigger our internal exit_ variable and wait while current exec_->spin_all(10msec) will exit. Signed-off-by: Michael Orlov <[email protected]> * Spin recorder node in a separate thread for better handling exit - Run exec->spin() in a separate thread, because we need to call exec->cancel() after recorder->stop() to be able to send notifications about bag split and close. - Wait on conditional variable for exit_ flag Signed-off-by: Michael Orlov <[email protected]> * Address race condition in rosbag2_py.test_record_cancel - Add `record_thread.join()` before trying to parse metadata. Signed-off-by: Michael Orlov <[email protected]> --------- Signed-off-by: Michael Orlov <[email protected]> (cherry picked from commit 46a23e9) # Conflicts: # rosbag2_py/src/rosbag2_py/_transport.cpp # rosbag2_py/test/test_transport.py * Address merge conflicts Signed-off-by: Michael Orlov <[email protected]> * Workaround for segmentation fault in rclcpp::SignalHandler::uninstall() Signed-off-by: Michael Orlov <[email protected]> --------- Signed-off-by: Michael Orlov <[email protected]> Co-authored-by: Michael Orlov <[email protected]>
Hey, I'm on Ubuntu, Iron and this doesn't seem to work. You can easily reproduce it with: import rclpy
from rclpy.node import Node
from rosbag2_py import Recorder
class RosBagRecorder(Node):
def __init__(self):
super().__init__("rosbag_recorder")
self.recorder = Recorder()
def main():
rclpy.init()
node = RosBagRecorder()
rclpy.spin(node)
if __name__ == "__main__":
main() The process never exits with CTRL-C opened #1458 |
Followup from Add recorder stop() API #1300
Relates Add
BagSplitInfo
on bag close #1213Relates Windows CI can't find
metadata.yaml
file when usingros2 bag record
#926Call recorder->stop() and exec_->cancel() instead of rclcpp::shutdown in signal handlers.
Note: Will address Windows specific signals handling in a separate PR.