Skip to content

Commit 4053651

Browse files
committed
Allow deferred service replies. (#29)
1 parent 36225bc commit 4053651

3 files changed

Lines changed: 69 additions & 13 deletions

File tree

ros_babel_fish/include/ros_babel_fish/detail/any_service_callback.hpp

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@
44
#ifndef ROS_BABEL_FISH_BABEL_FISH_ANY_SERVICE_CALLBACK_HPP
55
#define ROS_BABEL_FISH_BABEL_FISH_ANY_SERVICE_CALLBACK_HPP
66

7+
#include "ros_babel_fish/messages/compound_message.hpp"
8+
79
#include <rclcpp/any_service_callback.hpp>
810

911
namespace ros_babel_fish
1012
{
1113
class BabelFishService;
12-
class CompoundMessage;
1314

1415
/**
1516
* Designed to be compatible with rclcpp::AnyServiceCallback compatible callbacks with equivalent tracing support.
@@ -53,26 +54,38 @@ class AnyServiceCallback
5354
}
5455
}
5556

56-
void dispatch( const std::shared_ptr<BabelFishService> &service_handle,
57-
const std::shared_ptr<rmw_request_id_t> &request_header,
58-
std::shared_ptr<CompoundMessage> request, std::shared_ptr<CompoundMessage> response )
57+
//! Dispatches the request to the registered callback.
58+
//! @return The filled response which the caller should send, or nullptr if the callback deferred
59+
//! the response (a defer variant), in which case the caller must not send.
60+
std::shared_ptr<CompoundMessage> dispatch( const std::shared_ptr<BabelFishService> &service_handle,
61+
const std::shared_ptr<rmw_request_id_t> &request_header,
62+
std::shared_ptr<CompoundMessage> request,
63+
const MessageMembersIntrospection &response_type )
5964
{
6065
TRACEPOINT( callback_start, static_cast<const void *>( this ), false );
66+
if ( std::holds_alternative<SharedPtrDeferResponseCallback>( callback_ ) ) {
67+
const auto &cb = std::get<SharedPtrDeferResponseCallback>( callback_ );
68+
cb( request_header, std::move( request ) );
69+
TRACEPOINT( callback_end, static_cast<const void *>( this ) );
70+
return nullptr;
71+
}
72+
if ( std::holds_alternative<SharedPtrDeferResponseCallbackWithServiceHandle>( callback_ ) ) {
73+
const auto &cb = std::get<SharedPtrDeferResponseCallbackWithServiceHandle>( callback_ );
74+
cb( service_handle, request_header, std::move( request ) );
75+
TRACEPOINT( callback_end, static_cast<const void *>( this ) );
76+
return nullptr;
77+
}
78+
auto response = CompoundMessage::make_shared( response_type );
6179
if ( std::holds_alternative<SharedPtrCallback>( callback_ ) ) {
6280
(void)request_header;
6381
const auto &cb = std::get<SharedPtrCallback>( callback_ );
6482
cb( std::move( request ), response );
6583
} else if ( std::holds_alternative<SharedPtrWithRequestHeaderCallback>( callback_ ) ) {
6684
const auto &cb = std::get<SharedPtrWithRequestHeaderCallback>( callback_ );
6785
cb( request_header, std::move( request ), response );
68-
} else if ( std::holds_alternative<SharedPtrDeferResponseCallback>( callback_ ) ) {
69-
const auto &cb = std::get<SharedPtrDeferResponseCallback>( callback_ );
70-
cb( request_header, std::move( request ) );
71-
} else if ( std::holds_alternative<SharedPtrDeferResponseCallbackWithServiceHandle>( callback_ ) ) {
72-
const auto &cb = std::get<SharedPtrDeferResponseCallbackWithServiceHandle>( callback_ );
73-
cb( service_handle, request_header, std::move( request ) );
7486
}
7587
TRACEPOINT( callback_end, static_cast<const void *>( this ) );
88+
return response;
7689
}
7790

7891
void register_callback_for_tracing()

ros_babel_fish/src/detail/babel_fish_service.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,11 @@ void BabelFishService::handle_request( std::shared_ptr<rmw_request_id_t> request
8282
std::shared_ptr<void> request )
8383
{
8484
auto typed_request = CompoundMessage::make_shared( type_support_->request(), request );
85-
auto response = CompoundMessage::make_shared( type_support_->response() );
86-
callback_.dispatch( this->shared_from_this(), request_header, typed_request, response );
87-
send_response( *request_header, *response );
85+
// Defer variants do not fill a response and return nullptr, in which case the response is sent
86+
// later by the user via send_response. Immediate variants return the filled response to send.
87+
auto response = callback_.dispatch( this->shared_from_this(), request_header, typed_request,
88+
type_support_->response() );
89+
if ( response )
90+
send_response( *request_header, *response );
8891
}
8992
} // namespace ros_babel_fish

ros_babel_fish/test/service.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,46 @@ TEST( ServiceTest, server )
6565
EXPECT_EQ( result->sum, 512 + 314 + 1337 );
6666
}
6767

68+
TEST( ServiceTest, deferredResponse )
69+
{
70+
BabelFish fish;
71+
auto type_support = fish.get_service_type_support( "example_interfaces/srv/AddTwoInts" );
72+
std::shared_ptr<rmw_request_id_t> stored_header;
73+
CompoundMessage::SharedPtr stored_request;
74+
rclcpp::TimerBase::SharedPtr send_timer;
75+
BabelFishService::SharedPtr service;
76+
// Register the defer variant (header, request) which must NOT auto-send. The response is sent
77+
// later, back on the spinner thread, via a one-shot wall timer so take_request and send_response
78+
// stay on the same thread.
79+
service = fish.create_service(
80+
*node, "/test_service_server/deferred_two_ints", "example_interfaces/srv/AddTwoInts",
81+
[&]( std::shared_ptr<rmw_request_id_t> header, CompoundMessage::SharedPtr request ) {
82+
stored_header = std::move( header );
83+
stored_request = std::move( request );
84+
send_timer = node->create_wall_timer( 250ms, [&]() {
85+
send_timer->cancel();
86+
auto response = CompoundMessage::make_shared( type_support->response() );
87+
response->set( "sum", stored_request->get<int64_t>( "a" ) +
88+
stored_request->get<int64_t>( "b" ) + 100 );
89+
service->send_response( *stored_header, *response );
90+
} );
91+
} );
92+
auto req = std::make_shared<example_interfaces::srv::AddTwoInts::Request>();
93+
req->a = 7;
94+
req->b = 5;
95+
auto client = node->create_client<example_interfaces::srv::AddTwoInts>(
96+
"test_service_server/deferred_two_ints" );
97+
ASSERT_TRUE( client->wait_for_service( 5s ) );
98+
auto response_future = client->async_send_request( req );
99+
// Regression guard: with the auto-send disabled for defer variants, the client must not receive
100+
// an (empty) response before the deferred send actually fires.
101+
EXPECT_EQ( response_future.wait_for( 100ms ), std::future_status::timeout );
102+
ASSERT_EQ( response_future.wait_for( 5s ), std::future_status::ready );
103+
auto result = response_future.get();
104+
ASSERT_NE( result, nullptr );
105+
EXPECT_EQ( result->sum, 7 + 5 + 100 );
106+
}
107+
68108
int main( int argc, char **argv )
69109
{
70110
testing::InitGoogleTest( &argc, argv );

0 commit comments

Comments
 (0)