Skip to content

Commit b38c5b8

Browse files
andy31415andreilitvintehampsonbzbarsky-applerestyled-commits
authored
Add a server cluster interface registry for use by codegen. (#37850)
* Server cluster interface * ServerClusterInterfaceRegistry adding * Update src/data-model-providers/codegen/ServerClusterInterfaceRegistry.h Co-authored-by: Terence Hampson <thampson@google.com> * Code review: forward declare * Review comment: switch order of members * Updated logic: the condition is not relevant anymore * Code review: added comment * Restyle * Comment update * Updates to make callers use memory allocation * Self review: fix an include * Fix constexpr logic * Restyle * minor comment update so github is not confused about head SHA * Added useless unit test to get coverage * Restyle * Update src/app/server-cluster/ServerClusterInterface.h Co-authored-by: Boris Zbarsky <bzbarsky@apple.com> * Update src/data-model-providers/codegen/ServerClusterInterfaceRegistry.cpp Co-authored-by: Boris Zbarsky <bzbarsky@apple.com> * Update src/data-model-providers/codegen/ServerClusterInterfaceRegistry.cpp Co-authored-by: Boris Zbarsky <bzbarsky@apple.com> * Update src/data-model-providers/codegen/ServerClusterInterfaceRegistry.h Co-authored-by: Boris Zbarsky <bzbarsky@apple.com> * Update src/data-model-providers/codegen/ServerClusterInterfaceRegistry.h Co-authored-by: Boris Zbarsky <bzbarsky@apple.com> * Disallow null ptr registrations * Restyled by clang-format * Reworded based on code review --------- Co-authored-by: Andrei Litvin <andreilitvin@google.com> Co-authored-by: Terence Hampson <thampson@google.com> Co-authored-by: Boris Zbarsky <bzbarsky@apple.com> Co-authored-by: Restyled.io <commits@restyled.io>
1 parent cfcfef3 commit b38c5b8

8 files changed

+659
-10
lines changed

src/app/server-cluster/ServerClusterInterface.h

+6-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,12 @@ class ServerClusterInterface
4343
virtual ~ServerClusterInterface() = default;
4444

4545
///////////////////////////////////// Cluster Metadata Support //////////////////////////////////////////////////
46-
[[nodiscard]] virtual ClusterId GetClusterId() const = 0;
46+
47+
/// The path to this cluster instance.
48+
///
49+
/// This path (endpointid,clusterid) is expected to remain constant once the server
50+
/// cluster interface is in use.
51+
[[nodiscard]] virtual ConcreteClusterPath GetPath() const = 0;
4752

4853
/// Gets the data version for this cluster instance.
4954
///

src/app/server-cluster/tests/TestDefaultServerCluster.cpp

+19-9
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
#include <access/Privilege.h>
2020
#include <app-common/zap-generated/ids/Attributes.h>
21+
#include <app/ConcreteClusterPath.h>
2122
#include <app/data-model-provider/MetadataList.h>
2223
#include <app/data-model-provider/MetadataTypes.h>
2324
#include <app/data-model-provider/OperationTypes.h>
@@ -44,9 +45,9 @@ namespace {
4445
class FakeDefaultServerCluster : public DefaultServerCluster
4546
{
4647
public:
47-
FakeDefaultServerCluster(ClusterId id) : mClusterId(id) {}
48+
FakeDefaultServerCluster(ConcreteClusterPath path) : mPath(path) {}
4849

49-
ClusterId GetClusterId() const override { return mClusterId; }
50+
[[nodiscard]] ConcreteClusterPath GetPath() const override { return mPath; }
5051

5152
DataModel::ActionReturnStatus ReadAttribute(const DataModel::ReadAttributeRequest & request,
5253
AttributeValueEncoder & encoder) override
@@ -64,14 +65,14 @@ class FakeDefaultServerCluster : public DefaultServerCluster
6465
void TestIncreaseDataVersion() { IncreaseDataVersion(); }
6566

6667
private:
67-
ClusterId mClusterId;
68+
ConcreteClusterPath mPath;
6869
};
6970

7071
} // namespace
7172

7273
TEST(TestDefaultServerCluster, TestDataVersion)
7374
{
74-
FakeDefaultServerCluster cluster(1);
75+
FakeDefaultServerCluster cluster({ 1, 2 });
7576

7677
DataVersion v1 = cluster.GetDataVersion();
7778
cluster.TestIncreaseDataVersion();
@@ -80,13 +81,22 @@ TEST(TestDefaultServerCluster, TestDataVersion)
8081

8182
TEST(TestDefaultServerCluster, TestFlagsDefault)
8283
{
83-
FakeDefaultServerCluster cluster(1);
84+
FakeDefaultServerCluster cluster({ 1, 2 });
8485
ASSERT_EQ(cluster.GetClusterFlags().Raw(), 0u);
8586
}
8687

88+
TEST(TestDefaultServerCluster, ListWriteNotification)
89+
{
90+
FakeDefaultServerCluster cluster({ 1, 2 });
91+
92+
// this does not test anything really, except we get 100% coverage and we see that we do not crash
93+
cluster.ListAttributeWriteNotification({ 1, 2, 3 }, DataModel::ListWriteOperation::kListWriteBegin);
94+
cluster.ListAttributeWriteNotification({ 1, 2, 3 }, DataModel::ListWriteOperation::kListWriteFailure);
95+
}
96+
8797
TEST(TestDefaultServerCluster, AttributesDefault)
8898
{
89-
FakeDefaultServerCluster cluster(1);
99+
FakeDefaultServerCluster cluster({ 1, 2 });
90100

91101
DataModel::ListBuilder<AttributeEntry> attributes;
92102

@@ -114,7 +124,7 @@ TEST(TestDefaultServerCluster, AttributesDefault)
114124

115125
TEST(TestDefaultServerCluster, CommandsDefault)
116126
{
117-
FakeDefaultServerCluster cluster(1);
127+
FakeDefaultServerCluster cluster({ 1, 2 });
118128

119129
DataModel::ListBuilder<AcceptedCommandEntry> acceptedCommands;
120130
ASSERT_EQ(cluster.AcceptedCommands({ 1, 1 }, acceptedCommands), CHIP_NO_ERROR);
@@ -127,7 +137,7 @@ TEST(TestDefaultServerCluster, CommandsDefault)
127137

128138
TEST(TestDefaultServerCluster, WriteAttributeDefault)
129139
{
130-
FakeDefaultServerCluster cluster(1);
140+
FakeDefaultServerCluster cluster({ 1, 2 });
131141

132142
WriteOperation test(0 /* endpoint */, 1 /* cluster */, 1234 /* attribute */);
133143
test.SetSubjectDescriptor(kAdminSubjectDescriptor);
@@ -140,7 +150,7 @@ TEST(TestDefaultServerCluster, WriteAttributeDefault)
140150

141151
TEST(TestDefaultServerCluster, InvokeDefault)
142152
{
143-
FakeDefaultServerCluster cluster(1);
153+
FakeDefaultServerCluster cluster({ 1, 2 });
144154

145155
TLV::TLVReader tlvReader;
146156
InvokeRequest request;

src/data-model-providers/codegen/BUILD.gn

+14
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,17 @@ source_set("instance-header") {
4545
# generally being unit tests or data_model.gni/data_model.cmake files)
4646
sources = [ "Instance.h" ]
4747
}
48+
49+
source_set("registry") {
50+
sources = [
51+
"ServerClusterInterfaceRegistry.cpp",
52+
"ServerClusterInterfaceRegistry.h",
53+
]
54+
55+
public_deps = [
56+
"${chip_root}/src/app:paths",
57+
"${chip_root}/src/app/server-cluster",
58+
"${chip_root}/src/lib/core:types",
59+
"${chip_root}/src/lib/support",
60+
]
61+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
/*
2+
* Copyright (c) 2025 Project CHIP Authors
3+
* All rights reserved.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
#include <data-model-providers/codegen/ServerClusterInterfaceRegistry.h>
18+
19+
#include <app/ConcreteClusterPath.h>
20+
#include <app/server-cluster/ServerClusterInterface.h>
21+
#include <lib/core/CHIPError.h>
22+
#include <lib/core/DataModelTypes.h>
23+
#include <lib/support/CHIPMem.h>
24+
#include <lib/support/CodeUtils.h>
25+
26+
namespace chip {
27+
namespace app {
28+
29+
ServerClusterInterfaceRegistry::~ServerClusterInterfaceRegistry()
30+
{
31+
while (mRegistrations != nullptr)
32+
{
33+
ServerClusterRegistration * next = mRegistrations->next;
34+
mRegistrations->next = nullptr;
35+
mRegistrations = next;
36+
}
37+
}
38+
39+
CHIP_ERROR ServerClusterInterfaceRegistry::Register(ServerClusterRegistration & entry)
40+
{
41+
// we have no strong way to check if entry is already registered somewhere else, so we use "next" as some
42+
// form of double-check
43+
VerifyOrReturnError(entry.next == nullptr, CHIP_ERROR_INVALID_ARGUMENT);
44+
VerifyOrReturnError(entry.serverClusterInterface != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
45+
46+
ConcreteClusterPath path = entry.serverClusterInterface->GetPath();
47+
48+
VerifyOrReturnError(path.HasValidIds(), CHIP_ERROR_INVALID_ARGUMENT);
49+
50+
// Double-checking for duplicates makes the checks O(n^2) on the total number of registered
51+
// items. We preserve this however we may want to make this optional at some point in time.
52+
VerifyOrReturnError(Get(path) == nullptr, CHIP_ERROR_DUPLICATE_KEY_ID);
53+
54+
entry.next = mRegistrations;
55+
mRegistrations = &entry;
56+
57+
return CHIP_NO_ERROR;
58+
}
59+
60+
ServerClusterInterface * ServerClusterInterfaceRegistry::Unregister(const ConcreteClusterPath & path)
61+
{
62+
ServerClusterRegistration * prev = nullptr;
63+
ServerClusterRegistration * current = mRegistrations;
64+
65+
while (current != nullptr)
66+
{
67+
if (current->serverClusterInterface->GetPath() == path)
68+
{
69+
// take the item out of the current list and return it.
70+
ServerClusterRegistration * next = current->next;
71+
72+
if (prev == nullptr)
73+
{
74+
mRegistrations = next;
75+
}
76+
else
77+
{
78+
prev->next = next;
79+
}
80+
81+
if (mCachedInterface == current->serverClusterInterface)
82+
{
83+
mCachedInterface = nullptr;
84+
}
85+
86+
current->next = nullptr; // Make sure current does not look like part of a list.
87+
return current->serverClusterInterface;
88+
}
89+
90+
prev = current;
91+
current = current->next;
92+
}
93+
94+
// Not found.
95+
return nullptr;
96+
}
97+
98+
ServerClusterInterfaceRegistry::ClustersList ServerClusterInterfaceRegistry::ClustersOnEndpoint(EndpointId endpointId)
99+
{
100+
return { mRegistrations, endpointId };
101+
}
102+
103+
void ServerClusterInterfaceRegistry::UnregisterAllFromEndpoint(EndpointId endpointId)
104+
{
105+
ServerClusterRegistration * prev = nullptr;
106+
ServerClusterRegistration * current = mRegistrations;
107+
while (current != nullptr)
108+
{
109+
if (current->serverClusterInterface->GetPath().mEndpointId == endpointId)
110+
{
111+
if (mCachedInterface == current->serverClusterInterface)
112+
{
113+
mCachedInterface = nullptr;
114+
}
115+
if (prev == nullptr)
116+
{
117+
mRegistrations = current->next;
118+
}
119+
else
120+
{
121+
prev->next = current->next;
122+
}
123+
ServerClusterRegistration * actual_next = current->next;
124+
current->next = nullptr; // Make sure current does not look like part of a list.
125+
current = actual_next;
126+
}
127+
else
128+
{
129+
prev = current;
130+
current = current->next;
131+
}
132+
}
133+
}
134+
135+
ServerClusterInterface * ServerClusterInterfaceRegistry::Get(const ConcreteClusterPath & path)
136+
{
137+
// Check the cache to speed things up
138+
if ((mCachedInterface != nullptr) && (mCachedInterface->GetPath() == path))
139+
{
140+
return mCachedInterface;
141+
}
142+
143+
// The cluster searched for is not cached, do a linear search for it
144+
ServerClusterRegistration * current = mRegistrations;
145+
146+
while (current != nullptr)
147+
{
148+
if (current->serverClusterInterface->GetPath() == path)
149+
{
150+
mCachedInterface = current->serverClusterInterface;
151+
return mCachedInterface;
152+
}
153+
154+
current = current->next;
155+
}
156+
157+
// not found
158+
return nullptr;
159+
}
160+
161+
} // namespace app
162+
} // namespace chip

0 commit comments

Comments
 (0)