From d112201279dc10836d57b822841b8173901e852a Mon Sep 17 00:00:00 2001 From: Zang MingJie Date: Fri, 10 Sep 2021 19:52:29 +0800 Subject: [PATCH] Unify SystemPool and lib/support/Pool --- examples/shell/shell_common/cmd_send.cpp | 2 + .../tests/integration/chip_im_initiator.cpp | 1 + .../tests/integration/chip_im_responder.cpp | 2 +- src/include/platform/CHIPDeviceEvent.h | 1 - src/inet/DNSResolver.h | 17 +- src/inet/EndPointBasis.cpp | 14 - src/inet/EndPointBasis.h | 3 +- src/inet/IPEndPointBasis.h | 1 + src/inet/InetLayer.cpp | 8 +- src/inet/InetLayerBasis.h | 4 +- src/inet/TCPEndPoint.cpp | 37 +- src/inet/TCPEndPoint.h | 26 +- src/inet/UDPEndPoint.cpp | 33 +- src/inet/UDPEndPoint.h | 10 +- src/inet/tests/TestInetLayerCommon.hpp | 2 +- .../tests/TestSetupFaultInjectionPosix.cpp | 4 +- src/lib/core/ReferenceCounted.h | 45 +- src/lib/mdns/tests/TestMdnsCache.cpp | 2 + src/lib/support/Variant.h | 1 + .../tests/TestReliableMessageProtocol.cpp | 2 + src/messaging/tests/echo/echo_requester.cpp | 2 + src/messaging/tests/echo/echo_responder.cpp | 2 + src/platform/LwIPEventSupport.cpp | 4 +- .../UserDirectedCommissioningClient.cpp | 2 + src/system/BUILD.gn | 8 +- src/system/SystemLayer.h | 3 +- src/system/SystemLayerImplLwIP.cpp | 9 - src/system/SystemLayerImplLwIP.h | 1 + src/system/SystemLayerImplSelect.cpp | 4 +- src/system/SystemLayerImplSelect.h | 1 + src/system/SystemObject.cpp | 109 ----- src/system/SystemObject.h | 396 +----------------- src/system/SystemPool.h | 46 ++ src/system/SystemPoolHeap.h | 95 +++++ src/system/SystemPoolNonHeap.h | 99 +++++ .../SystemPoolNonHeapBitmapAllocator.cpp | 104 +++++ src/system/SystemPoolNonHeapBitmapAllocator.h | 62 +++ src/system/SystemPoolStatistics.h | 75 ++++ src/system/SystemStats.h | 4 +- src/system/SystemTimer.cpp | 4 +- src/system/SystemTimer.h | 24 +- src/system/tests/TestSystemObject.cpp | 148 ++++--- src/transport/raw/tests/TestTCP.cpp | 1 - 43 files changed, 788 insertions(+), 630 deletions(-) delete mode 100644 src/system/SystemObject.cpp create mode 100644 src/system/SystemPool.h create mode 100644 src/system/SystemPoolHeap.h create mode 100644 src/system/SystemPoolNonHeap.h create mode 100644 src/system/SystemPoolNonHeapBitmapAllocator.cpp create mode 100644 src/system/SystemPoolNonHeapBitmapAllocator.h create mode 100644 src/system/SystemPoolStatistics.h diff --git a/examples/shell/shell_common/cmd_send.cpp b/examples/shell/shell_common/cmd_send.cpp index a1bc6090745967..0953c799e7ee22 100644 --- a/examples/shell/shell_common/cmd_send.cpp +++ b/examples/shell/shell_common/cmd_send.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -26,6 +27,7 @@ #include #include #include +#include #include #include #include diff --git a/src/app/tests/integration/chip_im_initiator.cpp b/src/app/tests/integration/chip_im_initiator.cpp index 7c1a3ce2786fc9..c0fe95ac0dd9aa 100644 --- a/src/app/tests/integration/chip_im_initiator.cpp +++ b/src/app/tests/integration/chip_im_initiator.cpp @@ -724,6 +724,7 @@ int main(int argc, char * argv[]) chip::DeviceLayer::PlatformMgr().RunEventLoop(); chip::app::InteractionModelEngine::GetInstance()->Shutdown(); + gTransportManager.Close(); ShutdownChip(); exit: if (err != CHIP_NO_ERROR || (gCommandRespCount != kMaxCommandMessageCount + kTotalFailureCommandMessageCount)) diff --git a/src/app/tests/integration/chip_im_responder.cpp b/src/app/tests/integration/chip_im_responder.cpp index b95d7ec4c95dd3..648964de276628 100644 --- a/src/app/tests/integration/chip_im_responder.cpp +++ b/src/app/tests/integration/chip_im_responder.cpp @@ -245,7 +245,7 @@ int main(int argc, char * argv[]) } chip::app::InteractionModelEngine::GetInstance()->Shutdown(); - + gTransportManager.Close(); ShutdownChip(); return EXIT_SUCCESS; diff --git a/src/include/platform/CHIPDeviceEvent.h b/src/include/platform/CHIPDeviceEvent.h index f96cfa46f93aed..2e2e6b558564ce 100644 --- a/src/include/platform/CHIPDeviceEvent.h +++ b/src/include/platform/CHIPDeviceEvent.h @@ -303,7 +303,6 @@ typedef void (*AsyncWorkFunct)(intptr_t arg); #include #include #include -#include #include #include diff --git a/src/inet/DNSResolver.h b/src/inet/DNSResolver.h index 6975dcc337c3fa..f9475c3609077d 100644 --- a/src/inet/DNSResolver.h +++ b/src/inet/DNSResolver.h @@ -28,6 +28,8 @@ #include #include #include +#include +#include #define NL_DNS_HOSTNAME_MAX_LEN (253) @@ -61,6 +63,13 @@ enum DNSOptions kDNSOption_Default = kDNSOption_AddrFamily_Any }; +class DNSResolver; +class DNSResolverDeletor +{ +public: + static void Release(DNSResolver * obj); +}; + /** * @class DNSResolver * @@ -70,7 +79,7 @@ enum DNSOptions * interface available for the application layer. * */ -class DNSResolver : public InetLayerBasis +class DNSResolver : public InetLayerBasis, public AtomicReferenceCounted { private: friend class InetLayer; @@ -103,6 +112,7 @@ class DNSResolver : public InetLayerBasis */ typedef void (*OnResolveCompleteFunct)(void * appState, CHIP_ERROR err, uint8_t addrCount, IPAddress * addrArray); + friend class DNSResolverDeletor; static chip::System::ObjectPool sPool; /** @@ -168,5 +178,10 @@ class DNSResolver : public InetLayerBasis #endif // CHIP_SYSTEM_CONFIG_USE_LWIP }; +inline void DNSResolverDeletor::Release(DNSResolver * obj) +{ + DNSResolver::sPool.ReleaseObject(obj); +} + } // namespace Inet } // namespace chip diff --git a/src/inet/EndPointBasis.cpp b/src/inet/EndPointBasis.cpp index 68bbb72446ef67..6d4ca2c6d2b6e0 100644 --- a/src/inet/EndPointBasis.cpp +++ b/src/inet/EndPointBasis.cpp @@ -43,19 +43,5 @@ void EndPointBasis::InitEndPointBasis(InetLayer & aInetLayer, void * aAppState) #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS } -#if CHIP_SYSTEM_CONFIG_USE_LWIP -void EndPointBasis::DeferredFree(chip::System::Object::ReleaseDeferralErrorTactic aTactic) -{ - if (!CHIP_SYSTEM_CONFIG_USE_SOCKETS || IsLWIPEndPoint()) - { - DeferredRelease(static_cast(Layer().SystemLayer()), aTactic); - } - else - { - Release(); - } -} -#endif // CHIP_SYSTEM_CONFIG_USE_LWIP - } // namespace Inet } // namespace chip diff --git a/src/inet/EndPointBasis.h b/src/inet/EndPointBasis.h index 31ed82ca41d2d9..fd9a1e5587eff5 100644 --- a/src/inet/EndPointBasis.h +++ b/src/inet/EndPointBasis.h @@ -31,7 +31,7 @@ #include #include #include - +#include #include #if CHIP_SYSTEM_CONFIG_USE_SOCKETS @@ -124,7 +124,6 @@ class DLL_EXPORT EndPointBasis : public InetLayerBasis uint8_t mLwIPEndPointType; - void DeferredFree(chip::System::Object::ReleaseDeferralErrorTactic aTactic); #endif // CHIP_SYSTEM_CONFIG_USE_LWIP void InitEndPointBasis(InetLayer & aInetLayer, void * aAppState = nullptr); diff --git a/src/inet/IPEndPointBasis.h b/src/inet/IPEndPointBasis.h index e22e2cf4b84806..67c49e4a8d1cfa 100644 --- a/src/inet/IPEndPointBasis.h +++ b/src/inet/IPEndPointBasis.h @@ -28,6 +28,7 @@ #include +#include #include #if CHIP_SYSTEM_CONFIG_USE_LWIP diff --git a/src/inet/InetLayer.cpp b/src/inet/InetLayer.cpp index 0f3c1980aefac5..0c02289f017eef 100644 --- a/src/inet/InetLayer.cpp +++ b/src/inet/InetLayer.cpp @@ -513,7 +513,7 @@ CHIP_ERROR InetLayer::NewTCPEndPoint(TCPEndPoint ** retEndPoint) VerifyOrReturnError(State == kState_Initialized, CHIP_ERROR_INCORRECT_STATE); - *retEndPoint = TCPEndPoint::sPool.TryCreate(); + *retEndPoint = TCPEndPoint::sPool.CreateObject(); if (*retEndPoint == nullptr) { ChipLogError(Inet, "%s endpoint pool FULL", "TCP"); @@ -553,7 +553,7 @@ CHIP_ERROR InetLayer::NewUDPEndPoint(UDPEndPoint ** retEndPoint) VerifyOrReturnError(State == kState_Initialized, CHIP_ERROR_INCORRECT_STATE); - *retEndPoint = UDPEndPoint::sPool.TryCreate(); + *retEndPoint = UDPEndPoint::sPool.CreateObject(); if (*retEndPoint == nullptr) { ChipLogError(Inet, "%s endpoint pool FULL", "UDP"); @@ -728,7 +728,7 @@ CHIP_ERROR InetLayer::ResolveHostAddress(const char * hostName, uint16_t hostNam VerifyOrExit(hostNameLen <= NL_DNS_HOSTNAME_MAX_LEN, err = INET_ERROR_HOST_NAME_TOO_LONG); VerifyOrExit(maxAddrs > 0, err = CHIP_ERROR_NO_MEMORY); - resolver = DNSResolver::sPool.TryCreate(); + resolver = DNSResolver::sPool.CreateObject(); if (resolver != nullptr) { resolver->InitInetLayerBasis(*this); @@ -760,7 +760,7 @@ CHIP_ERROR InetLayer::ResolveHostAddress(const char * hostName, uint16_t hostNam onComplete(appState, err, (err == CHIP_NO_ERROR) ? 1 : 0, addrArray); } - resolver->Release(); + DNSResolver::sPool.ReleaseObject(resolver); resolver = nullptr; ExitNow(err = CHIP_NO_ERROR); diff --git a/src/inet/InetLayerBasis.h b/src/inet/InetLayerBasis.h index cfde74effad6c6..b8209bb7466d3d 100644 --- a/src/inet/InetLayerBasis.h +++ b/src/inet/InetLayerBasis.h @@ -51,12 +51,14 @@ class InetLayer; * InetLayer object. * */ -class InetLayerBasis : public chip::System::Object +class InetLayerBasis : public System::Object { public: InetLayer & Layer() const; bool IsCreatedByInetLayer(const InetLayer & aInetLayer) const; + void * AppState; + protected: void InitInetLayerBasis(InetLayer & aInetLayer, void * aAppState = nullptr); diff --git a/src/inet/TCPEndPoint.cpp b/src/inet/TCPEndPoint.cpp index fbc644fd573e79..eae55c7ece5a77 100644 --- a/src/inet/TCPEndPoint.cpp +++ b/src/inet/TCPEndPoint.cpp @@ -1197,12 +1197,43 @@ void TCPEndPoint::Free() if (err != CHIP_NO_ERROR) Abort(); - // Release the Retain() that happened when the end point was allocated - // [on LwIP, the object may still be alive if DoClose() used the - // EndPointBasis::DeferredFree() method.] + // Release the Retain() that happened when the end point was allocated [on + // LwIP, the object may still be alive if DoClose() used the DeferredFree() + // method.] Release(); } +#if CHIP_SYSTEM_CONFIG_USE_LWIP +void TCPEndPoint::DeferredFree(ReleaseDeferralErrorTactic aTactic) +{ + if (!CHIP_SYSTEM_CONFIG_USE_SOCKETS || IsLWIPEndPoint()) + { + System::LayerLwIP * lSystemLayer = static_cast(Layer().SystemLayer()); + CHIP_ERROR err = lSystemLayer->PostLambda([this] { this->Release(); }); + if (err != CHIP_NO_ERROR) + { + switch (aTactic) + { + case kReleaseDeferralErrorTactic_Ignore: + break; + + case kReleaseDeferralErrorTactic_Release: + this->Release(); + break; + + case kReleaseDeferralErrorTactic_Die: + VerifyOrDie(false); + break; + } + } + } + else + { + Release(); + } +} +#endif // CHIP_SYSTEM_CONFIG_USE_LWIP + #if INET_TCP_IDLE_CHECK_INTERVAL > 0 void TCPEndPoint::SetIdleTimeout(uint32_t timeoutMS) { diff --git a/src/inet/TCPEndPoint.h b/src/inet/TCPEndPoint.h index 8aa7d1a90471d6..421ac90a3d5f68 100644 --- a/src/inet/TCPEndPoint.h +++ b/src/inet/TCPEndPoint.h @@ -29,8 +29,9 @@ #include #include - +#include #include +#include #include @@ -48,6 +49,13 @@ namespace Inet { class InetLayer; +class TCPEndPoint; +class TCPEndPointDeletor +{ +public: + static void Release(TCPEndPoint * obj); +}; + /** * @brief Objects of this class represent TCP transport endpoints. * @@ -56,7 +64,7 @@ class InetLayer; * endpoints (SOCK_STREAM sockets on Linux and BSD-derived systems) or LwIP * TCP protocol control blocks, as the system is configured accordingly. */ -class DLL_EXPORT TCPEndPoint : public EndPointBasis +class DLL_EXPORT TCPEndPoint : public EndPointBasis, public AtomicReferenceCounted { friend class InetLayer; friend class ::chip::Transport::TCPTest; @@ -391,6 +399,14 @@ class DLL_EXPORT TCPEndPoint : public EndPointBasis */ void Free(); + enum ReleaseDeferralErrorTactic + { + kReleaseDeferralErrorTactic_Ignore, /**< No action. */ + kReleaseDeferralErrorTactic_Release, /**< Release immediately. */ + kReleaseDeferralErrorTactic_Die, /**< Die with message. */ + }; + void DeferredFree(ReleaseDeferralErrorTactic aTactic); + /** * @brief Extract whether TCP connection is established. */ @@ -585,6 +601,7 @@ class DLL_EXPORT TCPEndPoint : public EndPointBasis constexpr static size_t kMaxReceiveMessageSize = System::PacketBuffer::kMaxSizeWithoutReserve; private: + friend class TCPEndPointDeletor; static chip::System::ObjectPool sPool; chip::System::PacketBufferHandle mRcvQueue; @@ -701,6 +718,11 @@ class DLL_EXPORT TCPEndPoint : public EndPointBasis #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS }; +inline void TCPEndPointDeletor::Release(TCPEndPoint * obj) +{ + TCPEndPoint::sPool.ReleaseObject(obj); +} + #if INET_CONFIG_ENABLE_TCP_SEND_IDLE_CALLBACKS && INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT inline uint16_t TCPEndPoint::MaxTCPSendQueuePolls(void) { diff --git a/src/inet/UDPEndPoint.cpp b/src/inet/UDPEndPoint.cpp index 60bd3f841083a8..739ad7e9ff533c 100644 --- a/src/inet/UDPEndPoint.cpp +++ b/src/inet/UDPEndPoint.cpp @@ -434,10 +434,41 @@ void UDPEndPoint::Free() #if CHIP_SYSTEM_CONFIG_USE_LWIP DeferredFree(kReleaseDeferralErrorTactic_Die); #else // !CHIP_SYSTEM_CONFIG_USE_LWIP - Release(); + UDPEndPoint::sPool.ReleaseObject(this); #endif // !CHIP_SYSTEM_CONFIG_USE_LWIP } +#if CHIP_SYSTEM_CONFIG_USE_LWIP +void UDPEndPoint::DeferredFree(ReleaseDeferralErrorTactic aTactic) +{ + if (!CHIP_SYSTEM_CONFIG_USE_SOCKETS || IsLWIPEndPoint()) + { + System::LayerLwIP * lSystemLayer = static_cast(Layer().SystemLayer()); + CHIP_ERROR err = lSystemLayer->PostLambda([this] { UDPEndPoint::sPool.ReleaseObject(this); }); + if (err != CHIP_NO_ERROR) + { + switch (aTactic) + { + case kReleaseDeferralErrorTactic_Ignore: + break; + + case kReleaseDeferralErrorTactic_Release: + UDPEndPoint::sPool.ReleaseObject(this); + break; + + case kReleaseDeferralErrorTactic_Die: + VerifyOrDie(false); + break; + } + } + } + else + { + UDPEndPoint::sPool.ReleaseObject(this); + } +} +#endif // CHIP_SYSTEM_CONFIG_USE_LWIP + /** * A synonym for SendTo(addr, port, INET_NULL_INTERFACEID, msg, sendFlags). */ diff --git a/src/inet/UDPEndPoint.h b/src/inet/UDPEndPoint.h index 7a1574cd61852e..cb1afec5b1742e 100644 --- a/src/inet/UDPEndPoint.h +++ b/src/inet/UDPEndPoint.h @@ -30,8 +30,8 @@ #include "inet/IPEndPointBasis.h" #include - #include +#include #if CHIP_SYSTEM_CONFIG_USE_DISPATCH #include @@ -70,6 +70,14 @@ class DLL_EXPORT UDPEndPoint : public IPEndPointBasis void Close(); void Free(); + enum ReleaseDeferralErrorTactic + { + kReleaseDeferralErrorTactic_Ignore, /**< No action. */ + kReleaseDeferralErrorTactic_Release, /**< Release immediately. */ + kReleaseDeferralErrorTactic_Die, /**< Die with message. */ + }; + void DeferredFree(ReleaseDeferralErrorTactic aTactic); + private: UDPEndPoint(const UDPEndPoint &) = delete; diff --git a/src/inet/tests/TestInetLayerCommon.hpp b/src/inet/tests/TestInetLayerCommon.hpp index b90aa0bd6400f8..299a717051989d 100644 --- a/src/inet/tests/TestInetLayerCommon.hpp +++ b/src/inet/tests/TestInetLayerCommon.hpp @@ -34,7 +34,7 @@ #include #include #include - +#include #include // Preprocessor Macros diff --git a/src/inet/tests/TestSetupFaultInjectionPosix.cpp b/src/inet/tests/TestSetupFaultInjectionPosix.cpp index f7f8129e48ff1d..bb5a8fa47cdffd 100644 --- a/src/inet/tests/TestSetupFaultInjectionPosix.cpp +++ b/src/inet/tests/TestSetupFaultInjectionPosix.cpp @@ -26,13 +26,15 @@ * */ +#include +#include + #include "TestInetCommonOptions.h" #include "TestSetupFaultInjection.h" #include #include #include #include -#include #include struct RestartCallbackContext diff --git a/src/lib/core/ReferenceCounted.h b/src/lib/core/ReferenceCounted.h index bdbbb5830ccd16..024e466e6cce55 100644 --- a/src/lib/core/ReferenceCounted.h +++ b/src/lib/core/ReferenceCounted.h @@ -23,6 +23,7 @@ #pragma once +#include #include #include @@ -45,7 +46,7 @@ template , int kInitRefC class ReferenceCounted { public: - typedef uint32_t count_type; + using count_type = uint32_t; /** Adds one to the usage count of this class */ Subclass * Retain() @@ -80,4 +81,46 @@ class ReferenceCounted count_type mRefCount = kInitRefCount; }; +/// An variant of ReferenceCounted, with atomic counter. +template , int kInitRefCount = 1> +class AtomicReferenceCounted +{ +public: + using count_type = uint32_t; + + AtomicReferenceCounted() : mRefCount(kInitRefCount) {} + + /** Adds one to the usage count of this class */ + Subclass * Retain() + { + if (mRefCount == std::numeric_limits::max()) + { + abort(); + } + ++mRefCount; + + return static_cast(this); + } + + /** Release usage of this class */ + void Release() + { + if (mRefCount == 0) + { + abort(); + } + + if (--mRefCount == 0) + { + Deletor::Release(static_cast(this)); + } + } + + /** Get the current reference counter value */ + count_type GetReferenceCount() const { return mRefCount.load(); } + +private: + std::atomic mRefCount; +}; + } // namespace chip diff --git a/src/lib/mdns/tests/TestMdnsCache.cpp b/src/lib/mdns/tests/TestMdnsCache.cpp index 9f9380186b33f4..666289d9064e6c 100644 --- a/src/lib/mdns/tests/TestMdnsCache.cpp +++ b/src/lib/mdns/tests/TestMdnsCache.cpp @@ -20,6 +20,8 @@ // #define MDNS_LOGGING 1 #include #include +#include + #include #include diff --git a/src/lib/support/Variant.h b/src/lib/support/Variant.h index 338bff6a419b25..777a70bdbfa2cc 100644 --- a/src/lib/support/Variant.h +++ b/src/lib/support/Variant.h @@ -22,6 +22,7 @@ #include #include +#include #include #include #include diff --git a/src/messaging/tests/TestReliableMessageProtocol.cpp b/src/messaging/tests/TestReliableMessageProtocol.cpp index 34d6ef108256af..d95f881dfd5b8d 100644 --- a/src/messaging/tests/TestReliableMessageProtocol.cpp +++ b/src/messaging/tests/TestReliableMessageProtocol.cpp @@ -24,6 +24,8 @@ #include "TestMessagingLayer.h" +#include + #include #include #include diff --git a/src/messaging/tests/echo/echo_requester.cpp b/src/messaging/tests/echo/echo_requester.cpp index 00fcd77d19675b..2440c9fa9492fa 100644 --- a/src/messaging/tests/echo/echo_requester.cpp +++ b/src/messaging/tests/echo/echo_requester.cpp @@ -269,6 +269,8 @@ int main(int argc, char * argv[]) chip::DeviceLayer::PlatformMgr().RunEventLoop(); + gUDPManager.Close(); + Shutdown(); exit: diff --git a/src/messaging/tests/echo/echo_responder.cpp b/src/messaging/tests/echo/echo_responder.cpp index c207e484d2c3c2..6fe538c3b28f49 100644 --- a/src/messaging/tests/echo/echo_responder.cpp +++ b/src/messaging/tests/echo/echo_responder.cpp @@ -140,6 +140,8 @@ int main(int argc, char * argv[]) gEchoServer.Shutdown(); } + gUDPManager.Close(); + ShutdownChip(); return EXIT_SUCCESS; diff --git a/src/platform/LwIPEventSupport.cpp b/src/platform/LwIPEventSupport.cpp index b252a9d16ea4ec..69de65e7535720 100644 --- a/src/platform/LwIPEventSupport.cpp +++ b/src/platform/LwIPEventSupport.cpp @@ -42,9 +42,7 @@ CHIP_ERROR PlatformEventing::PostEvent(System::Layer & aLayer, const LambdaBridg event.Type = DeviceEventType::kChipLambdaEvent; event.LambdaEvent = bridge; - PlatformMgr().PostEvent(&event); - - return CHIP_NO_ERROR; + return PlatformMgr().PostEvent(&event); } CHIP_ERROR PlatformEventing::PostEvent(System::Layer & aLayer, System::Object & aTarget, System::EventType aType, diff --git a/src/protocols/user_directed_commissioning/UserDirectedCommissioningClient.cpp b/src/protocols/user_directed_commissioning/UserDirectedCommissioningClient.cpp index 6b67389653aceb..124efa66ffbd05 100644 --- a/src/protocols/user_directed_commissioning/UserDirectedCommissioningClient.cpp +++ b/src/protocols/user_directed_commissioning/UserDirectedCommissioningClient.cpp @@ -25,6 +25,8 @@ #include "UserDirectedCommissioning.h" +#include + namespace chip { namespace Protocols { namespace UserDirectedCommissioning { diff --git a/src/system/BUILD.gn b/src/system/BUILD.gn index b06a9eee6a7f1e..cd8f96147b4b15 100644 --- a/src/system/BUILD.gn +++ b/src/system/BUILD.gn @@ -138,10 +138,14 @@ static_library("system") { "SystemLayerPrivate.h", "SystemMutex.cpp", "SystemMutex.h", - "SystemObject.cpp", - "SystemObject.h", "SystemPacketBuffer.cpp", "SystemPacketBuffer.h", + "SystemPool.h", + "SystemPoolHeap.h", + "SystemPoolNonHeap.h", + "SystemPoolNonHeapBitmapAllocator.cpp", + "SystemPoolNonHeapBitmapAllocator.h", + "SystemPoolStatistics.h", "SystemStats.cpp", "SystemStats.h", "SystemTimer.cpp", diff --git a/src/system/SystemLayer.h b/src/system/SystemLayer.h index 2600abba0e5751..59aa8807d8d360 100644 --- a/src/system/SystemLayer.h +++ b/src/system/SystemLayer.h @@ -25,6 +25,8 @@ #pragma once +#include + // Include configuration headers #include @@ -36,7 +38,6 @@ #include #include #include -#include #if CHIP_SYSTEM_CONFIG_USE_SOCKETS #include diff --git a/src/system/SystemLayerImplLwIP.cpp b/src/system/SystemLayerImplLwIP.cpp index 4b4356e2507c69..2dd1d0b05e26c3 100644 --- a/src/system/SystemLayerImplLwIP.cpp +++ b/src/system/SystemLayerImplLwIP.cpp @@ -188,9 +188,6 @@ CHIP_ERROR LayerImplLwIP::HandleEvent(Object & aTarget, EventType aEventType, ui { VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE); - // Prevent the target object from being freed while dispatching the event. - aTarget.Retain(); - CHIP_ERROR lReturn = CHIP_ERROR_UNEXPECTED_EVENT; const LwIPEventHandlerDelegate * lEventDelegate = static_cast(mEventDelegateList); @@ -205,12 +202,6 @@ CHIP_ERROR LayerImplLwIP::HandleEvent(Object & aTarget, EventType aEventType, ui ChipLogError(chipSystemLayer, "Unexpected event type %d", aEventType); } - /* - Release the reference to the target object. When the object's lifetime finally comes to an end, in most cases this will be - the release call that decrements the ref count to zero. - */ - aTarget.Release(); - return lReturn; } diff --git a/src/system/SystemLayerImplLwIP.h b/src/system/SystemLayerImplLwIP.h index 5993a7a4245dd2..999b5cf69cf384 100644 --- a/src/system/SystemLayerImplLwIP.h +++ b/src/system/SystemLayerImplLwIP.h @@ -24,6 +24,7 @@ #include #include +#include namespace chip { namespace System { diff --git a/src/system/SystemLayerImplSelect.cpp b/src/system/SystemLayerImplSelect.cpp index 48900f3a21248b..856d1f5e9fedb0 100644 --- a/src/system/SystemLayerImplSelect.cpp +++ b/src/system/SystemLayerImplSelect.cpp @@ -81,7 +81,7 @@ CHIP_ERROR LayerImplSelect::Shutdown() } #endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH - timer->Release(); + Timer::ReleaseTimer(timer); } mWakeEvent.Close(*this); mLayerState.Reset(); // Return to uninitialized state to permit re-initialization. @@ -174,7 +174,7 @@ void LayerImplSelect::CancelTimer(TimerCompleteCallback onComplete, void * appSt } #endif - timer->Release(); + Timer::ReleaseTimer(timer); Signal(); } diff --git a/src/system/SystemLayerImplSelect.h b/src/system/SystemLayerImplSelect.h index 6db3abcef30b60..da13ea0d0607fa 100644 --- a/src/system/SystemLayerImplSelect.h +++ b/src/system/SystemLayerImplSelect.h @@ -31,6 +31,7 @@ #include #include +#include #include namespace chip { diff --git a/src/system/SystemObject.cpp b/src/system/SystemObject.cpp deleted file mode 100644 index 37ce315aa0fa66..00000000000000 --- a/src/system/SystemObject.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/* - * - * Copyright (c) 2020 Project CHIP Authors - * Copyright (c) 2016-2017 Nest Labs, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @file - * This file contains definitions of member functions for class - * chip::System::Object. - */ - -// Include module header -#include - -// Include common private header -#include "SystemLayerPrivate.h" - -// Include local headers -#include -#include - -// Include local headers -#include -#include - -namespace chip { -namespace System { - -/** - * @brief - * Decrements the reference count for the CHIP System Layer object. Recycles the object back into the pool if the reference - * count is decremented to zero. No destructor is invoked. - */ -DLL_EXPORT void Object::Release() -{ - unsigned int oldCount = __sync_fetch_and_sub(&this->mRefCount, 1); - - if (oldCount == 1) - { -#if CHIP_SYSTEM_CONFIG_POOL_USE_HEAP - std::lock_guard lock(*mMutexRef); - this->mPrev->mNext = this->mNext; - if (this->mNext) - this->mNext->mPrev = this->mPrev; - delete this; -#endif - __sync_synchronize(); - } - else if (oldCount == 0) - { - abort(); - } -} - -DLL_EXPORT bool Object::TryCreate(size_t aOctets) -{ - if (!__sync_bool_compare_and_swap(&this->mRefCount, 0, 1)) - { - return false; // object already in use - } - - this->AppState = nullptr; - memset(reinterpret_cast(this) + sizeof(*this), 0, aOctets - sizeof(*this)); - - return true; -} - -#if CHIP_SYSTEM_CONFIG_USE_LWIP -void Object::DeferredRelease(LayerLwIP * aSystemLayer, Object::ReleaseDeferralErrorTactic aTactic) -{ - VerifyOrReturn(aSystemLayer != nullptr, ChipLogError(chipSystemLayer, "aSystemLayer is nullptr")); - - CHIP_ERROR lError = aSystemLayer->PostLambda([this] { this->Release(); }); - - if (lError != CHIP_NO_ERROR) - { - switch (aTactic) - { - case kReleaseDeferralErrorTactic_Ignore: - break; - - case kReleaseDeferralErrorTactic_Release: - this->Release(); - break; - - case kReleaseDeferralErrorTactic_Die: - VerifyOrDieWithMsg(false, chipSystemLayer, "Object::DeferredRelease %p->PostEvent failed err(%" CHIP_ERROR_FORMAT ")", - aSystemLayer, lError.Format()); - break; - } - } -} -#endif // CHIP_SYSTEM_CONFIG_USE_LWIP - -} // namespace System -} // namespace chip diff --git a/src/system/SystemObject.h b/src/system/SystemObject.h index 4a38fa0e864e68..dc6d12daf38ca6 100644 --- a/src/system/SystemObject.h +++ b/src/system/SystemObject.h @@ -1,5 +1,4 @@ /* - * * Copyright (c) 2020 Project CHIP Authors * Copyright (c) 2016-2017 Nest Labs, Inc. * @@ -16,410 +15,17 @@ * limitations under the License. */ -/** - * @file - * This file contains declarations of the following classes and - * templates: - * - * - class chip::System::Object - * - template union chip::System::ObjectArena - * - template class chip::System::ObjectPool - */ - #pragma once -// Include configuration headers #include -// Include dependent headers -#include -#include -#include -#include - -#include -#if CHIP_SYSTEM_CONFIG_POOL_USE_HEAP -#include -#include -#include -#endif - -#include - -#include -#include - -#ifndef SYSTEM_OBJECT_HWM_TEST_HOOK -#define SYSTEM_OBJECT_HWM_TEST_HOOK() -#endif - namespace chip { namespace System { -// Forward class and class template declarations -class Layer; -class LayerSockets; -class LayerLwIP; -template -class ObjectPool; - -/** - * @class Object - * - * @brief - * This represents a reference-counted object allocated from space contained in an ObjectPool object. - * - * @note - * Instance of this class may only be constructed using the related ObjectPool class template. The copy constructor and the - * assignment operator are deleted. A reference counting system is used to track retentions of instances of this class. - * When an object is initially retained, its reference count is one. Additional retentions may increment the reference count. - * When the object is released, the reference count is decremented. When the reference count is zero, the object is recycled - * back to the pool for reallocation. There is no destructor available. Subclasses must be designed to ensure that all - * encapsulated resources are released when the final retention is released and the object is recycled. - * - * While this class is defined as concrete, it should be regarded as abstract. - */ +// An empty object, all its functionality is decoupled, but we still need for the type system. class DLL_EXPORT Object { - template - friend class ObjectPool; - -public: - Object() : mRefCount(0) - { -#if CHIP_SYSTEM_CONFIG_POOL_USE_HEAP - mNext = nullptr; - mPrev = nullptr; -#endif - } - - virtual ~Object() {} - - /** Test whether this object is retained. Concurrency safe. */ - bool IsRetained() const; - - void Retain(); - void Release(); - Layer & SystemLayer() const; - -protected: -#if CHIP_SYSTEM_CONFIG_USE_LWIP - /**< What to do when DeferredRelease fails to post a kEvent_ReleaseObj. */ - enum ReleaseDeferralErrorTactic - { - kReleaseDeferralErrorTactic_Ignore, /**< No action. */ - kReleaseDeferralErrorTactic_Release, /**< Release immediately. */ - kReleaseDeferralErrorTactic_Die, /**< Die with message. */ - }; - - void DeferredRelease(LayerLwIP * aSystemLayer, ReleaseDeferralErrorTactic aTactic); -#endif // CHIP_SYSTEM_CONFIG_USE_LWIP - -private: - Object(const Object &) = delete; - Object & operator=(const Object &) = delete; - -#if CHIP_SYSTEM_CONFIG_POOL_USE_HEAP - Object * mNext; - Object * mPrev; - std::mutex * mMutexRef; -#endif - - unsigned int mRefCount; /**< Count of remaining calls to Release before object is dead. */ - - /** - * @brief - * Attempts to perform an initial retention of this object, in a THREAD-SAFE manner. - * - * @note - * If reference count is non-zero, tryCreate will fail and return false. - * If reference count is zero, then: - * - reference count will be set to 1 - * - The size of the created object (assumed to be derived from SystemObject) is aOctects. - * the method will memset to 0 the bytes following sizeof(SystemObject). - * - * Typical usage is like: - * class Foo: public SystemObject {...} - * .... - * Foo foo; - * foo.TryCreate(sizeof(foo)); - * - * IMPORTANT inheritance precondition: - * 0 memset assumes that SystemObject is the top of the inheritance. This will NOT work properly: - * class Bar: public Baz, SystemObject {...} - * Bar bar; - * bar.TryCreate(sizeof(bar)); /// NOT safe: this will clear sizeof(Baz) extra bytes in unallocated space. - */ - bool TryCreate(size_t aOctets); - -public: - void * AppState; /**< Generic pointer to app-specific data associated with the object. */ -}; - -/** - * @brief - * Tests whether this object is retained. - * - * @note - * No memory barrier is applied. If this returns \c false in one thread context, then it does not imply that another thread - * cannot have previously retained the object for \c aLayer. If it returns \c true, then the logic using \c mRefCount is - * responsible for ensuring concurrency safety for this object. - */ -inline bool Object::IsRetained() const -{ - return this->mRefCount > 0; -} - -/** - * @brief - * Increments the reference count for the CHIP System Layer object. The object is assumed to be live. - */ -inline void Object::Retain() -{ - __sync_fetch_and_add(&this->mRefCount, 1); -} - -/** - * @brief - * A union template used for representing a well-aligned block of memory. - * - * @tparam ALIGN a typename with the alignment properties for the block. - * @tparam SIZE a constant size of the block in bytes. - */ -template -union ObjectArena -{ - uint8_t uMemory[SIZE]; - ALIGN uAlign; -}; - -/** - * @brief - * A class template used for allocating Object subclass objects from an ObjectArena<> template union. - * - * @tparam T a subclass of Object to be allocated from the arena. - * @tparam N a positive integer number of objects of class T to allocate from the arena. - */ -template -class ObjectPool -{ -public: - void Reset(); - - T * TryCreate(); - void GetStatistics(chip::System::Stats::count_t & aNumInUse, chip::System::Stats::count_t & aHighWatermark); - - /** - * @brief - * Run a functor for each active object in the pool - * - * @param function The functor of type `bool (*)(T*)`, return false to break the iteration - * @return bool Returns false if broke during iteration - */ - template - bool ForEachActiveObject(Function && function) - { -#if CHIP_SYSTEM_CONFIG_POOL_USE_HEAP - std::lock_guard lock(mMutex); - Object * p = mDummyHead.mNext; - while (p) - { - if (!function(static_cast(p))) - { - return false; - } - p = p->mNext; - } -#else - for (unsigned int i = 0; i < N; ++i) - { - T & lObject = reinterpret_cast(mArena.uMemory)[i]; - - if (lObject.IsRetained()) - { - if (!function(&lObject)) - return false; - } - } -#endif - return true; - } - -private: - friend class TestObject; - -#if CHIP_SYSTEM_CONFIG_POOL_USE_HEAP - std::mutex mMutex; - Object mDummyHead; -#else - ObjectArena mArena; - -#if CHIP_SYSTEM_CONFIG_PROVIDE_STATISTICS - void GetNumObjectsInUse(unsigned int aStartIndex, unsigned int & aNumInUse); - void UpdateHighWatermark(const unsigned int & aCandidate); - volatile unsigned int mHighWatermark; -#endif -#endif }; -template -inline void ObjectPool::Reset() -{ -#if CHIP_SYSTEM_CONFIG_POOL_USE_HEAP - std::lock_guard lock(mMutex); - Object * p = mDummyHead.mNext; - - while (p) - { - Object * del = p; - p = p->mNext; - delete del; - } - - mDummyHead.mNext = nullptr; -#else - memset(mArena.uMemory, 0, N * sizeof(T)); - -#if CHIP_SYSTEM_CONFIG_PROVIDE_STATISTICS - mHighWatermark = 0; -#endif -#endif -} - -/** - * @brief - * Tries to initially retain the first object in the pool that is not retained. - */ -template -inline T * ObjectPool::TryCreate() -{ - T * lReturn = nullptr; - - (void) static_cast(lReturn); /* In C++-11, this would be a static_assert that T inherits Object. */ - -#if CHIP_SYSTEM_CONFIG_POOL_USE_HEAP - T * newNode = new T(); - - if (newNode->TryCreate(sizeof(T))) - { - std::lock_guard lock(mMutex); - Object * p = &mDummyHead; - if (p->mNext) - { - p->mNext->mPrev = newNode; - } - newNode->mNext = p->mNext; - p->mNext = newNode; - newNode->mPrev = p; - newNode->mMutexRef = &mMutex; - lReturn = newNode; - } - else - { - delete newNode; - } -#else // CHIP_SYSTEM_CONFIG_POOL_USE_HEAP - unsigned int lIndex = 0; - - for (lIndex = 0; lIndex < N; ++lIndex) - { - T & lObject = reinterpret_cast(mArena.uMemory)[lIndex]; - - if (lObject.TryCreate(sizeof(T))) - { - lReturn = &lObject; - break; - } - } - -#if CHIP_SYSTEM_CONFIG_PROVIDE_STATISTICS - unsigned int lNumInUse = 0; - - if (lReturn != nullptr) - { - lIndex++; - lNumInUse = lIndex; - GetNumObjectsInUse(lIndex, lNumInUse); - } - else - { - lNumInUse = N; - } - - UpdateHighWatermark(lNumInUse); -#endif -#endif // CHIP_SYSTEM_CONFIG_POOL_USE_HEAP - - return lReturn; -} - -#if CHIP_SYSTEM_CONFIG_PROVIDE_STATISTICS && !CHIP_SYSTEM_CONFIG_POOL_USE_HEAP -template -inline void ObjectPool::UpdateHighWatermark(const unsigned int & aCandidate) -{ - unsigned int lTmp; - - while (aCandidate > (lTmp = mHighWatermark)) - { - SYSTEM_OBJECT_HWM_TEST_HOOK(); - (void) __sync_bool_compare_and_swap(&mHighWatermark, lTmp, aCandidate); - } -} - -/** - * Return the number of objects in use starting at a given index - * - * @param[in] aStartIndex The index to start counting from; pass 0 to count over - * the whole pool. - * @param[in/out] aNumInUse The number of objects in use. If aStartIndex is not 0, - * the function adds to the counter without resetting it first. - */ -template -inline void ObjectPool::GetNumObjectsInUse(unsigned int aStartIndex, unsigned int & aNumInUse) -{ - unsigned int count = 0; - - for (unsigned int lIndex = aStartIndex; lIndex < N; ++lIndex) - { - T & lObject = reinterpret_cast(mArena.uMemory)[lIndex]; - - if (lObject.IsRetained()) - { - count++; - } - } - - if (aStartIndex == 0) - { - aNumInUse = 0; - } - - aNumInUse += count; -} -#endif // CHIP_SYSTEM_CONFIG_PROVIDE_STATISTICS && !CHIP_SYSTEM_CONFIG_POOL_USE_HEAP - -template -inline void ObjectPool::GetStatistics(chip::System::Stats::count_t & aNumInUse, chip::System::Stats::count_t & aHighWatermark) -{ -#if CHIP_SYSTEM_CONFIG_PROVIDE_STATISTICS && !CHIP_SYSTEM_CONFIG_POOL_USE_HEAP - unsigned int lNumInUse; - unsigned int lHighWatermark; - - GetNumObjectsInUse(0, lNumInUse); - lHighWatermark = mHighWatermark; - - if (lNumInUse > CHIP_SYS_STATS_COUNT_MAX) - { - lNumInUse = CHIP_SYS_STATS_COUNT_MAX; - } - if (lHighWatermark > CHIP_SYS_STATS_COUNT_MAX) - { - lHighWatermark = CHIP_SYS_STATS_COUNT_MAX; - } - aNumInUse = static_cast(lNumInUse); - aHighWatermark = static_cast(lHighWatermark); -#endif -} - } // namespace System } // namespace chip diff --git a/src/system/SystemPool.h b/src/system/SystemPool.h new file mode 100644 index 00000000000000..c89d0ce70fa46d --- /dev/null +++ b/src/system/SystemPool.h @@ -0,0 +1,46 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2016-2017 Nest Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file + * This file contains declarations of the following classes and + * templates: + * + * - class chip::System::Object + * - template union chip::System::ObjectArena + * - template class chip::System::ObjectPool + */ + +#pragma once + +#include +#include + +namespace chip { +namespace System { + +#if CHIP_SYSTEM_CONFIG_POOL_USE_HEAP +template +using ObjectPool = ObjectPoolHeap; +#else +template +using ObjectPool = ObjectPoolNonHeap; +#endif + +} // namespace System +} // namespace chip diff --git a/src/system/SystemPoolHeap.h b/src/system/SystemPoolHeap.h new file mode 100644 index 00000000000000..34f1c7459439d3 --- /dev/null +++ b/src/system/SystemPoolHeap.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2021 Project CHIP Authors + * Copyright (c) 2016-2017 Nest Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#if CHIP_SYSTEM_CONFIG_POOL_USE_HEAP + +#include +#include + +#include +#include + +namespace chip { +namespace System { + +/** + * @brief + * A class template used for allocating Object subclass objects from an ObjectArena<> template union. + * + * @tparam T a subclass of Object to be allocated from the arena. + * @tparam N a positive integer number of objects of class T to allocate from the arena. + */ +template +class ObjectPoolHeap : public ObjectPoolStatistics +{ +public: + template + T * CreateObject(Args &&... args) + { + std::lock_guard lock(mutex); + T * object = new T(std::forward(args)...); + if (object == nullptr) + { + return nullptr; + } + + mObjects.insert(object); + IncreaseUsage(); + return object; + } + + void ReleaseObject(T * object) + { + std::lock_guard lock(mutex); + auto iter = mObjects.find(object); + VerifyOrDie(iter != mObjects.end()); + mObjects.erase(iter); + delete object; + DecreaseUsage(); + } + + /** + * @brief + * Run a functor for each active object in the pool + * + * @param function The functor of type `bool (*)(T*)`, return false to break the iteration + * @return bool Returns false if broke during iteration + */ + template + bool ForEachActiveObject(Function && function) + { + std::lock_guard lock(mutex); + // Create a new copy of original set, allowing add/remove elements while iterating in the same thread. + for (auto object : std::set(mObjects)) + { + if (!function(object)) + return false; + } + return true; + } + +private: + std::recursive_mutex mutex; + std::set mObjects; +}; + +} // namespace System +} // namespace chip + +#endif // CHIP_SYSTEM_CONFIG_POOL_USE_HEAP diff --git a/src/system/SystemPoolNonHeap.h b/src/system/SystemPoolNonHeap.h new file mode 100644 index 00000000000000..7f9cf62d0a15bb --- /dev/null +++ b/src/system/SystemPoolNonHeap.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2013 Nest Labs, Inc. + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include +#include + +namespace chip { +namespace System { + +/** + * @brief + * A class template used for allocating Objects. + * + * @tparam T a subclass of element to be allocated. + * @tparam N a positive integer max number of elements the pool provides. + */ +template +class ObjectPoolNonHeap : public StaticAllocatorBitmap +{ +public: + ObjectPoolNonHeap() : StaticAllocatorBitmap(mMemory, mUsage, N, sizeof(T)) {} + + template + T * CreateObject(Args &&... args) + { + T * element = static_cast(Allocate()); + if (element != nullptr) + return new (element) T(std::forward(args)...); + else + return nullptr; + } + + void ReleaseObject(T * element) + { + if (element == nullptr) + return; + + element->~T(); + Deallocate(element); + } + + /** + * @brief + * Run a functor for each active object in the pool + * + * @param function The functor of type `bool (*)(T*)`, return false to break the iteration + * @return bool Returns false if broke during iteration + * + * caution + * this function is not thread-safe, make sure all usage of the + * pool is protected by a lock, or else avoid using this function + */ + template + bool ForEachActiveObject(Function && function) + { + LambdaProxy proxy(std::forward(function)); + return ForEachActiveObjectInner(&proxy, &LambdaProxy::Call); + } + +private: + template + class LambdaProxy + { + public: + LambdaProxy(Function && function) : mFunction(std::move(function)) {} + static bool Call(void * context, void * target) + { + return static_cast(context)->mFunction(static_cast(target)); + } + + private: + Function mFunction; + }; + + std::atomic mUsage[(N + kBitChunkSize - 1) / kBitChunkSize]; + alignas(alignof(T)) uint8_t mMemory[N * sizeof(T)]; +}; + +} // namespace System +} // namespace chip diff --git a/src/system/SystemPoolNonHeapBitmapAllocator.cpp b/src/system/SystemPoolNonHeapBitmapAllocator.cpp new file mode 100644 index 00000000000000..bfceb4399a2a56 --- /dev/null +++ b/src/system/SystemPoolNonHeapBitmapAllocator.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2013 Nest Labs, Inc. + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +namespace chip { +namespace System { + +StaticAllocatorBitmap::StaticAllocatorBitmap(void * storage, std::atomic * usage, size_t capacity, + size_t elementSize) : + mCapacity(capacity), + mElements(storage), mElementSize(elementSize), mUsage(usage) +{ + for (size_t word = 0; word * kBitChunkSize < mCapacity; ++word) + { + mUsage[word].store(0); + } +} + +void * StaticAllocatorBitmap::Allocate() +{ + for (size_t word = 0; word * kBitChunkSize < mCapacity; ++word) + { + auto & usage = mUsage[word]; + auto value = usage.load(std::memory_order_relaxed); + for (size_t offset = 0; offset < kBitChunkSize && offset + word * kBitChunkSize < mCapacity; ++offset) + { + if ((value & (kBit1 << offset)) == 0) + { + if (usage.compare_exchange_strong(value, value | (kBit1 << offset))) + { + IncreaseUsage(); + return At(word * kBitChunkSize + offset); + } + else + { + value = usage.load(std::memory_order_relaxed); // if there is a race, update new usage + } + } + } + } + return nullptr; +} + +void StaticAllocatorBitmap::Deallocate(void * element) +{ + size_t index = IndexOf(element); + size_t word = index / kBitChunkSize; + size_t offset = index - (word * kBitChunkSize); + + // ensure the element is in the pool + VerifyOrDie(index < mCapacity); + + auto value = mUsage[word].fetch_and(~(kBit1 << offset)); + VerifyOrDie((value & (kBit1 << offset)) != 0); // assert fail when free an unused slot + DecreaseUsage(); +} + +size_t StaticAllocatorBitmap::IndexOf(void * element) +{ + std::ptrdiff_t diff = static_cast(element) - static_cast(mElements); + VerifyOrDie(diff >= 0); + VerifyOrDie(static_cast(diff) % mElementSize == 0); + auto index = static_cast(diff) / mElementSize; + VerifyOrDie(index < mCapacity); + return index; +} + +bool StaticAllocatorBitmap::ForEachActiveObjectInner(void * context, Lambda lambda) +{ + for (size_t word = 0; word * kBitChunkSize < mCapacity; ++word) + { + auto & usage = mUsage[word]; + auto value = usage.load(std::memory_order_relaxed); + for (size_t offset = 0; offset < kBitChunkSize && offset + word * kBitChunkSize < mCapacity; ++offset) + { + if ((value & (kBit1 << offset)) != 0) + { + if (!lambda(context, At(word * kBitChunkSize + offset))) + return false; + } + } + } + return true; +} + +} // namespace System +} // namespace chip diff --git a/src/system/SystemPoolNonHeapBitmapAllocator.h b/src/system/SystemPoolNonHeapBitmapAllocator.h new file mode 100644 index 00000000000000..3cde30e34b06f4 --- /dev/null +++ b/src/system/SystemPoolNonHeapBitmapAllocator.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2013 Nest Labs, Inc. + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +#include + +namespace chip { +namespace System { + +class StaticAllocatorBitmap : public ObjectPoolStatistics +{ +protected: + /** + * Use the largest data type supported by `std::atomic`. Putting multiple atomic inside a single cache line won't improve + * concurrency, while the use of larger data type can improve the performance by reducing the number of outer loop iterations. + */ + using tBitChunkType = unsigned long; + static constexpr const tBitChunkType kBit1 = 1; // make sure bitshifts produce the right type + static constexpr const size_t kBitChunkSize = std::numeric_limits::digits; + static_assert(ATOMIC_LONG_LOCK_FREE, "StaticAllocatorBitmap is not lock free"); + +public: + StaticAllocatorBitmap(void * storage, std::atomic * usage, size_t capacity, size_t elementSize); + void * Allocate(); + void Deallocate(void * element); + +protected: + void * At(size_t index) { return static_cast(mElements) + mElementSize * index; } + size_t IndexOf(void * element); + + using Lambda = bool (*)(void *, void *); + bool ForEachActiveObjectInner(void * context, Lambda lambda); + +private: + const size_t mCapacity; + void * mElements; + const size_t mElementSize; + std::atomic * mUsage; +}; + +} // namespace System +} // namespace chip diff --git a/src/system/SystemPoolStatistics.h b/src/system/SystemPoolStatistics.h new file mode 100644 index 00000000000000..4e4c8439a8fcca --- /dev/null +++ b/src/system/SystemPoolStatistics.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2021 Project CHIP Authors + * Copyright (c) 2016-2017 Nest Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +#include + +namespace chip { +namespace System { + +#if CHIP_SYSTEM_CONFIG_PROVIDE_STATISTICS + +class ObjectPoolStatistics +{ +public: + void ResetStatistics() { mHighWatermark.store(mUsage.load()); } + + void GetStatistics(Stats::count_t & aNumInUse, Stats::count_t & aHighWatermark) + { + aNumInUse = static_cast(std::min(Stats::CHIP_SYS_STATS_COUNT_MAX, mUsage.load())); + aHighWatermark = + static_cast(std::min(Stats::CHIP_SYS_STATS_COUNT_MAX, mHighWatermark.load())); + } + +protected: + void IncreaseUsage() + { + unsigned int usage = ++mUsage; + unsigned int prev = mHighWatermark; + while (prev < usage && !mHighWatermark.compare_exchange_weak(prev, usage)) + { + } + } + + void DecreaseUsage() { --mUsage; } + +private: + std::atomic mUsage; + std::atomic mHighWatermark; +}; + +#else + +class ObjectPoolStatistics +{ +public: + void ResetStatistics() {} + void GetStatistics(Stats::count_t & aNumInUse, Stats::count_t & aHighWatermark) {} + +protected: + void IncreaseUsage() {} + void DecreaseUsage() {} +}; + +#endif + +} // namespace System +} // namespace chip diff --git a/src/system/SystemStats.h b/src/system/SystemStats.h index cb5be8c72eb20d..22a81a5c2bb493 100644 --- a/src/system/SystemStats.h +++ b/src/system/SystemStats.h @@ -74,9 +74,9 @@ enum kNumEntries }; -typedef int8_t count_t; +typedef uint8_t count_t; #define PRI_CHIP_SYS_STATS_COUNT PRId8 -#define CHIP_SYS_STATS_COUNT_MAX INT8_MAX +constexpr const auto CHIP_SYS_STATS_COUNT_MAX = std::numeric_limits::max(); extern count_t ResourcesInUse[kNumEntries]; extern count_t HighWatermarks[kNumEntries]; diff --git a/src/system/SystemTimer.cpp b/src/system/SystemTimer.cpp index fefa8f3243e16d..1a804119f5baad 100644 --- a/src/system/SystemTimer.cpp +++ b/src/system/SystemTimer.cpp @@ -81,7 +81,7 @@ ObjectPool Timer::sPool; Timer * Timer::New(System::Layer & systemLayer, uint32_t delayMilliseconds, TimerCompleteCallback onComplete, void * appState) { - Timer * timer = Timer::sPool.TryCreate(); + Timer * timer = Timer::sPool.CreateObject(); if (timer == nullptr) { ChipLogError(chipSystemLayer, "Timer pool EMPTY"); @@ -129,7 +129,7 @@ void Timer::HandleComplete() // Since this thread changed the state of mOnComplete, release the timer. AppState = nullptr; mSystemLayer = nullptr; - this->Release(); + ReleaseTimer(this); // Invoke the app's callback, if it's still valid. if (lOnComplete != nullptr) diff --git a/src/system/SystemTimer.h b/src/system/SystemTimer.h index 6427c8dc3f6f2e..961494ae29b9b9 100644 --- a/src/system/SystemTimer.h +++ b/src/system/SystemTimer.h @@ -31,12 +31,13 @@ #include // Include dependent headers +#include #include - #include #include +#include #include -#include +#include #include #if CHIP_SYSTEM_CONFIG_USE_DISPATCH @@ -54,10 +55,17 @@ using TimerCompleteCallback = void (*)(Layer * aLayer, void * appState); #if CHIP_SYSTEM_CONFIG_USE_TIMER_POOL +class Timer; +class TimerDeletor +{ +public: + static void Release(Timer * obj); +}; + /** * This is an Object-pool based class that System::Layer implementations can use to assist in providing timer functions. */ -class DLL_EXPORT Timer : public Object +class DLL_EXPORT Timer : public AtomicReferenceCounted { public: /** @@ -219,8 +227,13 @@ class DLL_EXPORT Timer : public Object sPool.GetStatistics(aNumInUse, aHighWatermark); } + static void ReleaseTimer(Timer * timer) { sPool.ReleaseObject(timer); } + + void * AppState; + private: friend class LayerImplLwIP; + friend class TimerDeletor; static ObjectPool sPool; TimerCompleteCallback mOnComplete; @@ -239,6 +252,11 @@ class DLL_EXPORT Timer : public Object Timer & operator=(const Timer &) = delete; }; +inline void TimerDeletor::Release(Timer * obj) +{ + Timer::sPool.ReleaseObject(obj); +} + #endif // CHIP_SYSTEM_CONFIG_USE_TIMER_POOL } // namespace System diff --git a/src/system/tests/TestSystemObject.cpp b/src/system/tests/TestSystemObject.cpp index bc710fdd1e759e..c52752751083bc 100644 --- a/src/system/tests/TestSystemObject.cpp +++ b/src/system/tests/TestSystemObject.cpp @@ -37,7 +37,9 @@ // clang-format on #include +#include +#include #include #include #include @@ -69,7 +71,14 @@ namespace System { static int Initialize(void * aContext); static int Finalize(void * aContext); -class TestObject : public Object +class TestObject; +class TestObjectDeletor +{ +public: + static void Release(TestObject * obj); +}; + +class TestObject : public AtomicReferenceCounted { public: CHIP_ERROR Init(); @@ -77,10 +86,7 @@ class TestObject : public Object static void CheckIteration(nlTestSuite * inSuite, void * aContext); static void CheckRetention(nlTestSuite * inSuite, void * aContext); static void CheckConcurrency(nlTestSuite * inSuite, void * aContext); -#if !CHIP_SYSTEM_CONFIG_POOL_USE_HEAP static void CheckHighWatermark(nlTestSuite * inSuite, void * aContext); - static void CheckHighWatermarkConcurrency(nlTestSuite * inSuite, void * aContext); -#endif private: static constexpr int kNumThreads = 16; @@ -89,8 +95,9 @@ class TestObject : public Object static constexpr int kPoolSize = (kNumThreads * 7) + 1; static_assert(kNumThreads > 1, "kNumThreads should be more than 1"); - static_assert(kPoolSize < CHIP_SYS_STATS_COUNT_MAX, "kPoolSize is not less than CHIP_SYS_STATS_COUNT_MAX"); + static_assert(kPoolSize < Stats::CHIP_SYS_STATS_COUNT_MAX, "kPoolSize is not less than CHIP_SYS_STATS_COUNT_MAX"); + friend class TestObjectDeletor; static ObjectPool sPool; #if CHIP_SYSTEM_CONFIG_POSIX_LOCKING @@ -98,9 +105,7 @@ class TestObject : public Object void Delay(volatile unsigned int & aAccumulator); static void * CheckConcurrencyThread(void * aContext); -#if !CHIP_SYSTEM_CONFIG_POOL_USE_HEAP static void * CheckHighWatermarkThread(void * aContext); -#endif static void MultithreadedTest(nlTestSuite * inSuite, void * aContext, void * (*aStartRoutine)(void *) ); #endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING @@ -108,6 +113,11 @@ class TestObject : public Object TestObject & operator=(const TestObject &) = delete; }; +void TestObjectDeletor::Release(TestObject * obj) +{ + TestObject::sPool.ReleaseObject(obj); +} + ObjectPool TestObject::sPool; CHIP_ERROR TestObject::Init() @@ -140,11 +150,15 @@ void TestObject::CheckIteration(nlTestSuite * inSuite, void * aContext) TestContext & lContext = *static_cast(aContext); unsigned int i; - sPool.Reset(); + // Pool should be empty before tests. + sPool.ForEachActiveObject([&](auto object) { + NL_TEST_ASSERT(lContext.mTestSuite, false); + return true; + }); for (i = 0; i < kPoolSize; ++i) { - TestObject * lCreated = sPool.TryCreate(); + TestObject * lCreated = sPool.CreateObject(); NL_TEST_ASSERT(lContext.mTestSuite, lCreated != nullptr); lCreated->Init(); @@ -152,7 +166,7 @@ void TestObject::CheckIteration(nlTestSuite * inSuite, void * aContext) i = 0; sPool.ForEachActiveObject([&](TestObject * lCreated) { - NL_TEST_ASSERT(lContext.mTestSuite, lCreated->IsRetained()); + NL_TEST_ASSERT(lContext.mTestSuite, lCreated->GetReferenceCount() > 0); i++; return true; }); @@ -167,6 +181,12 @@ void TestObject::CheckIteration(nlTestSuite * inSuite, void * aContext) return true; }); NL_TEST_ASSERT(lContext.mTestSuite, i == kPoolSize / 2); + + // Clear the pool + sPool.ForEachActiveObject([&](auto object) { + object->Release(); + return true; + }); } // Test Object retention @@ -176,11 +196,15 @@ void TestObject::CheckRetention(nlTestSuite * inSuite, void * aContext) TestContext & lContext = *static_cast(aContext); unsigned int i; - sPool.Reset(); + // Pool should be empty before tests. + sPool.ForEachActiveObject([&](auto object) { + NL_TEST_ASSERT(lContext.mTestSuite, false); + return true; + }); for (i = 0; i < kPoolSize; ++i) { - TestObject * lCreated = sPool.TryCreate(); + TestObject * lCreated = sPool.CreateObject(); NL_TEST_ASSERT(lContext.mTestSuite, lCreated != nullptr); @@ -202,6 +226,12 @@ void TestObject::CheckRetention(nlTestSuite * inSuite, void * aContext) return true; }); NL_TEST_ASSERT(lContext.mTestSuite, i == kPoolSize); + + // Clear the pool + sPool.ForEachActiveObject([&](auto object) { + object->Release(); + return true; + }); } // Test Object concurrency @@ -238,10 +268,10 @@ void * TestObject::CheckConcurrencyThread(void * aContext) lObject = nullptr; while (lObject == nullptr) { - lObject = sPool.TryCreate(); + lObject = sPool.CreateObject(); } - NL_TEST_ASSERT(lContext.mTestSuite, lObject->IsRetained()); + NL_TEST_ASSERT(lContext.mTestSuite, lObject->GetReferenceCount() > 0); lObject->Init(); lObject->Delay(lContext.mAccumulator); @@ -254,10 +284,10 @@ void * TestObject::CheckConcurrencyThread(void * aContext) lObject = nullptr; while (lObject == nullptr) { - lObject = sPool.TryCreate(); + lObject = sPool.CreateObject(); } - NL_TEST_ASSERT(lContext.mTestSuite, lObject->IsRetained()); + NL_TEST_ASSERT(lContext.mTestSuite, lObject->GetReferenceCount() > 0); lObject->Init(); lObject->Delay(lContext.mAccumulator); @@ -268,36 +298,16 @@ void * TestObject::CheckConcurrencyThread(void * aContext) return aContext; } -#if !CHIP_SYSTEM_CONFIG_POOL_USE_HEAP -void * TestObject::CheckHighWatermarkThread(void * aContext) -{ - TestContext & lContext = *static_cast(aContext); - int i; - chip::System::Stats::count_t lNumInUse; - chip::System::Stats::count_t lHighWatermark; - - i = (rand() % CHIP_SYS_STATS_COUNT_MAX); - - sPool.UpdateHighWatermark(static_cast(i)); - - sPool.GetStatistics(lNumInUse, lHighWatermark); - - NL_TEST_ASSERT(lContext.mTestSuite, lHighWatermark >= i); - if (lHighWatermark < i) - { - printf("hwm: %d, i: %d\n", lHighWatermark, i); - } - - return aContext; -} -#endif - void TestObject::MultithreadedTest(nlTestSuite * inSuite, void * aContext, void * (*aStartRoutine)(void *) ) { TestContext & lContext = *static_cast(aContext); pthread_t lThread[kNumThreads]; - sPool.Reset(); + // Pool should be empty before tests. + sPool.ForEachActiveObject([&](auto object) { + NL_TEST_ASSERT(lContext.mTestSuite, false); + return true; + }); for (unsigned int i = 0; i < kNumThreads; ++i) { @@ -320,28 +330,27 @@ void TestObject::CheckConcurrency(nlTestSuite * inSuite, void * aContext) #if CHIP_SYSTEM_CONFIG_POSIX_LOCKING MultithreadedTest(inSuite, aContext, CheckConcurrencyThread); #endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING -} - -#if !CHIP_SYSTEM_CONFIG_POOL_USE_HEAP -void TestObject::CheckHighWatermarkConcurrency(nlTestSuite * inSuite, void * aContext) -{ - sPool.Reset(); -#if CHIP_SYSTEM_CONFIG_POSIX_LOCKING - for (unsigned int i = 0; i < 100; i++) - { - MultithreadedTest(inSuite, aContext, CheckHighWatermarkThread); - } -#endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING + // Clear the pool + sPool.ForEachActiveObject([&](auto object) { + object->Release(); + return true; + }); } void TestObject::CheckHighWatermark(nlTestSuite * inSuite, void * aContext) { - sPool.Reset(); - - const int kNumObjects = kPoolSize; - TestObject * lObject = nullptr; TestContext & lContext = *static_cast(aContext); + + // Pool should be empty before tests. + sPool.ForEachActiveObject([&](auto object) { + NL_TEST_ASSERT(lContext.mTestSuite, false); + return true; + }); + sPool.ResetStatistics(); + + const int kNumObjects = kPoolSize; + TestObject * lObject = nullptr; chip::System::Stats::count_t lNumInUse; chip::System::Stats::count_t lHighWatermark; @@ -349,19 +358,22 @@ void TestObject::CheckHighWatermark(nlTestSuite * inSuite, void * aContext) // increases monotonically for (int i = 0; i < kNumObjects; ++i) { - lObject = sPool.TryCreate(); + lObject = sPool.CreateObject(); - NL_TEST_ASSERT(lContext.mTestSuite, lObject->IsRetained()); + NL_TEST_ASSERT(lContext.mTestSuite, lObject->GetReferenceCount() > 0); sPool.GetStatistics(lNumInUse, lHighWatermark); +#if CHIP_SYSTEM_CONFIG_PROVIDE_STATISTICS NL_TEST_ASSERT(lContext.mTestSuite, lNumInUse == (i + 1)); NL_TEST_ASSERT(lContext.mTestSuite, lHighWatermark == lNumInUse); - +#endif lObject->Init(); } +#if !CHIP_SYSTEM_CONFIG_POOL_USE_HEAP // Fail an allocation and check that both stats don't change - NL_TEST_ASSERT(lContext.mTestSuite, sPool.TryCreate() == nullptr); + NL_TEST_ASSERT(lContext.mTestSuite, sPool.CreateObject() == nullptr); +#endif sPool.GetStatistics(lNumInUse, lHighWatermark); NL_TEST_ASSERT(lContext.mTestSuite, lNumInUse == kNumObjects); @@ -370,13 +382,18 @@ void TestObject::CheckHighWatermark(nlTestSuite * inSuite, void * aContext) // Free the last object and check that the watermark does not // change. lObject->Release(); - NL_TEST_ASSERT(lContext.mTestSuite, !lObject->IsRetained()); + NL_TEST_ASSERT(lContext.mTestSuite, lObject->GetReferenceCount() == 0); sPool.GetStatistics(lNumInUse, lHighWatermark); NL_TEST_ASSERT(lContext.mTestSuite, lNumInUse == (kNumObjects - 1)); NL_TEST_ASSERT(lContext.mTestSuite, lHighWatermark == kNumObjects); + + // Clear the pool + sPool.ForEachActiveObject([&](auto object) { + object->Release(); + return true; + }); } -#endif // !CHIP_SYSTEM_CONFIG_POOL_USE_HEAP // Test Suite @@ -389,11 +406,8 @@ static const nlTest sTests[] = NL_TEST_DEF("Iteration", TestObject::CheckIteration), NL_TEST_DEF("Retention", TestObject::CheckRetention), NL_TEST_DEF("Concurrency", TestObject::CheckConcurrency), -#if !CHIP_SYSTEM_CONFIG_POOL_USE_HEAP NL_TEST_DEF("HighWatermark", TestObject::CheckHighWatermark), - NL_TEST_DEF("HighWatermarkConcurrency", TestObject::CheckHighWatermarkConcurrency), -#endif - NL_TEST_SENTINEL() + NL_TEST_SENTINEL() }; static nlTestSuite sTestSuite = diff --git a/src/transport/raw/tests/TestTCP.cpp b/src/transport/raw/tests/TestTCP.cpp index 31324bd457a71f..550a1253343a09 100644 --- a/src/transport/raw/tests/TestTCP.cpp +++ b/src/transport/raw/tests/TestTCP.cpp @@ -29,7 +29,6 @@ #include #include #include -#include #include #include