Line data Source code
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 : #include <optional>
26 :
27 : namespace chip {
28 : namespace app {
29 :
30 172 : ServerClusterInterfaceRegistry::~ServerClusterInterfaceRegistry()
31 : {
32 375 : while (mRegistrations != nullptr)
33 : {
34 203 : ServerClusterRegistration * next = mRegistrations->next;
35 203 : if (mContext.has_value())
36 : {
37 1 : mRegistrations->serverClusterInterface->Shutdown();
38 : }
39 203 : mRegistrations->next = nullptr;
40 203 : mRegistrations = next;
41 : }
42 172 : }
43 :
44 1018 : CHIP_ERROR ServerClusterInterfaceRegistry::Register(ServerClusterRegistration & entry)
45 : {
46 : // we have no strong way to check if entry is already registered somewhere else, so we use "next" as some
47 : // form of double-check
48 1018 : VerifyOrReturnError(entry.next == nullptr, CHIP_ERROR_INVALID_ARGUMENT);
49 1017 : VerifyOrReturnError(entry.serverClusterInterface != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
50 :
51 1017 : Span<const ConcreteClusterPath> paths = entry.serverClusterInterface->GetPaths();
52 1017 : VerifyOrReturnError(!paths.empty(), CHIP_ERROR_INVALID_ARGUMENT);
53 :
54 2035 : for (const ConcreteClusterPath & path : paths)
55 : {
56 1023 : VerifyOrReturnError(path.HasValidIds(), CHIP_ERROR_INVALID_ARGUMENT);
57 :
58 : // Double-checking for duplicates makes the checks O(n^2) on the total number of registered
59 : // items. We preserve this however we may want to make this optional at some point in time.
60 1021 : 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 1020 : VerifyOrReturnError(path.mEndpointId == paths[0].mEndpointId, CHIP_ERROR_INVALID_ARGUMENT);
65 : }
66 :
67 1012 : if (mContext.has_value())
68 : {
69 4 : ReturnErrorOnFailure(entry.serverClusterInterface->Startup(*mContext));
70 : }
71 :
72 1012 : entry.next = mRegistrations;
73 1012 : mRegistrations = &entry;
74 :
75 1012 : return CHIP_NO_ERROR;
76 : }
77 :
78 806 : CHIP_ERROR ServerClusterInterfaceRegistry::Unregister(ServerClusterInterface * what)
79 : {
80 806 : ServerClusterRegistration * prev = nullptr;
81 806 : ServerClusterRegistration * current = mRegistrations;
82 :
83 60063 : while (current != nullptr)
84 : {
85 59662 : if (current->serverClusterInterface == what)
86 : {
87 : // take the item out of the current list and return it.
88 405 : ServerClusterRegistration * next = current->next;
89 :
90 405 : if (prev == nullptr)
91 : {
92 13 : mRegistrations = next;
93 : }
94 : else
95 : {
96 392 : prev->next = next;
97 : }
98 :
99 405 : if (mCachedInterface == current->serverClusterInterface)
100 : {
101 403 : mCachedInterface = nullptr;
102 : }
103 :
104 405 : current->next = nullptr; // Make sure current does not look like part of a list.
105 405 : if (mContext.has_value())
106 : {
107 3 : current->serverClusterInterface->Shutdown();
108 : }
109 :
110 405 : return CHIP_NO_ERROR;
111 : }
112 :
113 59257 : prev = current;
114 59257 : current = current->next;
115 : }
116 :
117 401 : return CHIP_ERROR_NOT_FOUND;
118 : }
119 :
120 16920 : ServerClusterInterfaceRegistry::ClustersList ServerClusterInterfaceRegistry::ClustersOnEndpoint(EndpointId endpointId)
121 : {
122 16920 : return { mRegistrations, endpointId };
123 : }
124 :
125 23 : void ServerClusterInterfaceRegistry::UnregisterAllFromEndpoint(EndpointId endpointId)
126 : {
127 23 : ServerClusterRegistration * prev = nullptr;
128 23 : ServerClusterRegistration * current = mRegistrations;
129 2175 : 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 2152 : auto paths = current->serverClusterInterface->GetPaths();
136 2152 : if (paths.empty() || paths.front().mEndpointId == endpointId)
137 : {
138 404 : if (mCachedInterface == current->serverClusterInterface)
139 : {
140 4 : mCachedInterface = nullptr;
141 : }
142 404 : if (prev == nullptr)
143 : {
144 35 : mRegistrations = current->next;
145 : }
146 : else
147 : {
148 369 : prev->next = current->next;
149 : }
150 404 : ServerClusterRegistration * actual_next = current->next;
151 :
152 404 : current->next = nullptr; // Make sure current does not look like part of a list.
153 404 : if (mContext.has_value())
154 : {
155 1 : current->serverClusterInterface->Shutdown();
156 : }
157 :
158 404 : current = actual_next;
159 : }
160 : else
161 : {
162 1748 : prev = current;
163 1748 : current = current->next;
164 : }
165 : }
166 23 : }
167 :
168 38144 : ServerClusterInterface * ServerClusterInterfaceRegistry::Get(const ConcreteClusterPath & clusterPath)
169 : {
170 : // Check the cache to speed things up
171 38144 : if ((mCachedInterface != nullptr) && mCachedInterface->PathsContains(clusterPath))
172 : {
173 9 : return mCachedInterface;
174 : }
175 :
176 : // The cluster searched for is not cached, do a linear search for it
177 38135 : ServerClusterRegistration * current = mRegistrations;
178 :
179 1736435 : while (current != nullptr)
180 : {
181 1699709 : if (current->serverClusterInterface->PathsContains(clusterPath))
182 : {
183 1409 : mCachedInterface = current->serverClusterInterface;
184 1409 : return mCachedInterface;
185 : }
186 :
187 1698300 : current = current->next;
188 : }
189 :
190 : // not found
191 36726 : return nullptr;
192 : }
193 :
194 400 : CHIP_ERROR ServerClusterInterfaceRegistry::SetContext(ServerClusterContext && context)
195 : {
196 400 : if (mContext.has_value())
197 : {
198 : // if there is no difference, do not re-initialize.
199 229 : VerifyOrReturnError(*mContext != context, CHIP_NO_ERROR);
200 3 : ClearContext();
201 : }
202 :
203 174 : mContext.emplace(std::move(context));
204 174 : bool had_failure = false;
205 :
206 181 : for (ServerClusterRegistration * registration = mRegistrations; registration != nullptr; registration = registration->next)
207 : {
208 7 : CHIP_ERROR err = registration->serverClusterInterface->Startup(*mContext);
209 7 : if (err != CHIP_NO_ERROR)
210 : {
211 : #if CHIP_ERROR_LOGGING
212 : // Paths MUST contain at least one element. Log the first one for identification (even though there may be more)
213 1 : const ConcreteClusterPath path = registration->serverClusterInterface->GetPaths().front();
214 1 : ChipLogError(DataManagement, "Cluster %u/" ChipLogFormatMEI " startup failed: %" CHIP_ERROR_FORMAT, path.mEndpointId,
215 : ChipLogValueMEI(path.mClusterId), err.Format());
216 : #endif
217 1 : had_failure = true;
218 : // NOTE: this makes the object be in an awkward state:
219 : // - cluster is not initialized
220 : // - mContext is valid
221 : // As a result, ::Shutdown on this cluster WILL be called even if startup failed.
222 : }
223 : }
224 :
225 174 : if (had_failure)
226 : {
227 1 : return CHIP_ERROR_HAD_FAILURES;
228 : }
229 :
230 173 : return CHIP_NO_ERROR;
231 : }
232 :
233 162 : void ServerClusterInterfaceRegistry::ClearContext()
234 : {
235 162 : if (!mContext.has_value())
236 : {
237 4 : return;
238 : }
239 164 : for (ServerClusterRegistration * registration = mRegistrations; registration != nullptr; registration = registration->next)
240 : {
241 6 : registration->serverClusterInterface->Shutdown();
242 : }
243 :
244 158 : mContext.reset();
245 : }
246 :
247 : } // namespace app
248 : } // namespace chip
|