diff --git a/src/core/BUILD.gn b/src/core/BUILD.gn index 8f6590dff..ef0ee1eb6 100644 --- a/src/core/BUILD.gn +++ b/src/core/BUILD.gn @@ -389,6 +389,7 @@ openthread_core_files = [ "common/extension.hpp", "common/heap.cpp", "common/heap.hpp", + "common/heap_allocatable.hpp", "common/heap_data.cpp", "common/heap_data.hpp", "common/heap_string.cpp", diff --git a/src/core/Makefile.am b/src/core/Makefile.am index 4d38055a8..7c1246edb 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -429,6 +429,7 @@ HEADERS_COMMON = \ common/error.hpp \ common/extension.hpp \ common/heap.hpp \ + common/heap_allocatable.hpp \ common/heap_data.hpp \ common/heap_string.hpp \ common/instance.hpp \ diff --git a/src/core/common/heap_allocatable.hpp b/src/core/common/heap_allocatable.hpp new file mode 100644 index 000000000..66fab9a5b --- /dev/null +++ b/src/core/common/heap_allocatable.hpp @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2021, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * This file includes definitions for `Heap::Allocatable`. + */ + +#ifndef HEAP_ALLOCATABLE_HPP_ +#define HEAP_ALLOCATABLE_HPP_ + +#include "openthread-core-config.h" + +#include "common/code_utils.hpp" +#include "common/error.hpp" +#include "common/heap.hpp" +#include "common/new.hpp" + +namespace ot { +namespace Heap { + +/** + * This template class defines a `Heap::Allocatable` object. + * + * `Heap::Allocatable` provides `New()` and `Free()` methods to allocate and free instances of `Type` on heap. + * + * Users of this class should follow CRTP-style inheritance, i.e., the `Type` class itself should inherit from + * `Allocatable`. + * + * The `Type` class destructor is used when `Free()` is called. The destructor frees any heap allocated data members + * that are stored in a `Type` instance (e.g., `Heap::String`, `Heap::Data`, etc). + * + */ +template class Allocatable +{ +public: + /** + * This static method allocates a new instance of `Type` on heap and initializes it. + * + * The `Type` class MUST have a default constructor (with no arguments) which is invoked upon allocation of new + * `Type` instance. It MUST also provide an `Error Init(Args...)` method to initialize the instance. If any `Error` + * other than `kErrorNone` is returned the initialization is considered failed. + * + * @param[in] aArgs A set of arguments to initialize the allocated `Type` instance. + * + * @returns A pointer to the newly allocated instance or `nullptr` if it fails to allocate or initialize. + * + */ + template static Type *New(Args &&... aArgs) + { + void *buf = Heap::CAlloc(1, sizeof(Type)); + Type *object = nullptr; + + VerifyOrExit(buf != nullptr); + + object = new (buf) Type(); + + if (object->Init(static_cast(aArgs)...) != kErrorNone) + { + object->Free(); + object = nullptr; + } + + exit: + return object; + } + + /** + * This method frees the `Type` instance. + * + * The instance MUST be heap allocated using the `New()` method. + * + * The `Free()` method invokes the `Type` destructor before releasing the allocated heap buffer for the instance. + * This ensures that any heap allocated member variables in `Type` are freed before the `Type` instance itself is + * freed. + * + */ + void Free(void) + { + static_cast(this)->~Type(); + Heap::Free(this); + } + +protected: + Allocatable(void) = default; +}; + +} // namespace Heap +} // namespace ot + +#endif // HEAP_ALLOCATABLE_HPP_ diff --git a/src/core/net/srp_server.cpp b/src/core/net/srp_server.cpp index c8a372017..89ce08e90 100644 --- a/src/core/net/srp_server.cpp +++ b/src/core/net/srp_server.cpp @@ -1144,8 +1144,8 @@ void Server::HandleUpdate(Host &aHost, const MessageMetadata &aMetadata) service.IsSubType(), aMetadata.mRxTime); VerifyOrExit(newService != nullptr, error = kErrorNoBufs); - newService->mDescription.mUpdateTime = aMetadata.mRxTime; - newService->mIsDeleted = true; + newService->mDescription->mUpdateTime = aMetadata.mRxTime; + newService->mIsDeleted = true; } } @@ -1444,43 +1444,16 @@ const char *Server::AddressModeToString(AddressMode aMode) //--------------------------------------------------------------------------------------------------------------------- // Server::Service -Server::Service *Server::Service::New(const char * aServiceName, - Description &aDescription, - bool aIsSubType, - TimeMilli aUpdateTime) +Error Server::Service::Init(const char *aServiceName, Description &aDescription, bool aIsSubType, TimeMilli aUpdateTime) { - void * buf; - Service *service = nullptr; + mDescription = &aDescription; + mNext = nullptr; + mUpdateTime = aUpdateTime; + mIsDeleted = false; + mIsSubType = aIsSubType; + mIsCommitted = false; - buf = Heap::CAlloc(1, sizeof(Service)); - VerifyOrExit(buf != nullptr); - - service = new (buf) Service(aDescription, aIsSubType, aUpdateTime); - - if (service->mServiceName.Set(aServiceName) != kErrorNone) - { - service->Free(); - service = nullptr; - } - -exit: - return service; -} - -void Server::Service::Free(void) -{ - mServiceName.Free(); - Heap::Free(this); -} - -Server::Service::Service(Description &aDescription, bool aIsSubType, TimeMilli aUpdateTime) - : mDescription(aDescription) - , mNext(nullptr) - , mUpdateTime(aUpdateTime) - , mIsDeleted(false) - , mIsSubType(aIsSubType) - , mIsCommitted(false) -{ + return mServiceName.Set(aServiceName); } Error Server::Service::GetServiceSubTypeLabel(char *aLabel, uint8_t aMaxSize) const @@ -1518,12 +1491,12 @@ TimeMilli Server::Service::GetExpireTime(void) const OT_ASSERT(!mIsDeleted); OT_ASSERT(!GetHost().IsDeleted()); - return mUpdateTime + Time::SecToMsec(mDescription.mLease); + return mUpdateTime + Time::SecToMsec(mDescription->mLease); } TimeMilli Server::Service::GetKeyExpireTime(void) const { - return mUpdateTime + Time::SecToMsec(mDescription.mKeyLease); + return mUpdateTime + Time::SecToMsec(mDescription->mKeyLease); } bool Server::Service::MatchesFlags(Flags aFlags) const @@ -1597,43 +1570,19 @@ void Server::Service::Log(Action) const //--------------------------------------------------------------------------------------------------------------------- // Server::Service::Description -Server::Service::Description *Server::Service::Description::New(const char *aInstanceName, Host &aHost) +Error Server::Service::Description::Init(const char *aInstanceName, Host &aHost) { - void * buf; - Description *desc = nullptr; - - buf = Heap::CAlloc(1, sizeof(Description)); - VerifyOrExit(buf != nullptr); - - desc = new (buf) Description(aHost); - - if (desc->mInstanceName.Set(aInstanceName) != kErrorNone) - { - desc->Free(); - desc = nullptr; - } - -exit: - return desc; -} - -void Server::Service::Description::Free(void) -{ - mInstanceName.Free(); + mNext = nullptr; + mHost = &aHost; + mPriority = 0; + mWeight = 0; + mPort = 0; + mLease = 0; + mKeyLease = 0; + mUpdateTime = TimerMilli::GetNow().GetDistantPast(); mTxtData.Free(); - Heap::Free(this); -} -Server::Service::Description::Description(Host &aHost) - : mNext(nullptr) - , mHost(aHost) - , mPriority(0) - , mWeight(0) - , mPort(0) - , mLease(0) - , mKeyLease(0) - , mUpdateTime(TimerMilli::GetNow().GetDistantPast()) -{ + return mInstanceName.Set(aInstanceName); } void Server::Service::Description::ClearResources(void) @@ -1675,35 +1624,25 @@ exit: //--------------------------------------------------------------------------------------------------------------------- // Server::Host -Server::Host *Server::Host::New(Instance &aInstance, TimeMilli aUpdateTime) +Error Server::Host::Init(Instance &aInstance, TimeMilli aUpdateTime) { - void *buf; - Host *host = nullptr; + InstanceLocatorInit::Init(aInstance); + mNext = nullptr; + mFullName.Free(); + mAddresses.Clear(); + mKey.Clear(); + mLease = 0; + mKeyLease = 0; + mUpdateTime = aUpdateTime; + mServices.Clear(); + mServiceDescriptions.Clear(); - buf = Heap::CAlloc(1, sizeof(Host)); - VerifyOrExit(buf != nullptr); - - host = new (buf) Host(aInstance, aUpdateTime); - -exit: - return host; + return kErrorNone; } -void Server::Host::Free(void) +Server::Host::~Host(void) { FreeAllServices(); - mFullName.Free(); - Heap::Free(this); -} - -Server::Host::Host(Instance &aInstance, TimeMilli aUpdateTime) - : InstanceLocator(aInstance) - , mNext(nullptr) - , mLease(0) - , mKeyLease(0) - , mUpdateTime(aUpdateTime) -{ - mKey.Clear(); } Error Server::Host::SetFullName(const char *aFullName) @@ -1919,7 +1858,7 @@ Error Server::Host::MergeServicesAndResourcesFrom(Host &aHost) // (1) Service description is shared across a base type and all its subtypes. // (2) `TakeResourcesFrom()` releases resources pinned to its argument. // Therefore, make sure the function is called only for the base type. - newService->mDescription.TakeResourcesFrom(service.mDescription); + newService->mDescription->TakeResourcesFrom(*service.mDescription); } newService->Log((existingService != nullptr) ? Service::kUpdateExisting : Service::kAddNew); @@ -1977,41 +1916,23 @@ exit: //--------------------------------------------------------------------------------------------------------------------- // Server::UpdateMetadata -Server::UpdateMetadata *Server::UpdateMetadata::New(Instance & aInstance, - Host & aHost, - const MessageMetadata &aMessageMetadata) +Error Server::UpdateMetadata::Init(Instance &aInstance, Host &aHost, const MessageMetadata &aMessageMetadata) { - void * buf; - UpdateMetadata *update = nullptr; + InstanceLocatorInit::Init(aInstance); + mNext = nullptr; + mExpireTime = TimerMilli::GetNow() + kDefaultEventsHandlerTimeout; + mDnsHeader = aMessageMetadata.mDnsHeader; + mId = Get().AllocateId(); + mLeaseConfig = aMessageMetadata.mLeaseConfig; + mHost = &aHost; + mIsDirectRxFromClient = aMessageMetadata.IsDirectRxFromClient(); - buf = Heap::CAlloc(1, sizeof(UpdateMetadata)); - VerifyOrExit(buf != nullptr); - - update = new (buf) UpdateMetadata(aInstance, aHost, aMessageMetadata); - -exit: - return update; -} - -void Server::UpdateMetadata::Free(void) -{ - Heap::Free(this); -} - -Server::UpdateMetadata::UpdateMetadata(Instance &aInstance, Host &aHost, const MessageMetadata &aMessageMetadata) - : InstanceLocator(aInstance) - , mNext(nullptr) - , mExpireTime(TimerMilli::GetNow() + kDefaultEventsHandlerTimeout) - , mDnsHeader(aMessageMetadata.mDnsHeader) - , mId(Get().AllocateId()) - , mLeaseConfig(aMessageMetadata.mLeaseConfig) - , mHost(aHost) - , mIsDirectRxFromClient(aMessageMetadata.IsDirectRxFromClient()) -{ if (aMessageMetadata.mMessageInfo != nullptr) { mMessageInfo = *aMessageMetadata.mMessageInfo; } + + return kErrorNone; } } // namespace Srp diff --git a/src/core/net/srp_server.hpp b/src/core/net/srp_server.hpp index ae7bb76b6..aaec30c77 100644 --- a/src/core/net/srp_server.hpp +++ b/src/core/net/srp_server.hpp @@ -57,6 +57,7 @@ #include "common/as_core_type.hpp" #include "common/clearable.hpp" #include "common/heap.hpp" +#include "common/heap_allocatable.hpp" #include "common/heap_data.hpp" #include "common/heap_string.hpp" #include "common/linked_list.hpp" @@ -151,11 +152,15 @@ public: * This class implements a server-side SRP service. * */ - class Service : public otSrpServerService, public LinkedListEntry, private NonCopyable + class Service : public otSrpServerService, + public LinkedListEntry, + private Heap::Allocatable, + private NonCopyable { friend class Server; friend class LinkedList; friend class LinkedListEntry; + friend class Heap::Allocatable; public: /** @@ -216,7 +221,7 @@ public: * @returns A pointer service instance name (as a null-terminated C string). * */ - const char *GetInstanceName(void) const { return mDescription.mInstanceName.AsCString(); } + const char *GetInstanceName(void) const { return mDescription->mInstanceName.AsCString(); } /** * This method gets the full service name of the service. @@ -251,7 +256,7 @@ public: * @returns The port of the service. * */ - uint16_t GetPort(void) const { return mDescription.mPort; } + uint16_t GetPort(void) const { return mDescription->mPort; } /** * This method returns the weight of the service instance. @@ -259,7 +264,7 @@ public: * @returns The weight of the service. * */ - uint16_t GetWeight(void) const { return mDescription.mWeight; } + uint16_t GetWeight(void) const { return mDescription->mWeight; } /** * This method returns the priority of the service instance. @@ -269,7 +274,7 @@ public: * @returns The priority of the service. * */ - uint16_t GetPriority(void) const { return mDescription.mPriority; } + uint16_t GetPriority(void) const { return mDescription->mPriority; } /** * This method returns the TXT record data of the service instance. @@ -277,7 +282,7 @@ public: * @returns A pointer to the buffer containing the TXT record data. * */ - const uint8_t *GetTxtData(void) const { return mDescription.mTxtData.GetBytes(); } + const uint8_t *GetTxtData(void) const { return mDescription->mTxtData.GetBytes(); } /** * This method returns the TXT record data length of the service instance. @@ -285,7 +290,7 @@ public: * @return The TXT record data length (number of bytes in buffer returned from `GetTxtData()`). * */ - uint16_t GetTxtDataLength(void) const { return mDescription.mTxtData.GetLength(); } + uint16_t GetTxtDataLength(void) const { return mDescription->mTxtData.GetLength(); } /** * This method returns the host which the service instance reside on. @@ -293,7 +298,7 @@ public: * @returns A reference to the host instance. * */ - const Host &GetHost(void) const { return mDescription.mHost; } + const Host &GetHost(void) const { return *mDescription->mHost; } /** * This method returns the expire time (in milliseconds) of the service. @@ -322,7 +327,7 @@ public: */ bool MatchesInstanceName(const char *aInstanceName) const { - return (mDescription.mInstanceName == aInstanceName); + return (mDescription->mInstanceName == aInstanceName); } /** @@ -337,12 +342,11 @@ public: bool MatchesServiceName(const char *aServiceName) const { return (mServiceName == aServiceName); } private: - struct Description : public LinkedListEntry, private NonCopyable + struct Description : public LinkedListEntry, + public Heap::Allocatable, + private NonCopyable { - static Description *New(const char *aInstanceName, Host &aHost); - - explicit Description(Host &aHost); - void Free(void); + Error Init(const char *aInstanceName, Host &aHost); const char *GetInstanceName(void) const { return mInstanceName.AsCString(); } bool Matches(const char *aInstanceName) const { return (mInstanceName == aInstanceName); } void ClearResources(void); @@ -351,7 +355,7 @@ public: Description *mNext; Heap::String mInstanceName; - Host & mHost; + Host * mHost; Heap::Data mTxtData; uint16_t mPriority; uint16_t mWeight; @@ -371,20 +375,13 @@ public: kKeyLeaseExpired, }; - static Service *New(const char * aServiceName, - Description &aDescription, - bool aIsSubType, - TimeMilli aUpdateTime); - - Service(Description &aDescription, bool aIsSubType, TimeMilli aUpdateTime); - - void Free(void); - bool MatchesFlags(Flags aFlags) const; + Error Init(const char *aServiceName, Description &aDescription, bool aIsSubType, TimeMilli aUpdateTime); + bool MatchesFlags(Flags aFlags) const; const TimeMilli &GetUpdateTime(void) const { return mUpdateTime; } void Log(Action aAction) const; Heap::String mServiceName; - Description &mDescription; + Description *mDescription; Service * mNext; TimeMilli mUpdateTime; bool mIsDeleted : 1; @@ -396,10 +393,15 @@ public: * This class implements the Host which registers services on the SRP server. * */ - class Host : public otSrpServerHost, public LinkedListEntry, public InstanceLocator, private NonCopyable + class Host : public otSrpServerHost, + public InstanceLocatorInit, + public LinkedListEntry, + private Heap::Allocatable, + private NonCopyable { - friend class LinkedListEntry; friend class Server; + friend class LinkedListEntry; + friend class Heap::Allocatable; public: /** @@ -513,10 +515,10 @@ public: private: static constexpr uint16_t kMaxAddresses = OPENTHREAD_CONFIG_SRP_SERVER_MAX_ADDRESSES_NUM; - static Host *New(Instance &aInstance, TimeMilli aUpdateTime); + Host(void) = default; + ~Host(void); - Host(Instance &aInstance, TimeMilli aUpdateTime); - void Free(void); + Error Init(Instance &aInstance, TimeMilli aUpdateTime); Error SetFullName(const char *aFullName); void SetKey(Dns::Ecdsa256KeyRecord &aKey); void SetLease(uint32_t aLease) { mLease = aLease; } @@ -786,31 +788,33 @@ private: // This class includes metadata for processing a SRP update (register, deregister) // and sending DNS response to the client. - class UpdateMetadata : public InstanceLocator, public LinkedListEntry + class UpdateMetadata : public InstanceLocatorInit, + public LinkedListEntry, + public Heap::Allocatable { friend class LinkedListEntry; + friend class Heap::Allocatable; public: - static UpdateMetadata * New(Instance &aInstance, Host &aHost, const MessageMetadata &aMessageMetadata); - void Free(void); + Error Init(Instance &aInstance, Host &aHost, const MessageMetadata &aMessageMetadata); TimeMilli GetExpireTime(void) const { return mExpireTime; } const Dns::UpdateHeader &GetDnsHeader(void) const { return mDnsHeader; } ServiceUpdateId GetId(void) const { return mId; } const LeaseConfig & GetLeaseConfig(void) const { return mLeaseConfig; } - Host & GetHost(void) { return mHost; } + Host & GetHost(void) { return *mHost; } const Ip6::MessageInfo & GetMessageInfo(void) const { return mMessageInfo; } bool IsDirectRxFromClient(void) const { return mIsDirectRxFromClient; } bool Matches(ServiceUpdateId aId) const { return mId == aId; } private: - UpdateMetadata(Instance &aInstance, Host &aHost, const MessageMetadata &aMessageMetadata); + UpdateMetadata(void) = default; UpdateMetadata * mNext; TimeMilli mExpireTime; Dns::UpdateHeader mDnsHeader; ServiceUpdateId mId; // The ID of this service update transaction. LeaseConfig mLeaseConfig; // Lease config to use when processing the message. - Host & mHost; // The `UpdateMetadata` has no ownership of this host. + Host * mHost; // The `UpdateMetadata` has no ownership of this host. Ip6::MessageInfo mMessageInfo; // Valid when `mIsDirectRxFromClient` is true. bool mIsDirectRxFromClient; };