From 75c7879da9e864d9f0bc9af807bb3352dcba9ece Mon Sep 17 00:00:00 2001 From: Andrei Litvin Date: Tue, 25 Feb 2025 14:47:12 -0500 Subject: [PATCH] Add a "List clusters on endpoint" support into the server cluster registry as this seems very useful --- .../ServerClusterInterfaceRegistry.cpp | 15 +++++- .../ServerClusterInterfaceRegistry.h | 36 +++++++++++++ .../TestServerClusterInterfaceRegistry.cpp | 53 +++++++++++++++++++ 3 files changed, 103 insertions(+), 1 deletion(-) diff --git a/src/app/server-cluster/ServerClusterInterfaceRegistry.cpp b/src/app/server-cluster/ServerClusterInterfaceRegistry.cpp index a36812644085a8..bbc31687a8d4f5 100644 --- a/src/app/server-cluster/ServerClusterInterfaceRegistry.cpp +++ b/src/app/server-cluster/ServerClusterInterfaceRegistry.cpp @@ -40,11 +40,12 @@ void ClearSingleLinkedList(ServerClusterInterface * clusters) } } +ServerClusterInterfaceRegistry sRegistry; + } // namespace ServerClusterInterfaceRegistry & ServerClusterInterfaceRegistry::Instance() { - static ServerClusterInterfaceRegistry sRegistry; return sRegistry; } @@ -155,6 +156,18 @@ ServerClusterInterface * ServerClusterInterfaceRegistry::Unregister(const Concre return nullptr; } +ServerClusterInterfaceRegistry::ClustersList ServerClusterInterfaceRegistry::ClustersOnEndpoint(EndpointId endpointId) +{ + EndpointClusters * clusters = FindClusters(endpointId); + + if (clusters == nullptr) + { + return nullptr; + } + + return clusters->firstCluster; +} + void ServerClusterInterfaceRegistry::UnregisterAllFromEndpoint(EndpointId endpointId) { if ((mEndpointClustersCache != nullptr) && (mEndpointClustersCache->endpointId == endpointId)) diff --git a/src/app/server-cluster/ServerClusterInterfaceRegistry.h b/src/app/server-cluster/ServerClusterInterfaceRegistry.h index 46533070ec2613..cb8ab8038956ed 100644 --- a/src/app/server-cluster/ServerClusterInterfaceRegistry.h +++ b/src/app/server-cluster/ServerClusterInterfaceRegistry.h @@ -28,6 +28,39 @@ namespace app { class ServerClusterInterfaceRegistry { public: + /// represents an iterable list of clusters + class ClustersList + { + public: + class Iterator + { + public: + Iterator(ServerClusterInterface * interface) : mInterface(interface) {} + + Iterator & operator++() + { + if (mInterface != nullptr) + { + mInterface = mInterface->GetNextListItem(); + } + return *this; + } + bool operator==(const Iterator & other) const { return mInterface == other.mInterface; } + bool operator!=(const Iterator & other) const { return mInterface != other.mInterface; } + ServerClusterInterface * operator*() { return mInterface; } + + private: + ServerClusterInterface * mInterface; + }; + + ClustersList(ServerClusterInterface * start) : mStart(start) {} + Iterator begin() { return mStart; } + Iterator end() { return nullptr; } + + private: + ServerClusterInterface * mStart; + }; + ~ServerClusterInterfaceRegistry(); /// Associate a specific interface with the given endpoint. @@ -51,6 +84,9 @@ class ServerClusterInterfaceRegistry /// Return the interface registered for the given cluster path or nullptr if one does not exist ServerClusterInterface * Get(const ConcreteClusterPath & path); + /// ClustersList is valid as a snapshot only and should not be saved. + ClustersList ClustersOnEndpoint(EndpointId endpointId); + /// Unregister all registrations for the given endpoint. void UnregisterAllFromEndpoint(EndpointId endpointId); diff --git a/src/app/server-cluster/tests/TestServerClusterInterfaceRegistry.cpp b/src/app/server-cluster/tests/TestServerClusterInterfaceRegistry.cpp index b4e2e8e9a79446..d441699ff05840 100644 --- a/src/app/server-cluster/tests/TestServerClusterInterfaceRegistry.cpp +++ b/src/app/server-cluster/tests/TestServerClusterInterfaceRegistry.cpp @@ -299,6 +299,59 @@ TEST_F(TestServerClusterInterfaceRegistry, TestDestructionOrder) } } +TEST_F(TestServerClusterInterfaceRegistry, ClustersOnEndpoint) +{ + std::vector items; + + static constexpr ClusterId kClusterTestCount = 200; + static constexpr EndpointId kEndpointTestCount = 10; + + static_assert(kInvalidClusterId > kClusterTestCount, "Tests assume all clusters IDs [0...] are valid"); + + items.reserve(kClusterTestCount); + for (ClusterId i = 0; i < kClusterTestCount; i++) + { + items.emplace_back(i); + ASSERT_FALSE(items[i].IsInList()); + } + + ServerClusterInterfaceRegistry registry; + + // place the clusters on the respecitve endpoints + for (ClusterId i = 0; i < kClusterTestCount; i++) + { + ASSERT_EQ(registry.Register(i % kEndpointTestCount, &items[i]), CHIP_NO_ERROR); + ASSERT_TRUE(items[i].IsInList()); + } + + // this IS implementation defined: we always register at "HEAD" so the listing is in + // INVERSE order of registering. + for (EndpointId ep = 0; ep < kEndpointTestCount; ep++) + { + // Move to the end since we iterate in reverse order + ClusterId expectedClusterId = ep + kEndpointTestCount * (kClusterTestCount / kEndpointTestCount); + if (expectedClusterId >= kClusterTestCount) + { + expectedClusterId -= kEndpointTestCount; + } + + // ensure that iteration happens exactly as we expect: reverse order and complete + for (auto cluster : registry.ClustersOnEndpoint(ep)) + { + ASSERT_LT(expectedClusterId, kClusterTestCount); + ASSERT_EQ(cluster->GetClusterId(), expectedClusterId); + expectedClusterId -= kEndpointTestCount; // next expected/registered cluster + } + + // Iterated through all : we overflowed and got a large number + ASSERT_GE(expectedClusterId, kClusterTestCount); + } + + // invalid index works and iteration on empty lists is ok + auto clusters = registry.ClustersOnEndpoint(kEndpointTestCount + 1); + ASSERT_EQ(clusters.begin(), clusters.end()); +} + TEST_F(TestServerClusterInterfaceRegistry, InstanceUsage) { // This tests uses the instance member, to get coverage