diff --git a/include/libcyphal/application/registry/register.hpp b/include/libcyphal/application/registry/register.hpp index 1c594a7a1..e92009f63 100644 --- a/include/libcyphal/application/registry/register.hpp +++ b/include/libcyphal/application/registry/register.hpp @@ -51,7 +51,7 @@ enum class SetError : std::uint8_t /// Defines interface for a register. /// -class IRegister : public cavl::Node +class IRegister : public common::cavl::Node { // 1AD1885B-954B-48CF-BAC4-FA0A251D3FC0 // clang-format off diff --git a/include/libcyphal/application/registry/registry_impl.hpp b/include/libcyphal/application/registry/registry_impl.hpp index 21d274fa7..895bd0970 100644 --- a/include/libcyphal/application/registry/registry_impl.hpp +++ b/include/libcyphal/application/registry/registry_impl.hpp @@ -159,8 +159,8 @@ class Registry final : public IIntrospectableRegistry [key = IRegister::Key{name}](const IRegister& other) { return other.compareBy(key); }); } - cetl::pmr::memory_resource& memory_; - cavl::Tree registers_tree_; + cetl::pmr::memory_resource& memory_; + common::cavl::Tree registers_tree_; }; // Registry diff --git a/include/libcyphal/common/cavl/cavl.hpp b/include/libcyphal/common/cavl/cavl.hpp index bcb4c47ee..0c55774d1 100644 --- a/include/libcyphal/common/cavl/cavl.hpp +++ b/include/libcyphal/common/cavl/cavl.hpp @@ -46,6 +46,10 @@ // NOLINTBEGIN(cppcoreguidelines-pro-bounds-constant-array-index) +namespace libcyphal +{ +namespace common +{ namespace cavl { template @@ -788,7 +792,7 @@ class Tree final // NOSONAR cpp:S3624 { public: /// Helper alias of the compatible node type. - using NodeType = ::cavl::Node; + using NodeType = Node; using DerivedType = Derived; Tree() = default; @@ -998,5 +1002,7 @@ class Tree final // NOSONAR cpp:S3624 }; } // namespace cavl +} // namespace common +} // namespace libcyphal // NOLINTEND(cppcoreguidelines-pro-bounds-constant-array-index) diff --git a/include/libcyphal/platform/single_threaded_executor.hpp b/include/libcyphal/platform/single_threaded_executor.hpp index 398ea03aa..3ad1d65b6 100644 --- a/include/libcyphal/platform/single_threaded_executor.hpp +++ b/include/libcyphal/platform/single_threaded_executor.hpp @@ -111,7 +111,7 @@ class SingleThreadedExecutor : public IExecutor } protected: - class CallbackNode : public cavl::Node, public Callback::Interface + class CallbackNode : public libcyphal::common::cavl::Node, public Callback::Interface { public: CallbackNode(SingleThreadedExecutor& executor, Callback::Function&& function) @@ -272,7 +272,7 @@ class SingleThreadedExecutor : public IExecutor // MARK: - Data members: /// Holds AVL tree of registered callback node, sorted by the next execution time. - cavl::Tree callback_nodes_; + common::cavl::Tree callback_nodes_; }; // SingleThreadedExecutor diff --git a/include/libcyphal/presentation/client_impl.hpp b/include/libcyphal/presentation/client_impl.hpp index fd6aaa16c..b2f557852 100644 --- a/include/libcyphal/presentation/client_impl.hpp +++ b/include/libcyphal/presentation/client_impl.hpp @@ -34,7 +34,7 @@ namespace presentation namespace detail { -class SharedClient : public cavl::Node, public SharedObject +class SharedClient : public common::cavl::Node, public SharedObject { public: using Node::remove; @@ -388,9 +388,9 @@ class SharedClient : public cavl::Node, public SharedObject const UniquePtr svc_request_tx_session_; const UniquePtr svc_response_rx_session_; const transport::ResponseRxParams response_rx_params_; - cavl::Tree cb_nodes_by_transfer_id_; + common::cavl::Tree cb_nodes_by_transfer_id_; TimePoint nearest_deadline_; - cavl::Tree timeout_nodes_by_deadline_; + common::cavl::Tree timeout_nodes_by_deadline_; IExecutor::Callback::Any nearest_deadline_callback_; }; // SharedClient diff --git a/include/libcyphal/presentation/presentation.hpp b/include/libcyphal/presentation/presentation.hpp index 905d9bd08..464d84cd0 100644 --- a/include/libcyphal/presentation/presentation.hpp +++ b/include/libcyphal/presentation/presentation.hpp @@ -727,14 +727,14 @@ class Presentation final : private detail::IPresentationDelegate // MARK: Data members: - cetl::pmr::memory_resource& memory_; - IExecutor& executor_; - transport::ITransport& transport_; - cavl::Tree shared_client_nodes_; - cavl::Tree publisher_impl_nodes_; - cavl::Tree subscriber_impl_nodes_; - detail::UnRefNode unreferenced_nodes_; - IExecutor::Callback::Any unref_nodes_deleter_callback_; + cetl::pmr::memory_resource& memory_; + IExecutor& executor_; + transport::ITransport& transport_; + common::cavl::Tree shared_client_nodes_; + common::cavl::Tree publisher_impl_nodes_; + common::cavl::Tree subscriber_impl_nodes_; + detail::UnRefNode unreferenced_nodes_; + IExecutor::Callback::Any unref_nodes_deleter_callback_; }; // Presentation diff --git a/include/libcyphal/presentation/publisher_impl.hpp b/include/libcyphal/presentation/publisher_impl.hpp index 31061feaf..953338bfd 100644 --- a/include/libcyphal/presentation/publisher_impl.hpp +++ b/include/libcyphal/presentation/publisher_impl.hpp @@ -32,7 +32,7 @@ namespace presentation namespace detail { -class PublisherImpl final : public cavl::Node, public SharedObject +class PublisherImpl final : public common::cavl::Node, public SharedObject { public: using Node::remove; diff --git a/include/libcyphal/presentation/subscriber_impl.hpp b/include/libcyphal/presentation/subscriber_impl.hpp index a3b9eb866..6b6fe5dd1 100644 --- a/include/libcyphal/presentation/subscriber_impl.hpp +++ b/include/libcyphal/presentation/subscriber_impl.hpp @@ -33,7 +33,7 @@ namespace presentation namespace detail { -class SubscriberImpl final : public cavl::Node, public SharedObject +class SubscriberImpl final : public common::cavl::Node, public SharedObject { public: using Node::remove; @@ -304,7 +304,7 @@ class SubscriberImpl final : public cavl::Node, public SharedObj ITimeProvider& time_provider_; const UniquePtr msg_rx_session_; const transport::PortId subject_id_; - cavl::Tree callback_nodes_; + common::cavl::Tree callback_nodes_; CallbackNode* next_cb_node_; }; // SubscriberImpl diff --git a/include/libcyphal/transport/can/can_transport_impl.hpp b/include/libcyphal/transport/can/can_transport_impl.hpp index 85963cdaf..c61bf9016 100644 --- a/include/libcyphal/transport/can/can_transport_impl.hpp +++ b/include/libcyphal/transport/can/can_transport_impl.hpp @@ -324,6 +324,7 @@ class TransportImpl final : private TransportDelegate, public ICanTransport return MemoryError{}; } + const auto now_us = std::chrono::duration_cast(executor_.now().time_since_epoch()); const auto deadline_us = std::chrono::duration_cast(deadline.time_since_epoch()); for (Media& media : media_array_) @@ -335,7 +336,8 @@ class TransportImpl final : private TransportDelegate, public ICanTransport &canardInstance(), static_cast(deadline_us.count()), &metadata, - {payload.size(), payload.data()}); // NOSONAR cpp:S5356 + {payload.size(), payload.data()}, // NOSONAR cpp:S5356 + static_cast(now_us.count())); cetl::optional failure = tryHandleTransientCanardResult(media, result); @@ -572,77 +574,84 @@ class TransportImpl final : private TransportDelegate, public ICanTransport } } - /// @brief Tries to push next frame from TX queue to media. - /// - void pushNextFrameToMedia(Media& media) + std::int8_t handleMediaTxFrame(Media& media, const CanardMicrosecond deadline, CanardMutableFrame& frame) { - TimePoint tx_deadline; - while (CanardTxQueueItem* const tx_item = peekFirstValidTxItem(media.canard_tx_queue(), tx_deadline)) + // + // Move the payload from the frame to the media payload - `media.push` might take ownership of it. + // No Sonar `cpp:S5356` and `cpp:S5357` b/c we integrate here with C libcanard API. + // + MediaPayload payload{frame.payload.size, + static_cast(frame.payload.data), // NOSONAR cpp:S5356 cpp:S5357 + frame.payload.allocated_size, + &media.interface().getTxMemoryResource()}; + frame.payload = {0, nullptr, 0}; + + auto push_result = media.interface().push(TimePoint{std::chrono::microseconds{deadline}}, // + frame.extended_can_id, + payload); + + if (const auto* const push = cetl::get_if(&push_result)) { - // Move the payload from the frame to the media payload - `media.push` might take ownership of it. - // No Sonar `cpp:S5356` and `cpp:S5357` b/c we integrate here with C libcanard API. - // - auto& frame_payload = tx_item->frame.payload; - MediaPayload payload{frame_payload.size, - static_cast(frame_payload.data), // NOSONAR cpp:S5356 cpp:S5357 - frame_payload.allocated_size, - &media.interface().getTxMemoryResource()}; - frame_payload = {0, nullptr, 0}; - - auto push_result = media.interface().push(tx_deadline, tx_item->frame.extended_can_id, payload); - - // In case of media push error, we are going to drop this problematic frame - // (b/c it looks like media can't handle this frame), - // but we will continue to process with another transfer frame. - // Note that media not being ready/able to push a frame just yet (aka temporary) - // is not reported as an error (see `is_pushed` below). - // - auto* const push_failure = cetl::get_if(&push_result); - if (nullptr == push_failure) + if (!push->is_accepted) { - const auto push = cetl::get(push_result); - if (push.is_accepted) - { - popAndFreeCanardTxQueueItem(media.canard_tx_queue(), - canardInstance(), - tx_item, - false /* single frame */); - } - else - { - // Media has not accepted the frame, so we need return original payload back to the item, - // so that in the future potential retry could try to push it again. - const auto org_payload = payload.release(); - frame_payload.size = std::get<0>(org_payload); - frame_payload.data = std::get<1>(org_payload); - frame_payload.allocated_size = std::get<2>(org_payload); - } - - // If needed schedule (recursively!) next frame to push. - // Already existing callback will be called by executor when media TX is ready to push more. - // - if (!media.tx_callback()) - { - media.tx_callback() = media.interface().registerPushCallback([this, &media](const auto&) { - // - pushNextFrameToMedia(media); - }); - } - return; + // Media has not accepted the frame, so we need return original payload back to the item, + // so that in the future potential retry could try to push it again. + // No Sonar `cpp:S5356` b/c we need to pass payload as a raw data to the libcanard. + const auto org_payload = payload.release(); + frame.payload.size = org_payload.size; + frame.payload.data = org_payload.data; // NOSONAR cpp:S5356 + frame.payload.allocated_size = org_payload.allocated_size; } - // Release whole problematic transfer from the TX queue, - // so that other transfers in TX queue have their chance. - // Otherwise, we would be stuck in an execution loop trying to send the same frame. - popAndFreeCanardTxQueueItem(media.canard_tx_queue(), canardInstance(), tx_item, true /* whole transfer */); + // If needed schedule (recursively!) next frame to push. + // Already existing callback will be called by executor when media TX is ready to push more. + // + if (!media.tx_callback()) + { + media.tx_callback() = media.interface().registerPushCallback([this, &media](const auto&) { + // + pushNextFrameToMedia(media); + }); + } + return push->is_accepted ? 1 : 0; + } - using Report = TransientErrorReport::MediaPush; - tryHandleTransientMediaFailure(media, std::move(*push_failure)); + using Report = TransientErrorReport::MediaPush; + tryHandleTransientMediaFailure(media, cetl::get(std::move(push_result))); + return -1; + } - } // for a valid tx item + /// @brief Tries to push next frame from TX queue to media. + /// + void pushNextFrameToMedia(Media& media) + { + auto frame_handler = [this, &media](const CanardMicrosecond deadline, + CanardMutableFrame& frame) -> std::int8_t { + // + return handleMediaTxFrame(media, deadline, frame); + }; - // There is nothing to send anymore, so we are done with this media TX - no more callbacks for now. - media.tx_callback().reset(); + // In case of a media failure we gonna try to push another frame from the next transfer in the queue, so + // that at least (and at most) one new frame will be succesfully attempted to be pushed in the end. + // Everytime we poll the queue, its size surely decrements (when `result != 0`), + // so there is no risk of infinite loop here. + // + std::int8_t result = -1; + while (result < 0) + { + // No Sonar `cpp:S5356` & `cpp:S5356` b/c we integrate with Canard C api. + result = ::canardTxPoll( // + &media.canard_tx_queue(), + &canardInstance(), + static_cast(executor_.now().time_since_epoch().count()), + &frame_handler, // NOSONAR cpp:S5356 + [](auto* const user_reference, const auto deadline, auto* frame) { + // + auto* const frame_handler_ptr = + static_cast(user_reference); // NOSONAR cpp:S5356, cpp:S5357 + return (*frame_handler_ptr)(deadline, *frame); + }); + } } /// @brief Tries to peek the first TX item from the media TX queue which is not expired. diff --git a/include/libcyphal/transport/media_payload.hpp b/include/libcyphal/transport/media_payload.hpp index e02619053..8dea6ba1f 100644 --- a/include/libcyphal/transport/media_payload.hpp +++ b/include/libcyphal/transport/media_payload.hpp @@ -10,7 +10,6 @@ #include #include -#include #include namespace libcyphal @@ -26,12 +25,42 @@ namespace transport class MediaPayload final { public: + /// Structure with the payload size, pointer to the payload data, and the allocated size. + /// + /// NB! This structure (in contrast to the parent `MediaPayload` type) is intended for raw (unmanaged) and + /// explicit transfer of payload ownership out of the `MediaPayload` instance (see `release` method). + /// It's the caller's responsibility to deallocate the buffer with the correct memory resource, + /// or move it somewhere else with the same guarantee (like f.e. back to a lizard TX queue item). + /// If you just need to access the payload data (without owning it), use `getSpan` method instead. + /// + struct Ownership + { + /// Size of the payload data in bytes. + /// + /// Could be less or equal to the allocated size. + /// `0` when the payload is moved. + /// + std::size_t size; + + /// Pointer to the payload buffer. + /// + /// `nullptr` when the payload is moved. + /// + cetl::byte* data; + + /// Size of the allocated buffer. + /// + /// Could be greater or equal to the payload size. + /// `0` when the payload is moved. + /// + std::size_t allocated_size; + + }; // Ownership + /// Constructs a new empty payload. /// MediaPayload() - : size_{0U} - , data_{nullptr} - , allocated_size_{0U} + : ownership_{0U, nullptr, 0U} , mr_{nullptr} { } @@ -47,14 +76,12 @@ class MediaPayload final cetl::byte* const data, const std::size_t allocated_size, cetl::pmr::memory_resource* const mr) - : size_{size} - , data_{data} - , allocated_size_{allocated_size} + : ownership_{size, data, allocated_size} , mr_{mr} { - CETL_DEBUG_ASSERT(size_ <= allocated_size_, ""); - CETL_DEBUG_ASSERT((data_ == nullptr) || (mr_ != nullptr), ""); - CETL_DEBUG_ASSERT((data_ != nullptr) || ((size_ == 0) && (allocated_size_ == 0)), ""); + CETL_DEBUG_ASSERT(size <= allocated_size, ""); + CETL_DEBUG_ASSERT((data == nullptr) || (mr_ != nullptr), ""); + CETL_DEBUG_ASSERT((data != nullptr) || ((size == 0) && (allocated_size == 0)), ""); } /// Moves another payload into this new payload instance. @@ -62,14 +89,13 @@ class MediaPayload final /// @param other The other payload to move into. /// MediaPayload(MediaPayload&& other) noexcept - : size_{std::exchange(other.size_, 0U)} - , data_{std::exchange(other.data_, nullptr)} - , allocated_size_{std::exchange(other.allocated_size_, 0U)} + : ownership_{std::exchange(other.ownership_, {0U, nullptr, 0U})} , mr_{std::exchange(other.mr_, nullptr)} { - CETL_DEBUG_ASSERT(size_ <= allocated_size_, ""); - CETL_DEBUG_ASSERT((data_ == nullptr) || (mr_ != nullptr), ""); - CETL_DEBUG_ASSERT((data_ != nullptr) || ((size_ == 0) && (allocated_size_ == 0)), ""); + CETL_DEBUG_ASSERT(ownership_.size <= ownership_.allocated_size, ""); + CETL_DEBUG_ASSERT((ownership_.data == nullptr) || (mr_ != nullptr), ""); + CETL_DEBUG_ASSERT((ownership_.data != nullptr) || ((ownership_.size == 0) && (ownership_.allocated_size == 0)), + ""); } /// @brief Assigns another payload by moving it into this one. @@ -80,10 +106,8 @@ class MediaPayload final { reset(); - size_ = std::exchange(other.size_, 0U); - data_ = std::exchange(other.data_, nullptr); - allocated_size_ = std::exchange(other.allocated_size_, 0U); - mr_ = std::exchange(other.mr_, nullptr); + ownership_ = std::exchange(other.ownership_, {0U, nullptr, 0U}); + mr_ = std::exchange(other.mr_, nullptr); return *this; } @@ -103,7 +127,7 @@ class MediaPayload final /// cetl::span getSpan() const noexcept { - return {data_, size_}; + return {ownership_.data, ownership_.size}; } /// Gets size (in bytes) of allocated payload buffer. @@ -112,24 +136,23 @@ class MediaPayload final /// std::size_t getAllocatedSize() const noexcept { - return allocated_size_; + return ownership_.allocated_size; } /// Releases ownership of the payload by returning its data pointer and sizes. /// /// In use to return the payload to the lizard C API when media is not ready yet to accept the payload. /// After this call, corresponding internal fields will be nullified. + /// If you just need to access the payload data (without owning it), use `getSpan` method instead. /// /// @return Tuple with the payload size, pointer to the payload data, and the allocated size. /// It's the caller's responsibility to deallocate the buffer with the correct memory resource, /// or move it somewhere else with the same guarantee (like f.e. back to a lizard TX queue item). /// - std::tuple release() noexcept + Ownership release() noexcept { mr_ = nullptr; - return std::make_tuple(std::exchange(size_, 0U), - std::exchange(data_, nullptr), - std::exchange(allocated_size_, 0U)); + return std::exchange(ownership_, {0U, nullptr, 0U}); } /// Resets the payload by de-allocating its data buffer. @@ -138,40 +161,20 @@ class MediaPayload final /// void reset() noexcept { - if (data_ != nullptr) + if (ownership_.data != nullptr) { CETL_DEBUG_ASSERT(mr_ != nullptr, "Memory resource should not be null."); // No Sonar `cpp:S5356` b/c we integrate here PMR. - mr_->deallocate(data_, allocated_size_); // NOSONAR cpp:S5356 + mr_->deallocate(ownership_.data, ownership_.allocated_size); // NOSONAR cpp:S5356 - mr_ = nullptr; - data_ = nullptr; - size_ = 0; - allocated_size_ = 0; + mr_ = nullptr; + ownership_ = {0U, nullptr, 0U}; } } private: - /// Size of the payload data in bytes. - /// - /// Could be less or equal to the allocated size. - /// `0` when the payload is moved. - /// - std::size_t size_; - - /// Pointer to the payload buffer. - /// - /// `nullptr` when the payload is moved. - /// - cetl::byte* data_; - - /// Size of the allocated buffer. - /// - /// Could be greater or equal to the payload size. - /// `0` when the payload is moved. - /// - std::size_t allocated_size_; + Ownership ownership_; /// Holds pointer to the PMR which was used to allocate the payload buffer. Will be used to deallocate it. /// diff --git a/include/libcyphal/transport/udp/session_tree.hpp b/include/libcyphal/transport/udp/session_tree.hpp index 76fdf3a23..c2fd77870 100644 --- a/include/libcyphal/transport/udp/session_tree.hpp +++ b/include/libcyphal/transport/udp/session_tree.hpp @@ -140,7 +140,7 @@ class SessionTree final // MARK: Data members: - cavl::Tree nodes_; + common::cavl::Tree nodes_; libcyphal::detail::PmrAllocator allocator_; }; // SessionTree @@ -150,10 +150,10 @@ class SessionTree final struct RxSessionTreeNode { template - class Base : public cavl::Node + class Base : public common::cavl::Node { public: - using cavl::Node::getChildNode; + using common::cavl::Node::getChildNode; using ReferenceWrapper = std::reference_wrapper; explicit Base(const PortId port_id) diff --git a/test/unittest/common/cavl/test_cavl.cpp b/test/unittest/common/cavl/test_cavl.cpp index ec9aa0655..198aab8c7 100644 --- a/test/unittest/common/cavl/test_cavl.cpp +++ b/test/unittest/common/cavl/test_cavl.cpp @@ -34,6 +34,8 @@ # endif #endif +using namespace libcyphal::common; // NOLINT This our main concern here in the unit tests. + namespace { /// These aliases are introduced to keep things nicely aligned in test cases. @@ -49,7 +51,7 @@ class My : public cavl::Node : value(v) { } - using Self = cavl::Node; + using Self = Node; using Self::isLinked; using Self::isRoot; using Self::getChildNode; @@ -991,7 +993,7 @@ TEST(TestCavl, manualMy) class V : public cavl::Node { public: - using Self = cavl::Node; + using Self = Node; using Self::isLinked; using Self::isRoot; using Self::getChildNode; diff --git a/test/unittest/transport/can/test_can_msg_tx_session.cpp b/test/unittest/transport/can/test_can_msg_tx_session.cpp index 2bc55a1ca..fb176dfd4 100644 --- a/test/unittest/transport/can/test_can_msg_tx_session.cpp +++ b/test/unittest/transport/can/test_can_msg_tx_session.cpp @@ -218,12 +218,12 @@ TEST_F(TestCanMsgTxSession, send_empty_expired_payload) scheduler_.scheduleAt(1s, [&](const auto&) { // - // Emulate that media became ready on the very edge of the default 1s timeout (exactly at the deadline). + // Emulate that media became ready on the very edge of the default 1s timeout (+1us after the deadline). EXPECT_CALL(media_mock_, push(_, _, _)) // .WillOnce(Return(IMedia::PushResult::Success{false /* is_accepted */})); EXPECT_CALL(media_mock_, registerPushCallback(_)) // .WillOnce(Invoke([&](auto function) { // - return scheduler_.registerAndScheduleNamedCallback("", now() + timeout, std::move(function)); + return scheduler_.registerAndScheduleNamedCallback("", now() + timeout + 1us, std::move(function)); })); metadata.deadline = now() + timeout; @@ -248,23 +248,23 @@ TEST_F(TestCanMsgTxSession, send_7bytes_payload_with_500ms_timeout) scheduler_.scheduleAt(1s, [&](const auto&) { // - // Emulate that socket became ready on the very edge of the 500ms timeout (just 1us before the deadline). + // Emulate that socket became ready on the very edge of the 500ms timeout (exactly at the deadline). EXPECT_CALL(media_mock_, push(_, _, _)) // .WillOnce(Return(IMedia::PushResult::Success{false /* is_accepted */})); EXPECT_CALL(media_mock_, registerPushCallback(_)) // .WillOnce(Invoke([&](auto function) { // - return scheduler_.registerAndScheduleNamedCallback("", now() + timeout - 1us, std::move(function)); + return scheduler_.registerAndScheduleNamedCallback("", now() + timeout, std::move(function)); })); metadata.deadline = now() + timeout; auto failure = session->send(metadata, makeSpansFrom(payload)); EXPECT_THAT(failure, Eq(cetl::nullopt)); }); - scheduler_.scheduleAt(1s + timeout - 1us, [&](const auto&) { + scheduler_.scheduleAt(1s + timeout, [&](const auto&) { // EXPECT_CALL(media_mock_, push(_, _, _)) // .WillOnce([&](auto, auto can_id, auto& pld) { - EXPECT_THAT(now(), metadata.deadline - 1us); + EXPECT_THAT(now(), metadata.deadline); EXPECT_THAT(can_id, SubjectOfCanIdEq(17)); EXPECT_THAT(can_id, AllOf(PriorityOfCanIdEq(metadata.base.priority), IsMessageCanId())); diff --git a/test/unittest/transport/test_media_payload.cpp b/test/unittest/transport/test_media_payload.cpp index 8f7a8de16..bde1b5de1 100644 --- a/test/unittest/transport/test_media_payload.cpp +++ b/test/unittest/transport/test_media_payload.cpp @@ -52,10 +52,10 @@ TEST_F(TestMediaPayload, default_ctor) EXPECT_THAT(payload.getAllocatedSize(), 0); // It's fine to attempt to reset or release an empty payload. - const auto fields = payload.release(); - EXPECT_THAT(std::get<0>(fields), 0); - EXPECT_THAT(std::get<1>(fields), nullptr); - EXPECT_THAT(std::get<2>(fields), 0); + const auto ownership = payload.release(); + EXPECT_THAT(ownership.size, 0); + EXPECT_THAT(ownership.data, nullptr); + EXPECT_THAT(ownership.allocated_size, 0); payload.reset(); } @@ -111,16 +111,16 @@ TEST_F(TestMediaPayload, release) MediaPayload payload{payload_size, payload_data, payload_allocated_size, &mr_}; - auto fields = payload.release(); - EXPECT_THAT(std::get<0>(fields), payload_size); - EXPECT_THAT(std::get<1>(fields), payload_data); - EXPECT_THAT(std::get<2>(fields), payload_allocated_size); - mr_.deallocate(std::get<1>(fields), std::get<2>(fields)); + auto ownership = payload.release(); + EXPECT_THAT(ownership.size, payload_size); + EXPECT_THAT(ownership.data, payload_data); + EXPECT_THAT(ownership.allocated_size, payload_allocated_size); + mr_.deallocate(ownership.data, ownership.allocated_size); - fields = payload.release(); - EXPECT_THAT(std::get<0>(fields), 0); - EXPECT_THAT(std::get<1>(fields), nullptr); - EXPECT_THAT(std::get<2>(fields), 0); + ownership = payload.release(); + EXPECT_THAT(ownership.size, 0); + EXPECT_THAT(ownership.data, nullptr); + EXPECT_THAT(ownership.allocated_size, 0); } TEST_F(TestMediaPayload, reset)