{"id":31582186,"url":"https://github.com/ijnek/nested_services_rclcpp_demo","last_synced_at":"2025-10-05T22:33:22.363Z","repository":{"id":225035557,"uuid":"764903180","full_name":"ijnek/nested_services_rclcpp_demo","owner":"ijnek","description":"Demo of calling services from within callbacks in rclcpp (ROS 2)","archived":false,"fork":false,"pushed_at":"2024-02-28T23:45:07.000Z","size":3,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-02-29T00:37:53.167Z","etag":null,"topics":["rclcpp","ros2","ros2-humble","ros2-iron","ros2-rolling"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ijnek.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2024-02-28T23:12:32.000Z","updated_at":"2024-02-29T00:37:57.098Z","dependencies_parsed_at":"2024-02-29T00:47:58.892Z","dependency_job_id":null,"html_url":"https://github.com/ijnek/nested_services_rclcpp_demo","commit_stats":null,"previous_names":["ijnek/nested_services_rclcpp_demo"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ijnek/nested_services_rclcpp_demo","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ijnek%2Fnested_services_rclcpp_demo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ijnek%2Fnested_services_rclcpp_demo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ijnek%2Fnested_services_rclcpp_demo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ijnek%2Fnested_services_rclcpp_demo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ijnek","download_url":"https://codeload.github.com/ijnek/nested_services_rclcpp_demo/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ijnek%2Fnested_services_rclcpp_demo/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278532002,"owners_count":26002338,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-05T02:00:06.059Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["rclcpp","ros2","ros2-humble","ros2-iron","ros2-rolling"],"created_at":"2025-10-05T22:32:47.989Z","updated_at":"2025-10-05T22:33:22.357Z","avatar_url":"https://github.com/ijnek.png","language":"C++","readme":"# Demo of calling services from within callbacks\n\nA major pain point of ROS 2 is not being able to call services from within any sort of callback.\nThis is caused by the executor being occupied by the callback, and not free to process service responses, causing a deadlock.\n\nMany forum posts suggest using multiple callback groups to solve this problem, but this involves creating\nmultiple threads, which is not always desirable. It also means your node will require a MultiThreadedExecutor.\n\nThis solution provides a way to use a SingleThreadedExecutor, and still be able to call services from within callbacks. Only one executor thread is used, and it is not blocked by the callback.\n\nIt utilizes asynchronous service calls and **deferred service responses**, a feature available since ROS 2 Humble.\n\nIn this demo, NodeA and NodeB are created, and added to a SingleThreadedExecutor, and spun. Nothing special.\n\n```cpp\nint main(int argc, char * argv[])\n{\n  rclcpp::init(argc, argv);\n  rclcpp::executors::SingleThreadedExecutor executor;\n  auto node1 = std::make_shared\u003cNode1\u003e();\n  auto node2 = std::make_shared\u003cNode2\u003e();\n  executor.add_node(node1);\n  executor.add_node(node2);\n  executor.spin();\n  rclcpp::shutdown();\n  return 0;\n}\n```\n\nNode1 provides a service of type `std_srvs/srv/Trigger` on `node1/trigger`.\nIt also creates a client to call a service of type `std_srvs/srv/Trigger` on `node2/trigger`.\n\n```cpp\nclass Node1 : public rclcpp::Node\n{\npublic:\n  Node1() : rclcpp::Node(\"node1\")\n  {\n    service_ = create_service\u003cstd_srvs::srv::Trigger\u003e(\n      \"node1/trigger\", std::bind(\u0026Node1::srv_cb, this, _1, _2, _3));\n    client_ = create_client\u003cstd_srvs::srv::Trigger\u003e(\"node2/trigger\");\n  }\nprivate:\n  void srv_cb(std::shared_ptr\u003crclcpp::Service\u003cstd_srvs::srv::Trigger\u003e\u003e service,\n              const std::shared_ptr\u003crmw_request_id_t\u003e request_header,\n              const std::shared_ptr\u003cstd_srvs::srv::Trigger::Request\u003e request)\n  {\n    ...\n  }\n\n  rclcpp::Service\u003cstd_srvs::srv::Trigger\u003e::SharedPtr service_;\n  rclcpp::Client\u003cstd_srvs::srv::Trigger\u003e::SharedPtr client_;\n};\n```\n\nNodeB provides a service of type `std_srvs/srv/Trigger` on `node2/trigger`, which simply responds with a message saying ``\"Hello!\"``.\n\n```cpp\nclass Node2 : public rclcpp::Node\n{\npublic:\n  Node2() : rclcpp::Node(\"node2\")\n  {\n    service_ = create_service\u003cstd_srvs::srv::Trigger\u003e(\n      \"node2/trigger\", std::bind(\u0026Node2::srv_cb, this, _1, _2));\n  }\nprivate:\n  void srv_cb(const std::shared_ptr\u003cstd_srvs::srv::Trigger::Request\u003e request,\n              std::shared_ptr\u003cstd_srvs::srv::Trigger::Response\u003e response)\n  {\n    (void)request;\n    response-\u003emessage = \"Hello!\";\n  }\n\n  rclcpp::Service\u003cstd_srvs::srv::Trigger\u003e::SharedPtr service_;\n};\n```\n\nNow, going back to Node1's srv_cb function, this function's desired behavior is to call the ``node2/trigger`` service, prepend the response's message with ``\"Node 2 said: \"``, and send that back.\n\nTo do this, we create an asynchronous callback function, and call the second service with ``async_send_request`` function. This allows the executor to finish srv_cb and free up without returning a response. The executor can then listen to the service response from Node2. When the service response is received, the ``async_cb`` lambda is called, which sends a deferred response back to the original service call.\n\nNote the callback signature is different to the ones in the basic service examples. This is because we are using a deferred response.\n\n```cpp\nvoid srv_cb(std::shared_ptr\u003crclcpp::Service\u003cstd_srvs::srv::Trigger\u003e\u003e service,\n            const std::shared_ptr\u003crmw_request_id_t\u003e request_header,\n            const std::shared_ptr\u003cstd_srvs::srv::Trigger::Request\u003e request)\n{\n  auto async_cb = [service, request_header, request](rclcpp::Client\u003cstd_srvs::srv::Trigger\u003e::SharedFuture future) {\n    (void)request;\n    std_srvs::srv::Trigger::Response response;\n    response.message = \"Node 2 said: '\" + future.get()-\u003emessage + \"'\";\n    service-\u003esend_response(*request_header, response);\n  };\n\n  auto request_inner = std::make_shared\u003cstd_srvs::srv::Trigger::Request\u003e();\n  client_-\u003easync_send_request(request_inner, async_cb);\n}\n```\n\nTo test this, run the program:\n```\nros2 run nested_services_rclcpp_demo nested_service\n```\n\nand call the service from the command line:\n\n```bash\nros2 service call /node1/trigger std_srvs/srv/Trigger\n```\n\nYou should expect a response:\n\n```bash\n ros2 service call /node1/trigger std_srvs/srv/Trigger\nrequester: making request: std_srvs.srv.Trigger_Request()\n\nresponse:\nstd_srvs.srv.Trigger_Response(success=False, message=\"Node 2 said: 'Hello!'\")\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fijnek%2Fnested_services_rclcpp_demo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fijnek%2Fnested_services_rclcpp_demo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fijnek%2Fnested_services_rclcpp_demo/lists"}