Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
98 commits
Select commit Hold shift + click to select a range
0900065
Added new example_10
serges147 Aug 26, 2024
b405bf5
Implemented raw subscriber #verification #docs #sonar
serges147 Aug 26, 2024
148ac93
latest cavl #verification #docs #sonar
serges147 Aug 26, 2024
6f9b1f8
better coverage of raw subscriber #verification #docs #sonar
serges147 Aug 26, 2024
00835c3
a bit reduce code duplication around subscriber impl creation #verifi…
serges147 Aug 26, 2024
bf9812f
more docs about extent bytes #verification #docs #sonar
serges147 Aug 26, 2024
c90cc9f
implemented rpc server #verification #docs #sonar
serges147 Aug 28, 2024
230dd3e
PR review fixes
serges147 Aug 28, 2024
bb34f63
Merge remote-tracking branch 'origin/sshirokov/examples' into sshirok…
serges147 Aug 28, 2024
eb3b39e
example todo #verification #docs #sonar
serges147 Aug 28, 2024
4be2aa5
Merge remote-tracking branch 'origin/main' into sshirokov/rpc_server
serges147 Aug 29, 2024
df19cab
example todo #verification #docs #sonar
serges147 Aug 29, 2024
f1a00cb
rename examples
serges147 Aug 29, 2024
01040e8
Implemented all `makeServer` overloads #verification #docs #sonar
serges147 Aug 30, 2024
b6e18a6
Sonar fixes; call duration stats #verification #docs #sonar
serges147 Aug 30, 2024
d2f2d99
Addressed some todos #verification #docs #sonar
serges147 Aug 30, 2024
3a285f0
added activity icons #verification #docs #sonar
serges147 Aug 30, 2024
813ffee
more coverage #verification #docs #sonar
serges147 Aug 30, 2024
c025b08
first draft of RPC client #verification #docs #sonar
serges147 Aug 31, 2024
e9377a9
Added `makeClient` overloads #verification #docs #sonar
serges147 Sep 1, 2024
71b9151
Added get/set priority methods #verification #docs #sonar
serges147 Sep 1, 2024
d8e690e
tranport matchers; sonar fixes #verification #docs #sonar
serges147 Sep 1, 2024
cb8f5c3
msg transport matchers #verification #docs #sonar
serges147 Sep 1, 2024
31fe12f
more client coverage #verification #docs #sonar
serges147 Sep 1, 2024
c470cbc
first draft of response promise
serges147 Sep 2, 2024
1a08445
add promise `Result`, `Expired` and `Waiting` options
serges147 Sep 2, 2024
40cacb5
implemented client `request` methods #verification #docs #sonar
serges147 Sep 4, 2024
8e799a9
implemented promise callbacks methods #verification #docs #sonar
serges147 Sep 4, 2024
886d7b2
sonar fixes #verification #docs #sonar
serges147 Sep 4, 2024
59e95f4
less boilerplate code at client tests #verification #docs #sonar
serges147 Sep 5, 2024
7925086
more client coverage for failures #verification #docs #sonar
serges147 Sep 5, 2024
3dfcb13
implemented response promise deadlines #verification #docs #sonar
serges147 Sep 5, 2024
4ab7503
better coverage of response promise deadlines #verification #docs #sonar
serges147 Sep 5, 2024
0204096
minor PR review fixes
serges147 Sep 5, 2024
6742854
Merge branch 'sshirokov/rpc_server' into sshirokov/rpc_client
serges147 Sep 5, 2024
551772b
minor sonar fixes
serges147 Sep 5, 2024
4a9e921
Merge branch 'main' into sshirokov/rpc_client
serges147 Sep 5, 2024
c35f767
Fixed build on mac ("linux" word should be part of a linux specific e…
serges147 Sep 5, 2024
d747e32
#verification #docs #sonar
serges147 Sep 5, 2024
214d7bb
transfer id allocator WIP
serges147 Sep 6, 2024
a09d6c7
1. Reworked presentation level PMR allocations
serges147 Sep 6, 2024
4b4f04e
Implemented `SmallRangeTransferIdGenerator`
serges147 Sep 6, 2024
c209e8d
minor sonar fixes
serges147 Sep 6, 2024
b539cb8
better coverage
serges147 Sep 8, 2024
4fe7a3d
better coverage
serges147 Sep 8, 2024
d607297
better coverage for different modulo
serges147 Sep 8, 2024
eb8f11c
better coverage for unhappy scenarios
serges147 Sep 9, 2024
f51d401
better coverage for unhappy scenarios
serges147 Sep 9, 2024
12d6c48
better coverage for unhappy scenarios
serges147 Sep 9, 2024
372bfc4
better coverage for unhappy scenarios
serges147 Sep 9, 2024
a4dca11
better coverage for unhappy scenarios
serges147 Sep 9, 2024
a973018
build fixes
serges147 Sep 10, 2024
1ad851a
fix build
serges147 Sep 10, 2024
7f057f9
more docs
serges147 Sep 10, 2024
0a69d2b
PR review fixes
serges147 Sep 11, 2024
0d708aa
minor fix
serges147 Sep 11, 2024
765d99e
move promise-related errors next to the promise itself
serges147 Sep 11, 2024
cfa7a1a
added can example
serges147 Sep 12, 2024
3ec3ca8
Add CAN transport unit test to cover multiple, out of order service r…
serges147 Sep 13, 2024
34d16cb
fix build
serges147 Sep 13, 2024
82177b6
fix build
serges147 Sep 13, 2024
b9377f2
PR review fixes:
serges147 Sep 16, 2024
1b94150
Merge branch 'sshirokov/rpc_client' into sshirokov/rpc_can_example
serges147 Sep 16, 2024
0a99f42
Merge branch 'main' into sshirokov/rpc_can_example
serges147 Sep 16, 2024
9465657
address "Switch to `transport::PayloadFragments`" todo
serges147 Sep 16, 2024
0284932
Address some todos:
serges147 Sep 16, 2024
5e9d6bc
#verification #docs #sonar
serges147 Sep 16, 2024
69a94a1
apply canard fix for issue 228
serges147 Sep 24, 2024
6f2b997
Merge branch 'main' into sshirokov/canard_issue_228
serges147 Oct 7, 2024
9f9fda9
latest libcanard
serges147 Oct 7, 2024
bc5c75c
Merge branch 'main' into issue/registry
serges147 Oct 8, 2024
5176f62
First draft of Registry (#388)
serges147 Oct 14, 2024
7cf4ca8
Optional Registry Provider at Node (#389)
serges147 Oct 17, 2024
52b9e2a
Use `cetl::string_view` (#390)
serges147 Oct 29, 2024
770cb53
Merge branch 'main' into issue/registry
serges147 Oct 30, 2024
0793514
Simplified registry implementation (#392)
serges147 Oct 31, 2024
38bd841
Defined `platform::storage::IKeyValue` interface (#393)
serges147 Nov 5, 2024
af6b092
Set default PMR, so that default behavior of `std::polymorphic_alloca…
serges147 Nov 5, 2024
7a253d5
switch to `3.0.preview` branch of Nunavut
serges147 Nov 5, 2024
28757d0
Implemented subscriber message type id from its name and version #ver…
serges147 Nov 6, 2024
5fb5ee4
Drop not needed anymore `#include <cassert>` #verification #docs #sonar
serges147 Nov 6, 2024
20cc478
minor fix (of N/A todo) #verification #docs #sonar
serges147 Nov 6, 2024
4fa6a92
Added several setters at Get Info provider.
serges147 Nov 8, 2024
0e0ad04
Set default PMR (#394)
serges147 Nov 8, 2024
a5da7a2
Merge branch 'issue/registry' into sshirokov/type_name
serges147 Nov 8, 2024
5193208
Merge remote-tracking branch 'origin/main' into sshirokov/type_name
serges147 Nov 8, 2024
077b80d
Eliminated `#include <cassert> // NOLINT for NUNAVUT_ASSERT`
serges147 Nov 8, 2024
daacaec
clang-tidy fixes
serges147 Nov 8, 2024
aab425c
Added setSoftwareVcsRevisionId & setSoftwareImageCrc
serges147 Nov 8, 2024
8af7280
grammar
serges147 Nov 8, 2024
1e96de4
sonar fix
serges147 Nov 8, 2024
7dc156a
latest 3.0.preview Nunavut
serges147 Nov 11, 2024
36ef981
Implemented TODO to make asynchronous destruction of presentation ent…
serges147 Nov 13, 2024
88d7c4f
Merge branch 'main' into sshirokov/async_destroy
serges147 Nov 13, 2024
2951481
clang-tidy exceptions
serges147 Nov 13, 2024
f0a2692
clang-tidy false positive
serges147 Nov 13, 2024
a2bf7ca
clang-tidy false positive
serges147 Nov 13, 2024
38757c9
clang-tidy false positive
serges147 Nov 13, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 20 additions & 4 deletions include/libcyphal/presentation/client_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ namespace detail
class SharedClient : public cavl::Node<SharedClient>, public SharedObject
{
public:
using Node::remove;
using Node::isLinked;

class TimeoutNode : public Node<TimeoutNode>
{
public:
Expand Down Expand Up @@ -229,13 +232,20 @@ class SharedClient : public cavl::Node<SharedClient>, public SharedObject
CETL_DEBUG_ASSERT(cb_nodes_by_transfer_id_.empty(), "");
CETL_DEBUG_ASSERT(timeout_nodes_by_deadline_.empty(), "");

delegate_.releaseSharedClient(this);
delegate_.markSharedObjAsUnreferenced(*this);
return true;
}

return false;
}

// MARK: SharedObject

void destroy() noexcept override
{
delegate_.forgetSharedClient(*this);
}

protected:
virtual void insertNewCallbackNode(CallbackNode& callback_node)
{
Expand Down Expand Up @@ -393,7 +403,7 @@ class SharedClient : public cavl::Node<SharedClient>, public SharedObject
/// @brief Defines a shared client implementation that uses a generic transfer ID generator.
///
template <typename TransferIdGeneratorMixin>
class ClientImpl final : public SharedClient, public TransferIdGeneratorMixin
class ClientImpl final : public SharedClient, private TransferIdGeneratorMixin
{
public:
ClientImpl(IPresentationDelegate& delegate,
Expand All @@ -410,10 +420,13 @@ class ClientImpl final : public SharedClient, public TransferIdGeneratorMixin

void destroy() noexcept override
{
Base::destroy();
destroyWithPmr(this, memory());
}

private:
using Base = SharedClient;

// MARK: SharedClient

CETL_NODISCARD cetl::optional<transport::TransferId> nextTransferId() noexcept override
Expand All @@ -440,25 +453,28 @@ class ClientImpl final : public SharedClient, public TransferIdGeneratorMixin
template <>
class ClientImpl<transport::detail::TrivialTransferIdGenerator> final
: public SharedClient,
public transport::detail::TrivialTransferIdGenerator
private transport::detail::TrivialTransferIdGenerator
{
public:
ClientImpl(IPresentationDelegate& delegate,
IExecutor& executor,
UniquePtr<transport::IRequestTxSession> svc_request_tx_session,
UniquePtr<transport::IResponseRxSession> svc_response_rx_session)
: SharedClient{delegate, executor, std::move(svc_request_tx_session), std::move(svc_response_rx_session)}
: Base{delegate, executor, std::move(svc_request_tx_session), std::move(svc_response_rx_session)}
{
}

// MARK: SharedObject

void destroy() noexcept override
{
Base::destroy();
destroyWithPmr(this, memory());
}

private:
using Base = SharedClient;

// MARK: SharedClient

CETL_NODISCARD cetl::optional<transport::TransferId> nextTransferId() noexcept override
Expand Down
100 changes: 88 additions & 12 deletions include/libcyphal/presentation/presentation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,10 @@ struct IsFixedPortIdMessageTrait<T, true>
/// Instance of this class is supposed to be created once per transport instance (or even per application).
/// Main purpose of the presentation object is to create publishers, subscribers, and RPC clients and servers.
///
class Presentation final : private detail::IPresentationDelegate
/// No Sonar cpp:S4963 'The "Rule-of-Zero" should be followed'
/// b/c we do directly handle resources here.
///
class Presentation final : private detail::IPresentationDelegate // NOSONAR cpp:S4963
{
public:
/// @brief Defines failure type of various `make...` methods of the presentation layer.
Expand All @@ -112,7 +115,30 @@ class Presentation final : private detail::IPresentationDelegate
: memory_{memory}
, executor_{executor}
, transport_{transport}
, unreferenced_nodes_{&unreferenced_nodes_, &unreferenced_nodes_}
{
unref_nodes_deleter_callback_ = executor_.registerCallback([this](const auto&) {
//
destroyUnreferencedNodes();
});
CETL_DEBUG_ASSERT(unref_nodes_deleter_callback_, "Should not fail b/c we pass proper lambda.");
}

Presentation(const Presentation&) = delete;
Presentation(Presentation&&) noexcept = delete;
Presentation& operator=(const Presentation&) = delete;
Presentation& operator=(Presentation&&) noexcept = delete;

~Presentation()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be virtual? I'm not sure TBH.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Presentation is final, so I don't think we need virtual.

{
destroyUnreferencedNodes();

CETL_DEBUG_ASSERT(shared_client_nodes_.empty(), //
"RPC clients must be destroyed before presentation.");
CETL_DEBUG_ASSERT(publisher_impl_nodes_.empty(), //
"Message publishers must be destroyed before presentation.");
CETL_DEBUG_ASSERT(subscriber_impl_nodes_.empty(), //
"Message subscribers must be destroyed before presentation.");
}

/// @brief Gets reference to the executor instance of this presentation object.
Expand Down Expand Up @@ -161,6 +187,11 @@ class Presentation final : private detail::IPresentationDelegate
auto* const publisher_impl = std::get<0>(publisher_existing);
CETL_DEBUG_ASSERT(publisher_impl != nullptr, "");

// This publisher impl node might be in the list of previously unreferenced nodes -
// the ones that are going to be deleted asynchronously (by the `destroyUnreferencedNodes`).
// If it's the case, we need to remove it from the list b/c it's going to be referenced.
publisher_impl->unlinkIfReferenced();

return Publisher<Message>{publisher_impl};
}

Expand Down Expand Up @@ -456,6 +487,8 @@ class Presentation final : private detail::IPresentationDelegate
}

private:
using Schedule = IExecutor::Callback::Schedule;

IPresentationDelegate& asDelegate() noexcept
{
return static_cast<IPresentationDelegate&>(*this);
Expand Down Expand Up @@ -517,6 +550,12 @@ class Presentation final : private detail::IPresentationDelegate

auto* const subscriber_impl = std::get<0>(subscriber_existing);
CETL_DEBUG_ASSERT(subscriber_impl != nullptr, "");

// This subscriber impl node might be in the list of previously unreferenced nodes -
// the ones that are going to be deleted asynchronously (by the `destroyUnreferencedNodes`).
// If it's the case, we need to remove it from the list b/c it's going to be referenced.
subscriber_impl->unlinkIfReferenced();

return subscriber_impl;
}

Expand Down Expand Up @@ -571,6 +610,12 @@ class Presentation final : private detail::IPresentationDelegate

auto* const shared_client = std::get<0>(shared_client_existing);
CETL_DEBUG_ASSERT(shared_client != nullptr, "");

// This client impl node might be in the list of previously unreferenced nodes -
// the ones that are going to be deleted asynchronously (by the `destroyUnreferencedNodes`).
// If it's the case, we need to remove it from the list b/c it's going to be referenced.
shared_client->unlinkIfReferenced();

return shared_client;
}

Expand Down Expand Up @@ -628,30 +673,59 @@ class Presentation final : private detail::IPresentationDelegate
}

template <typename SharedNode>
void releaseSharedNode(SharedNode* const shared_node, cavl::Tree<SharedNode>& tree) const noexcept
static void forgetSharedNode(SharedNode& shared_node) noexcept
{
CETL_DEBUG_ASSERT(shared_node != nullptr, "");
CETL_DEBUG_ASSERT(shared_node.isLinked(), "");
CETL_DEBUG_ASSERT(!shared_node.isReferenced(), "");

// TODO: make it async (deferred to "on idle" callback).
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the "TODO" this PR is about.

tree.remove(shared_node);
shared_node->destroy();
// Remove the node from its tree (if it still there),
// as well as from the list of unreferenced nodes (b/c we gonna finally destroy it).
shared_node.remove(); // from the tree
shared_node.unlinkIfReferenced(); // from the list
}

void destroyUnreferencedNodes() const noexcept
{
// In the loop, destruction of a shared object also removes it from the list of unreferenced nodes.
// So, it implicitly updates the `unreferenced_nodes_` list.
while (unreferenced_nodes_.next_node != &unreferenced_nodes_)
{
// Downcast is safe here b/c every `UnRefNode` instance is always a `SharedObject` one.
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-static-cast-downcast)
auto* const shared_obj = static_cast<detail::SharedObject*>(unreferenced_nodes_.next_node);
shared_obj->destroy();
}
}

// MARK: IPresentationDelegate

void releaseSharedClient(detail::SharedClient* const shared_client) noexcept override
void markSharedObjAsUnreferenced(detail::SharedObject& shared_obj) noexcept override
{
// We are not going to destroy the shared object immediately, but schedule it for deletion.
// This is b/c destruction of shared objects may be time-consuming (f.e. closing under the hood sockets).
// Double-linked list is used to avoid the need to traverse the tree of shared objects.
//
CETL_DEBUG_ASSERT(!shared_obj.isReferenced(), "");
shared_obj.linkAsUnreferenced(unreferenced_nodes_);
//
const auto result = unref_nodes_deleter_callback_.schedule(Schedule::Once{executor_.now()});
CETL_DEBUG_ASSERT(result, "Should not fail b/c we never reset `unref_nodes_deleter_callback_`.");
(void) result;
}

void forgetSharedClient(detail::SharedClient& shared_client) noexcept override
{
releaseSharedNode(shared_client, shared_client_nodes_);
forgetSharedNode(shared_client);
}

void releasePublisherImpl(detail::PublisherImpl* const publisher_impl) noexcept override
void forgetPublisherImpl(detail::PublisherImpl& publisher_impl) noexcept override
{
releaseSharedNode(publisher_impl, publisher_impl_nodes_);
forgetSharedNode(publisher_impl);
}

void releaseSubscriberImpl(detail::SubscriberImpl* const subscriber_impl) noexcept override
void forgetSubscriberImpl(detail::SubscriberImpl& subscriber_impl) noexcept override
{
releaseSharedNode(subscriber_impl, subscriber_impl_nodes_);
forgetSharedNode(subscriber_impl);
}

// MARK: Data members:
Expand All @@ -662,6 +736,8 @@ class Presentation final : private detail::IPresentationDelegate
cavl::Tree<detail::SharedClient> shared_client_nodes_;
cavl::Tree<detail::PublisherImpl> publisher_impl_nodes_;
cavl::Tree<detail::SubscriberImpl> subscriber_impl_nodes_;
detail::UnRefNode unreferenced_nodes_;
IExecutor::Callback::Any unref_nodes_deleter_callback_;

}; // Presentation

Expand Down
9 changes: 6 additions & 3 deletions include/libcyphal/presentation/presentation_delegate.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#ifndef LIBCYPHAL_PRESENTATION_DELEGATE_HPP_INCLUDED
#define LIBCYPHAL_PRESENTATION_DELEGATE_HPP_INCLUDED

#include "shared_object.hpp"

#include <cetl/pf17/cetlpf.hpp>

#include <type_traits>
Expand Down Expand Up @@ -84,9 +86,10 @@ class IPresentationDelegate

virtual cetl::pmr::memory_resource& memory() const noexcept = 0;

virtual void releaseSharedClient(SharedClient* shared_client) noexcept = 0;
virtual void releasePublisherImpl(PublisherImpl* publisher_impl) noexcept = 0;
virtual void releaseSubscriberImpl(SubscriberImpl* subscriber_impl) noexcept = 0;
virtual void markSharedObjAsUnreferenced(SharedObject& shared_obj) noexcept = 0;
virtual void forgetSharedClient(SharedClient& shared_client) noexcept = 0;
virtual void forgetPublisherImpl(PublisherImpl& publisher_impl) noexcept = 0;
virtual void forgetSubscriberImpl(SubscriberImpl& subscriber_impl) noexcept = 0;

protected:
IPresentationDelegate() = default;
Expand Down
10 changes: 8 additions & 2 deletions include/libcyphal/presentation/publisher_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ namespace detail
class PublisherImpl final : public cavl::Node<PublisherImpl>, public SharedObject
{
public:
using Node::remove;
using Node::isLinked;

PublisherImpl(IPresentationDelegate& delegate, UniquePtr<transport::IMessageTxSession> msg_tx_session)
: delegate_{delegate}
, msg_tx_session_{std::move(msg_tx_session)}
Expand Down Expand Up @@ -70,19 +73,22 @@ class PublisherImpl final : public cavl::Node<PublisherImpl>, public SharedObjec
{
if (SharedObject::release())
{
delegate_.releasePublisherImpl(this);
delegate_.markSharedObjAsUnreferenced(*this);
return true;
}

return false;
}

private:
// MARK: SharedObject

void destroy() noexcept override
{
delegate_.forgetPublisherImpl(*this);
destroyWithPmr(this, delegate_.memory());
}

private:
// MARK: Data members:

IPresentationDelegate& delegate_;
Expand Down
53 changes: 52 additions & 1 deletion include/libcyphal/presentation/shared_object.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,53 @@ namespace presentation
namespace detail
{

/// @brief Defines double-linked list of unreferenced nodes.
///
struct UnRefNode
{
void linkAsUnreferenced(UnRefNode& origin)
{
CETL_DEBUG_ASSERT((prev_node != nullptr) == (next_node != nullptr), "Should be both or none.");

// Already linked?
if ((nullptr == prev_node) && (nullptr == next_node))
{
// Link to the end of the list, so that the object is destroyed in the order of unreferencing.
//
next_node = &origin;
prev_node = origin.prev_node;
origin.prev_node->next_node = this;
origin.prev_node = this;
}
}

void unlinkIfReferenced()
{
CETL_DEBUG_ASSERT((prev_node != nullptr) == (next_node != nullptr), "Should be both or none.");

// Already unlinked?
if ((nullptr != prev_node) && (nullptr != next_node))
{
prev_node->next_node = next_node;
next_node->prev_node = prev_node;

next_node = nullptr;
prev_node = nullptr;
}
}

// No Lint b/c this `UnRefNode` is a simple helper struct as base of the below `SharedObject`.
// It's under `detail` namespace and not supposed to be used directly by the users of the library.
// NOLINTBEGIN(misc-non-private-member-variables-in-classes)
UnRefNode* prev_node{nullptr};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we protect these by making them private then using friend class SharedObject?

Copy link
Contributor Author

@serges147 serges147 Nov 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we probably can but I'm not sure we need - all derived classes (from SharedObject) are still "Impl" internal (and under detail namespace) entities. F.e. if you work with Publisher<Message> (which is not shared object!) then you don't see under the hood involved PublisherImpl. The same story for 2 other entities: subscriber and rpc client.

UnRefNode* next_node{nullptr};
// NOLINTEND(misc-non-private-member-variables-in-classes)

}; // UnRefNode

/// @brief Defines the base class for all classes that need to be shared (using reference count).
///
class SharedObject
class SharedObject : public UnRefNode
{
public:
SharedObject() = default;
Expand All @@ -38,6 +82,13 @@ class SharedObject
SharedObject& operator=(const SharedObject&) = delete;
SharedObject& operator=(SharedObject&&) noexcept = delete;

/// @brief Gets boolean indicating whether the object is referenced at least once.
///
bool isReferenced() const noexcept
{
return ref_count_ > 0;
}

/// @brief Increments the reference count.
///
void retain() noexcept
Expand Down
Loading
Loading