Skip to content

Commit d17f264

Browse files
authored
Fix for issue 416: Impossible to retain transfer ids (#420)
- Introduced public `ITransferIdMap` and internal `detail::ITransferIdStorage` interfaces. - Presentation publisher and rpc client entities now... - try initialize (at ctor) their transfer ids with the transfer id map `getIdFor` method. - try to store their next transfer ids into the map (using `setIdFor`). - Extra unit tests and mocks to cover new transfer id map related business logic.
1 parent 06d654e commit d17f264

17 files changed

+734
-215
lines changed

include/libcyphal/application/node/heartbeat_producer.hpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ class HeartbeatProducer final // NOSONAR cpp:S3624
169169
void publishMessage(const TimePoint approx_now)
170170
{
171171
// Publishing of heartbeats makes sense only if the local node ID is known.
172-
if (presentation_.transport().getLocalNodeId() == cetl::nullopt)
172+
if (!presentation_.transport().getLocalNodeId().has_value())
173173
{
174174
return;
175175
}

include/libcyphal/presentation/client.hpp

+4-4
Original file line numberDiff line numberDiff line change
@@ -237,8 +237,8 @@ class Client final : public detail::ClientBase
237237
// For request (and the following response) we need to allocate a transfer ID,
238238
// which will be in use to pair the request with the response.
239239
//
240-
auto& shared_client = getSharedClient();
241-
auto opt_transfer_id = shared_client.nextTransferId();
240+
auto& shared_client = getSharedClient();
241+
const auto opt_transfer_id = shared_client.nextTransferId();
242242
if (!opt_transfer_id)
243243
{
244244
return TooManyPendingRequestsError{};
@@ -325,8 +325,8 @@ class RawServiceClient final : public detail::ClientBase
325325
// 1. For request (and following response) we need to allocate a transfer ID,
326326
// which will be in use to pair the request with the response.
327327
//
328-
auto& shared_client = getSharedClient();
329-
auto opt_transfer_id = shared_client.nextTransferId();
328+
auto& shared_client = getSharedClient();
329+
const auto opt_transfer_id = shared_client.nextTransferId();
330330
if (!opt_transfer_id)
331331
{
332332
return TooManyPendingRequestsError{};

include/libcyphal/presentation/client_impl.hpp

+53-21
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
#include "libcyphal/executor.hpp"
1414
#include "libcyphal/transport/errors.hpp"
1515
#include "libcyphal/transport/svc_sessions.hpp"
16-
#include "libcyphal/transport/transfer_id_generators.hpp"
16+
#include "libcyphal/transport/transfer_id_map.hpp"
1717
#include "libcyphal/transport/types.hpp"
1818
#include "libcyphal/types.hpp"
1919

@@ -34,7 +34,9 @@ namespace presentation
3434
namespace detail
3535
{
3636

37-
class SharedClient : public common::cavl::Node<SharedClient>, public SharedObject
37+
class SharedClient : public common::cavl::Node<SharedClient>,
38+
public SharedObject,
39+
protected transport::detail::ITransferIdStorage
3840
{
3941
public:
4042
using Node::remove;
@@ -134,17 +136,24 @@ class SharedClient : public common::cavl::Node<SharedClient>, public SharedObjec
134136
, svc_request_tx_session_{std::move(svc_request_tx_session)}
135137
, svc_response_rx_session_{std::move(svc_response_rx_session)}
136138
, response_rx_params_{svc_response_rx_session_->getParams()}
139+
, next_transfer_id_{0}
137140
, nearest_deadline_{DistantFuture()}
138141
{
139142
CETL_DEBUG_ASSERT(svc_request_tx_session_ != nullptr, "");
140143
CETL_DEBUG_ASSERT(svc_response_rx_session_ != nullptr, "");
141144

145+
if (const auto* const transfer_id_map = delegate.getTransferIdMap())
146+
{
147+
const SessionSpec session_spec{response_rx_params_.service_id, response_rx_params_.server_node_id};
148+
next_transfer_id_ = transfer_id_map->getIdFor(session_spec);
149+
}
150+
142151
// Override the default (2s) timeout value of the response session.
143152
// This is done to allow multiple overlapping responses to be handled properly.
144153
// Otherwise, the responses would be rejected (as "duplicates") if their transfer IDs are in order.
145154
// Real duplicates (f.e. caused by redundant transports) won't cause any issues
146155
// b/c shared RPC client expects/accepts only one response per transfer ID,
147-
// and corresponding promise callback node will be removed after the first response.
156+
// and the corresponding promise callback node will be removed after the first response.
148157
svc_response_rx_session_->setTransferIdTimeout({});
149158

150159
svc_response_rx_session_->setOnReceiveCallback([this](const auto& arg) {
@@ -199,7 +208,7 @@ class SharedClient : public common::cavl::Node<SharedClient>, public SharedObjec
199208
{
200209
if (timeout_node.isTimeoutLinked())
201210
{
202-
// Remove previous timeout node (if any),
211+
// Remove the previous timeout node (if any),
203212
// and then reinsert the node with updated/given new deadline time.
204213
//
205214
timeout_nodes_by_deadline_.remove(&timeout_node);
@@ -240,9 +249,27 @@ class SharedClient : public common::cavl::Node<SharedClient>, public SharedObjec
240249

241250
void destroy() noexcept override
242251
{
252+
if (auto* const transfer_id_map = delegate_.getTransferIdMap())
253+
{
254+
const SessionSpec session_spec{response_rx_params_.service_id, response_rx_params_.server_node_id};
255+
transfer_id_map->setIdFor(session_spec, next_transfer_id_);
256+
}
257+
243258
delegate_.forgetSharedClient(*this);
244259
}
245260

261+
// MARK: ITransferIdStorage
262+
263+
transport::TransferId load() const noexcept override
264+
{
265+
return next_transfer_id_;
266+
}
267+
268+
void save(const transport::TransferId transfer_id) noexcept override
269+
{
270+
next_transfer_id_ = transfer_id;
271+
}
272+
246273
protected:
247274
virtual void insertNewCallbackNode(CallbackNode& callback_node)
248275
{
@@ -273,7 +300,8 @@ class SharedClient : public common::cavl::Node<SharedClient>, public SharedObjec
273300
}
274301

275302
private:
276-
using Schedule = IExecutor::Callback::Schedule;
303+
using Schedule = IExecutor::Callback::Schedule;
304+
using SessionSpec = transport::ITransferIdMap::SessionSpec;
277305

278306
static constexpr TimePoint DistantFuture()
279307
{
@@ -388,6 +416,7 @@ class SharedClient : public common::cavl::Node<SharedClient>, public SharedObjec
388416
const UniquePtr<transport::IRequestTxSession> svc_request_tx_session_;
389417
const UniquePtr<transport::IResponseRxSession> svc_response_rx_session_;
390418
const transport::ResponseRxParams response_rx_params_;
419+
transport::TransferId next_transfer_id_;
391420
common::cavl::Tree<CallbackNode> cb_nodes_by_transfer_id_;
392421
TimePoint nearest_deadline_;
393422
common::cavl::Tree<TimeoutNode> timeout_nodes_by_deadline_;
@@ -399,8 +428,8 @@ class SharedClient : public common::cavl::Node<SharedClient>, public SharedObjec
399428

400429
/// @brief Defines a shared client implementation that uses a generic transfer ID generator.
401430
///
402-
template <typename TransferIdGeneratorMixin>
403-
class ClientImpl final : public SharedClient, private TransferIdGeneratorMixin
431+
template <typename TransferIdGenerator>
432+
class ClientImpl final : public SharedClient
404433
{
405434
public:
406435
ClientImpl(IPresentationDelegate& delegate,
@@ -409,7 +438,7 @@ class ClientImpl final : public SharedClient, private TransferIdGeneratorMixin
409438
UniquePtr<transport::IResponseRxSession> svc_response_rx_session,
410439
const transport::TransferId transfer_id_modulo)
411440
: SharedClient{delegate, executor, std::move(svc_request_tx_session), std::move(svc_response_rx_session)}
412-
, TransferIdGeneratorMixin{transfer_id_modulo}
441+
, transfer_id_generator_{transfer_id_modulo, *this}
413442
{
414443
}
415444

@@ -424,40 +453,41 @@ class ClientImpl final : public SharedClient, private TransferIdGeneratorMixin
424453
private:
425454
using Base = SharedClient;
426455

427-
// MARK: SharedClient
428-
429-
CETL_NODISCARD cetl::optional<transport::TransferId> nextTransferId() noexcept override
430-
{
431-
return TransferIdGeneratorMixin::nextTransferId();
432-
}
433-
434456
void insertNewCallbackNode(CallbackNode& callback_node) override
435457
{
436458
SharedClient::insertNewCallbackNode(callback_node);
437-
TransferIdGeneratorMixin::retainTransferId(callback_node.getTransferId());
459+
transfer_id_generator_.retainTransferId(callback_node.getTransferId());
438460
}
439461

440462
void removeCallbackNode(CallbackNode& callback_node) override
441463
{
442-
TransferIdGeneratorMixin::releaseTransferId(callback_node.getTransferId());
464+
transfer_id_generator_.releaseTransferId(callback_node.getTransferId());
443465
SharedClient::removeCallbackNode(callback_node);
444466
}
445467

468+
// MARK: SharedClient
469+
470+
CETL_NODISCARD cetl::optional<transport::TransferId> nextTransferId() noexcept override
471+
{
472+
return transfer_id_generator_.nextTransferId();
473+
}
474+
475+
TransferIdGenerator transfer_id_generator_;
476+
446477
}; // ClientImpl<TransferIdGeneratorMixin>
447478

448479
/// @brief Defines a shared client specialization that uses a trivial transfer ID generator.
449480
///
450481
template <>
451-
class ClientImpl<transport::detail::TrivialTransferIdGenerator> final
452-
: public SharedClient,
453-
private transport::detail::TrivialTransferIdGenerator
482+
class ClientImpl<transport::detail::TrivialTransferIdGenerator> final : public SharedClient
454483
{
455484
public:
456485
ClientImpl(IPresentationDelegate& delegate,
457486
IExecutor& executor,
458487
UniquePtr<transport::IRequestTxSession> svc_request_tx_session,
459488
UniquePtr<transport::IResponseRxSession> svc_response_rx_session)
460489
: Base{delegate, executor, std::move(svc_request_tx_session), std::move(svc_response_rx_session)}
490+
, transfer_id_generator_{*this}
461491
{
462492
}
463493

@@ -476,9 +506,11 @@ class ClientImpl<transport::detail::TrivialTransferIdGenerator> final
476506

477507
CETL_NODISCARD cetl::optional<transport::TransferId> nextTransferId() noexcept override
478508
{
479-
return TrivialTransferIdGenerator::nextTransferId();
509+
return transfer_id_generator_.nextTransferId();
480510
}
481511

512+
transport::detail::TrivialTransferIdGenerator transfer_id_generator_;
513+
482514
}; // ClientImpl<TrivialTransferIdGenerator>
483515

484516
} // namespace detail

0 commit comments

Comments
 (0)