@@ -33,6 +33,10 @@ namespace core {
3333
3434using namespace eprosima ::ddspipe::core::types;
3535
36+ // ! Maximum time (ms) to wait for the destination reply reader to be matched before writing a reply.
37+ // ! Only reached during the discovery race window; the wait returns immediately once matched.
38+ static constexpr unsigned int REPLY_MATCH_MAX_WAIT_MS = 1000 ;
39+
3640RpcBridge::RpcBridge (
3741 const RpcTopic& topic,
3842 const std::shared_ptr<ParticipantsDatabase>& participants_database,
@@ -405,6 +409,21 @@ void RpcBridge::transmit_(
405409 rpc_data.write_params .set_level ();
406410 rpc_data.write_params .get_reference ().related_sample_identity (registry_entry.second );
407411
412+ // Reply topics use VOLATILE durability: a reply written before the destination
413+ // client's reply reader has been matched on this writer's side is silently
414+ // dropped. As ROS 2 service clients never retry a request, that would hang the
415+ // client forever. The client's own \c wait_for_service only guarantees the match
416+ // from the reader side, so wait (bounded) for the writer side to match as well
417+ // before writing, closing the discovery race.
418+ if (!reply_writers_[registry_entry.first ]->wait_reader_matched (
419+ registry_entry.second .writer_guid ().guidPrefix , REPLY_MATCH_MAX_WAIT_MS ))
420+ {
421+ EPROSIMA_LOG_WARNING (DDSPIPE_RPCBRIDGE ,
422+ " RpcBridge for service " << rpc_topic_ << " writing reply towards participant "
423+ << registry_entry.second .writer_guid ().guidPrefix <<
424+ " whose reply reader is not matched yet; reply may be lost." );
425+ }
426+
408427 ret = reply_writers_[registry_entry.first ]->write (*data);
409428
410429 if (ret != utils::ReturnCode::RETCODE_OK )
0 commit comments