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 <app/server-cluster/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 238 : ServerClusterInterfaceRegistry::~ServerClusterInterfaceRegistry()
31 : {
32 454 : while (mRegistrations != nullptr)
33 : {
34 216 : ServerClusterRegistration * next = mRegistrations->next;
35 216 : if (mContext.has_value())
36 : {
37 3 : mRegistrations->serverClusterInterface->Shutdown();
38 : }
39 216 : mRegistrations->next = nullptr;
40 216 : mRegistrations = next;
41 : }
42 238 : }
43 :
44 1069 : 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 1069 : VerifyOrReturnError(entry.next == nullptr, CHIP_ERROR_INVALID_ARGUMENT);
49 1068 : VerifyOrReturnError(entry.serverClusterInterface != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
50 :
51 1068 : Span<const ConcreteClusterPath> paths = entry.serverClusterInterface->GetPaths();
52 1068 : VerifyOrReturnError(!paths.empty(), CHIP_ERROR_INVALID_ARGUMENT);
53 :
54 2139 : for (const ConcreteClusterPath & path : paths)
55 : {
56 1078 : 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 1075 : VerifyOrReturnError(Get(path) == nullptr, CHIP_ERROR_DUPLICATE_KEY_ID);
61 : }
62 :
63 1061 : if (mContext.has_value())
64 : {
65 11 : ReturnErrorOnFailure(entry.serverClusterInterface->Startup(*mContext));
66 : }
67 :
68 1061 : entry.next = mRegistrations;
69 1061 : mRegistrations = &entry;
70 :
71 1061 : return CHIP_NO_ERROR;
72 : }
73 :
74 842 : CHIP_ERROR ServerClusterInterfaceRegistry::Unregister(ServerClusterInterface * what)
75 : {
76 842 : ServerClusterRegistration * prev = nullptr;
77 842 : ServerClusterRegistration * current = mRegistrations;
78 :
79 60104 : while (current != nullptr)
80 : {
81 59701 : if (current->serverClusterInterface == what)
82 : {
83 : // take the item out of the current list and return it.
84 439 : ServerClusterRegistration * next = current->next;
85 :
86 439 : if (prev == nullptr)
87 : {
88 44 : mRegistrations = next;
89 : }
90 : else
91 : {
92 395 : prev->next = next;
93 : }
94 :
95 439 : if (mCachedInterface == current->serverClusterInterface)
96 : {
97 417 : mCachedInterface = nullptr;
98 : }
99 :
100 439 : current->next = nullptr; // Make sure current does not look like part of a list.
101 439 : if (mContext.has_value())
102 : {
103 9 : current->serverClusterInterface->Shutdown();
104 : }
105 :
106 439 : return CHIP_NO_ERROR;
107 : }
108 :
109 59262 : prev = current;
110 59262 : current = current->next;
111 : }
112 :
113 403 : return CHIP_ERROR_NOT_FOUND;
114 : }
115 :
116 38438 : ServerClusterInterface * ServerClusterInterfaceRegistry::Get(const ConcreteClusterPath & clusterPath)
117 : {
118 : // Check the cache to speed things up
119 38438 : if ((mCachedInterface != nullptr) && mCachedInterface->PathsContains(clusterPath))
120 : {
121 21 : return mCachedInterface;
122 : }
123 :
124 : // The cluster searched for is not cached, do a linear search for it
125 38417 : ServerClusterRegistration * current = mRegistrations;
126 :
127 1736741 : while (current != nullptr)
128 : {
129 1699754 : if (current->serverClusterInterface->PathsContains(clusterPath))
130 : {
131 1430 : mCachedInterface = current->serverClusterInterface;
132 1430 : return mCachedInterface;
133 : }
134 :
135 1698324 : current = current->next;
136 : }
137 :
138 : // not found
139 36987 : return nullptr;
140 : }
141 :
142 164 : CHIP_ERROR ServerClusterInterfaceRegistry::SetContext(ServerClusterContext && context)
143 : {
144 164 : if (mContext.has_value())
145 : {
146 : // if there is no difference, do not re-initialize.
147 10 : VerifyOrReturnError(*mContext != context, CHIP_NO_ERROR);
148 8 : ClearContext();
149 : }
150 :
151 162 : mContext.emplace(std::move(context));
152 162 : bool had_failure = false;
153 :
154 174 : for (ServerClusterRegistration * registration = mRegistrations; registration != nullptr; registration = registration->next)
155 : {
156 12 : CHIP_ERROR err = registration->serverClusterInterface->Startup(*mContext);
157 12 : if (err != CHIP_NO_ERROR)
158 : {
159 : #if CHIP_ERROR_LOGGING
160 : // Paths MUST contain at least one element. Log the first one for identification (even though there may be more)
161 1 : const ConcreteClusterPath path = registration->serverClusterInterface->GetPaths().front();
162 1 : ChipLogError(DataManagement, "Cluster %u/" ChipLogFormatMEI " startup failed: %" CHIP_ERROR_FORMAT, path.mEndpointId,
163 : ChipLogValueMEI(path.mClusterId), err.Format());
164 : #endif
165 1 : had_failure = true;
166 : // NOTE: this makes the object be in an awkward state:
167 : // - cluster is not initialized
168 : // - mContext is valid
169 : // As a result, ::Shutdown on this cluster WILL be called even if startup failed.
170 : }
171 : }
172 :
173 162 : if (had_failure)
174 : {
175 1 : return CHIP_ERROR_HAD_FAILURES;
176 : }
177 :
178 161 : return CHIP_NO_ERROR;
179 : }
180 :
181 172 : void ServerClusterInterfaceRegistry::ClearContext()
182 : {
183 172 : if (!mContext.has_value())
184 : {
185 14 : return;
186 : }
187 168 : for (ServerClusterRegistration * registration = mRegistrations; registration != nullptr; registration = registration->next)
188 : {
189 10 : registration->serverClusterInterface->Shutdown();
190 : }
191 :
192 158 : mContext.reset();
193 : }
194 :
195 305 : ServerClusterInterfaceRegistry::ServerClusterInstances ServerClusterInterfaceRegistry::AllServerClusterInstances()
196 : {
197 305 : return { mRegistrations };
198 : }
199 :
200 : } // namespace app
201 : } // namespace chip
|