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 169 : ServerClusterInterfaceRegistry::~ServerClusterInterfaceRegistry()
31 : {
32 373 : while (mRegistrations != nullptr)
33 : {
34 204 : ServerClusterRegistration * next = mRegistrations->next;
35 204 : if (mContext.has_value())
36 : {
37 2 : mRegistrations->serverClusterInterface->Shutdown();
38 : }
39 204 : mRegistrations->next = nullptr;
40 204 : mRegistrations = next;
41 : }
42 169 : }
43 :
44 1015 : 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 1015 : VerifyOrReturnError(entry.next == nullptr, CHIP_ERROR_INVALID_ARGUMENT);
49 1014 : VerifyOrReturnError(entry.serverClusterInterface != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
50 :
51 1014 : ConcreteClusterPath path = entry.serverClusterInterface->GetPath();
52 :
53 1014 : VerifyOrReturnError(path.HasValidIds(), CHIP_ERROR_INVALID_ARGUMENT);
54 :
55 : // Double-checking for duplicates makes the checks O(n^2) on the total number of registered
56 : // items. We preserve this however we may want to make this optional at some point in time.
57 1012 : VerifyOrReturnError(Get(path) == nullptr, CHIP_ERROR_DUPLICATE_KEY_ID);
58 :
59 1011 : if (mContext.has_value())
60 : {
61 4 : ReturnErrorOnFailure(entry.serverClusterInterface->Startup(*mContext));
62 : }
63 :
64 1011 : entry.next = mRegistrations;
65 1011 : mRegistrations = &entry;
66 :
67 1011 : return CHIP_NO_ERROR;
68 : }
69 :
70 805 : ServerClusterInterface * ServerClusterInterfaceRegistry::Unregister(const ConcreteClusterPath & path)
71 : {
72 805 : ServerClusterRegistration * prev = nullptr;
73 805 : ServerClusterRegistration * current = mRegistrations;
74 :
75 60062 : while (current != nullptr)
76 : {
77 59661 : if (current->serverClusterInterface->GetPath() == path)
78 : {
79 : // take the item out of the current list and return it.
80 404 : ServerClusterRegistration * next = current->next;
81 :
82 404 : if (prev == nullptr)
83 : {
84 12 : mRegistrations = next;
85 : }
86 : else
87 : {
88 392 : prev->next = next;
89 : }
90 :
91 404 : if (mCachedInterface == current->serverClusterInterface)
92 : {
93 401 : mCachedInterface = nullptr;
94 : }
95 :
96 404 : current->next = nullptr; // Make sure current does not look like part of a list.
97 404 : if (mContext.has_value())
98 : {
99 3 : current->serverClusterInterface->Shutdown();
100 : }
101 :
102 404 : return current->serverClusterInterface;
103 : }
104 :
105 59257 : prev = current;
106 59257 : current = current->next;
107 : }
108 :
109 : // Not found.
110 401 : return nullptr;
111 : }
112 :
113 16901 : ServerClusterInterfaceRegistry::ClustersList ServerClusterInterfaceRegistry::ClustersOnEndpoint(EndpointId endpointId)
114 : {
115 16901 : return { mRegistrations, endpointId };
116 : }
117 :
118 22 : void ServerClusterInterfaceRegistry::UnregisterAllFromEndpoint(EndpointId endpointId)
119 : {
120 22 : ServerClusterRegistration * prev = nullptr;
121 22 : ServerClusterRegistration * current = mRegistrations;
122 2172 : while (current != nullptr)
123 : {
124 2150 : if (current->serverClusterInterface->GetPath().mEndpointId == endpointId)
125 : {
126 403 : if (mCachedInterface == current->serverClusterInterface)
127 : {
128 4 : mCachedInterface = nullptr;
129 : }
130 403 : if (prev == nullptr)
131 : {
132 35 : mRegistrations = current->next;
133 : }
134 : else
135 : {
136 368 : prev->next = current->next;
137 : }
138 403 : ServerClusterRegistration * actual_next = current->next;
139 :
140 403 : current->next = nullptr; // Make sure current does not look like part of a list.
141 403 : if (mContext.has_value())
142 : {
143 0 : current->serverClusterInterface->Shutdown();
144 : }
145 :
146 403 : current = actual_next;
147 : }
148 : else
149 : {
150 1747 : prev = current;
151 1747 : current = current->next;
152 : }
153 : }
154 22 : }
155 :
156 37236 : ServerClusterInterface * ServerClusterInterfaceRegistry::Get(const ConcreteClusterPath & path)
157 : {
158 : // Check the cache to speed things up
159 37236 : if ((mCachedInterface != nullptr) && (mCachedInterface->GetPath() == path))
160 : {
161 6 : return mCachedInterface;
162 : }
163 :
164 : // The cluster searched for is not cached, do a linear search for it
165 37230 : ServerClusterRegistration * current = mRegistrations;
166 :
167 1715626 : while (current != nullptr)
168 : {
169 1679603 : if (current->serverClusterInterface->GetPath() == path)
170 : {
171 1207 : mCachedInterface = current->serverClusterInterface;
172 1207 : return mCachedInterface;
173 : }
174 :
175 1678396 : current = current->next;
176 : }
177 :
178 : // not found
179 36023 : return nullptr;
180 : }
181 :
182 398 : CHIP_ERROR ServerClusterInterfaceRegistry::SetContext(ServerClusterContext && context)
183 : {
184 398 : if (mContext.has_value())
185 : {
186 : // if there is no difference, do not re-initialize.
187 227 : VerifyOrReturnError(*mContext != context, CHIP_NO_ERROR);
188 3 : ClearContext();
189 : }
190 :
191 174 : mContext.emplace(std::move(context));
192 174 : bool had_failure = false;
193 :
194 181 : for (ServerClusterRegistration * registration = mRegistrations; registration != nullptr; registration = registration->next)
195 : {
196 7 : CHIP_ERROR err = registration->serverClusterInterface->Startup(*mContext);
197 7 : if (err != CHIP_NO_ERROR)
198 : {
199 : #if CHIP_ERROR_LOGGING
200 1 : const ConcreteClusterPath path = registration->serverClusterInterface->GetPath();
201 1 : ChipLogError(DataManagement, "Cluster %u/" ChipLogFormatMEI " startup failed: %" CHIP_ERROR_FORMAT, path.mEndpointId,
202 : ChipLogValueMEI(path.mClusterId), err.Format());
203 : #endif
204 1 : had_failure = true;
205 : // NOTE: this makes the object be in an awkward state:
206 : // - cluster is not initialized
207 : // - mContext is valid
208 : // As a result, ::Shutdown on this cluster WILL be called even if startup failed.
209 : }
210 : }
211 :
212 174 : if (had_failure)
213 : {
214 1 : return CHIP_ERROR_HAD_FAILURES;
215 : }
216 :
217 173 : return CHIP_NO_ERROR;
218 : }
219 :
220 162 : void ServerClusterInterfaceRegistry::ClearContext()
221 : {
222 162 : if (!mContext.has_value())
223 : {
224 4 : return;
225 : }
226 164 : for (ServerClusterRegistration * registration = mRegistrations; registration != nullptr; registration = registration->next)
227 : {
228 6 : registration->serverClusterInterface->Shutdown();
229 : }
230 :
231 158 : mContext.reset();
232 : }
233 :
234 : } // namespace app
235 : } // namespace chip
|