Skip to content

Commit f263ab7

Browse files
Split ServerClusterInterfaceRegistry into 2 classes to support both Single and Multi-endpoint paths (#40387)
* Split ServerClusterInterfaceRegistry into 2 classes to support both Single and Multi-endpoint use cases * Restyled by clang-format * Move dtor to base class * Remove codegen comment; fix minor test issue * Moved dtor back to the previous location * Address Andrei's code review suggestions: better comments;rename AllClustersList class to ServerClusterInstances * Split into separate classes * Restyled by whitespace * Restyled by clang-format --------- Co-authored-by: Restyled.io <commits@restyled.io>
1 parent cbbdfbf commit f263ab7

12 files changed

+1011
-471
lines changed

src/app/server-cluster/BUILD.gn

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ source_set("registry") {
4343
sources = [
4444
"ServerClusterInterfaceRegistry.cpp",
4545
"ServerClusterInterfaceRegistry.h",
46+
"SingleEndpointServerClusterRegistry.cpp",
47+
"SingleEndpointServerClusterRegistry.h",
4648
]
4749

4850
public_deps = [

src/app/server-cluster/ServerClusterInterfaceRegistry.cpp

Lines changed: 5 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,6 @@ CHIP_ERROR ServerClusterInterfaceRegistry::Register(ServerClusterRegistration &
5858
// Double-checking for duplicates makes the checks O(n^2) on the total number of registered
5959
// items. We preserve this however we may want to make this optional at some point in time.
6060
VerifyOrReturnError(Get(path) == nullptr, CHIP_ERROR_DUPLICATE_KEY_ID);
61-
62-
// Codegen registry requirements (so that we can support endpoint unregistration): every
63-
// path must belong to the same endpoint id.
64-
VerifyOrReturnError(path.mEndpointId == paths[0].mEndpointId, CHIP_ERROR_INVALID_ARGUMENT);
6561
}
6662

6763
if (mContext.has_value())
@@ -117,54 +113,6 @@ CHIP_ERROR ServerClusterInterfaceRegistry::Unregister(ServerClusterInterface * w
117113
return CHIP_ERROR_NOT_FOUND;
118114
}
119115

120-
ServerClusterInterfaceRegistry::ClustersList ServerClusterInterfaceRegistry::ClustersOnEndpoint(EndpointId endpointId)
121-
{
122-
return { mRegistrations, endpointId };
123-
}
124-
125-
void ServerClusterInterfaceRegistry::UnregisterAllFromEndpoint(EndpointId endpointId)
126-
{
127-
ServerClusterRegistration * prev = nullptr;
128-
ServerClusterRegistration * current = mRegistrations;
129-
while (current != nullptr)
130-
{
131-
// Requirements for Paths:
132-
// - GetPaths() MUST be non-empty
133-
// - GetPaths() MUST belong to the same endpoint
134-
// Loop below relies on that: if the endpoint matches, it can be removed
135-
auto paths = current->serverClusterInterface->GetPaths();
136-
if (paths.empty() || paths.front().mEndpointId == endpointId)
137-
{
138-
if (mCachedInterface == current->serverClusterInterface)
139-
{
140-
mCachedInterface = nullptr;
141-
}
142-
if (prev == nullptr)
143-
{
144-
mRegistrations = current->next;
145-
}
146-
else
147-
{
148-
prev->next = current->next;
149-
}
150-
ServerClusterRegistration * actual_next = current->next;
151-
152-
current->next = nullptr; // Make sure current does not look like part of a list.
153-
if (mContext.has_value())
154-
{
155-
current->serverClusterInterface->Shutdown();
156-
}
157-
158-
current = actual_next;
159-
}
160-
else
161-
{
162-
prev = current;
163-
current = current->next;
164-
}
165-
}
166-
}
167-
168116
ServerClusterInterface * ServerClusterInterfaceRegistry::Get(const ConcreteClusterPath & clusterPath)
169117
{
170118
// Check the cache to speed things up
@@ -244,5 +192,10 @@ void ServerClusterInterfaceRegistry::ClearContext()
244192
mContext.reset();
245193
}
246194

195+
ServerClusterInterfaceRegistry::ServerClusterInstances ServerClusterInterfaceRegistry::AllServerClusterInstances()
196+
{
197+
return { mRegistrations };
198+
}
199+
247200
} // namespace app
248201
} // namespace chip

src/app/server-cluster/ServerClusterInterfaceRegistry.h

Lines changed: 40 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
#include <cstdint>
2525
#include <new>
26+
#include <optional>
2627

2728
namespace chip {
2829
namespace app {
@@ -132,82 +133,12 @@ struct LazyRegisteredServerCluster
132133
class ServerClusterInterfaceRegistry
133134
{
134135
public:
135-
/// represents an iterable list of clusters
136-
class ClustersList
137-
{
138-
public:
139-
class Iterator
140-
{
141-
public:
142-
Iterator(ServerClusterRegistration * interface, EndpointId endpoint) : mEndpointId(endpoint), mRegistration(interface)
143-
{
144-
if (mRegistration != nullptr)
145-
{
146-
mSpan = interface->serverClusterInterface->GetPaths();
147-
}
148-
AdvanceUntilMatchingEndpoint();
149-
}
150-
151-
Iterator & operator++()
152-
{
153-
if (!mSpan.empty())
154-
{
155-
mSpan = mSpan.SubSpan(1);
156-
}
157-
AdvanceUntilMatchingEndpoint();
158-
return *this;
159-
}
160-
bool operator==(const Iterator & other) const { return mRegistration == other.mRegistration; }
161-
bool operator!=(const Iterator & other) const { return mRegistration != other.mRegistration; }
162-
ClusterId operator*() { return mSpan.begin()->mClusterId; }
163-
164-
private:
165-
const EndpointId mEndpointId;
166-
ServerClusterRegistration * mRegistration;
167-
Span<const ConcreteClusterPath> mSpan;
168-
169-
void AdvanceUntilMatchingEndpoint()
170-
{
171-
while (mRegistration != nullptr)
172-
{
173-
if (mSpan.empty())
174-
{
175-
mRegistration = mRegistration->next;
176-
if (mRegistration != nullptr)
177-
{
178-
mSpan = mRegistration->serverClusterInterface->GetPaths();
179-
}
180-
continue;
181-
}
182-
if (mSpan.begin()->mEndpointId == mEndpointId)
183-
{
184-
return;
185-
}
186-
187-
// need to keep searching
188-
mSpan = mSpan.SubSpan(1);
189-
}
190-
}
191-
};
192-
193-
constexpr ClustersList(ServerClusterRegistration * start, EndpointId endpointId) : mEndpointId(endpointId), mStart(start) {}
194-
Iterator begin() { return { mStart, mEndpointId }; }
195-
Iterator end() { return { nullptr, mEndpointId }; }
196-
197-
private:
198-
const EndpointId mEndpointId;
199-
ServerClusterRegistration * mStart;
200-
};
201-
202136
~ServerClusterInterfaceRegistry();
203137

204138
/// Add the given entry to the registry.
205-
/// NOTE the requirement of entries to be part of the same endpoint.
206139
///
207140
/// Requirements:
208141
/// - entry MUST NOT be part of any other registration
209-
/// - paths MUST be part of the same endpoint (requirement for codegen server cluster interface implementations)
210-
///
211142
/// - LIFETIME of entry must outlive the Registry (or entry must be unregistered)
212143
///
213144
/// There can be only a single registration for a given `endpointId/clusterId` path.
@@ -221,24 +152,52 @@ class ServerClusterInterfaceRegistry
221152
/// Return the interface registered for the given cluster path or nullptr if one does not exist
222153
ServerClusterInterface * Get(const ConcreteClusterPath & path);
223154

224-
/// Provides a list of clusters that are registered for the given endpoint.
225-
///
226-
/// ClustersList points inside the internal registrations of the registry, so
227-
/// the list is only valid as long as the registry is not modified.
228-
ClustersList ClustersOnEndpoint(EndpointId endpointId);
229-
230-
/// Unregister all registrations for the given endpoint.
231-
void UnregisterAllFromEndpoint(EndpointId endpointId);
232-
233155
// Set up the underlying context for all clusters that are managed by this registry.
234156
//
235-
// The values within context will be copied and used.
157+
// The values within context will be moved and used as-is.
236158
CHIP_ERROR SetContext(ServerClusterContext && context);
237159

238160
// Invalidates current context.
239161
void ClearContext();
240162

241-
private:
163+
// Represents an iterable list of all clusters registered in this registry.
164+
// The list is only valid as long as the registry is not modified.
165+
// The list is not guaranteed to be in any particular order.
166+
class ServerClusterInstances
167+
{
168+
public:
169+
class Iterator
170+
{
171+
public:
172+
Iterator(ServerClusterRegistration * registration) : mRegistration(registration) {}
173+
174+
Iterator & operator++()
175+
{
176+
if (mRegistration)
177+
{
178+
mRegistration = mRegistration->next;
179+
}
180+
return *this;
181+
}
182+
bool operator==(const Iterator & other) const { return mRegistration == other.mRegistration; }
183+
bool operator!=(const Iterator & other) const { return mRegistration != other.mRegistration; }
184+
ServerClusterInterface * operator*() { return mRegistration ? mRegistration->serverClusterInterface : nullptr; }
185+
186+
private:
187+
ServerClusterRegistration * mRegistration;
188+
};
189+
190+
constexpr ServerClusterInstances(ServerClusterRegistration * start) : mStart(start) {}
191+
Iterator begin() { return { mStart }; }
192+
Iterator end() { return { nullptr }; }
193+
194+
private:
195+
ServerClusterRegistration * mStart;
196+
};
197+
198+
ServerClusterInstances AllServerClusterInstances();
199+
200+
protected:
242201
ServerClusterRegistration * mRegistrations = nullptr;
243202

244203
// A one-element cache to speed up finding a cluster within an endpoint.
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
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 <app/server-cluster/SingleEndpointServerClusterRegistry.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+
#include <optional>
26+
27+
namespace chip {
28+
namespace app {
29+
30+
CHIP_ERROR SingleEndpointServerClusterRegistry::Register(ServerClusterRegistration & entry)
31+
{
32+
Span<const ConcreteClusterPath> paths = entry.serverClusterInterface->GetPaths();
33+
VerifyOrReturnError(!paths.empty(), CHIP_ERROR_INVALID_ARGUMENT);
34+
35+
// Codegen registry requirements (so that we can support endpoint unregistration): every
36+
// path must belong to the same endpoint id.
37+
for (const ConcreteClusterPath & path : paths)
38+
{
39+
VerifyOrReturnError(path.mEndpointId == paths[0].mEndpointId, CHIP_ERROR_INVALID_ARGUMENT);
40+
}
41+
42+
return ServerClusterInterfaceRegistry::Register(entry);
43+
}
44+
45+
SingleEndpointServerClusterRegistry::ClustersList SingleEndpointServerClusterRegistry::ClustersOnEndpoint(EndpointId endpointId)
46+
{
47+
return { mRegistrations, endpointId };
48+
}
49+
50+
void SingleEndpointServerClusterRegistry::UnregisterAllFromEndpoint(EndpointId endpointId)
51+
{
52+
ServerClusterRegistration * prev = nullptr;
53+
ServerClusterRegistration * current = mRegistrations;
54+
while (current != nullptr)
55+
{
56+
// Requirements for Paths:
57+
// - GetPaths() MUST be non-empty
58+
// - GetPaths() MUST belong to the same endpoint
59+
// Loop below relies on that: if the endpoint matches, it can be removed
60+
auto paths = current->serverClusterInterface->GetPaths();
61+
if (paths.empty() || paths.front().mEndpointId == endpointId)
62+
{
63+
if (mCachedInterface == current->serverClusterInterface)
64+
{
65+
mCachedInterface = nullptr;
66+
}
67+
if (prev == nullptr)
68+
{
69+
mRegistrations = current->next;
70+
}
71+
else
72+
{
73+
prev->next = current->next;
74+
}
75+
ServerClusterRegistration * actual_next = current->next;
76+
77+
current->next = nullptr; // Make sure current does not look like part of a list.
78+
if (mContext.has_value())
79+
{
80+
current->serverClusterInterface->Shutdown();
81+
}
82+
83+
current = actual_next;
84+
}
85+
else
86+
{
87+
prev = current;
88+
current = current->next;
89+
}
90+
}
91+
}
92+
93+
} // namespace app
94+
} // namespace chip

0 commit comments

Comments
 (0)