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 187 : ServerClusterInterfaceRegistry::~ServerClusterInterfaceRegistry()
31 : {
32 403 : 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 187 : }
43 :
44 1041 : 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 1041 : VerifyOrReturnError(entry.next == nullptr, CHIP_ERROR_INVALID_ARGUMENT);
49 1040 : VerifyOrReturnError(entry.serverClusterInterface != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
50 :
51 1040 : Span<const ConcreteClusterPath> paths = entry.serverClusterInterface->GetPaths();
52 1040 : VerifyOrReturnError(!paths.empty(), CHIP_ERROR_INVALID_ARGUMENT);
53 :
54 2081 : for (const ConcreteClusterPath & path : paths)
55 : {
56 1047 : 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 1044 : VerifyOrReturnError(Get(path) == nullptr, CHIP_ERROR_DUPLICATE_KEY_ID);
61 : }
62 :
63 1034 : if (mContext.has_value())
64 : {
65 9 : ReturnErrorOnFailure(entry.serverClusterInterface->Startup(*mContext));
66 : }
67 :
68 1034 : entry.next = mRegistrations;
69 1034 : mRegistrations = &entry;
70 :
71 1034 : return CHIP_NO_ERROR;
72 : }
73 :
74 815 : CHIP_ERROR ServerClusterInterfaceRegistry::Unregister(ServerClusterInterface * what)
75 : {
76 815 : ServerClusterRegistration * prev = nullptr;
77 815 : ServerClusterRegistration * current = mRegistrations;
78 :
79 60077 : while (current != nullptr)
80 : {
81 59674 : if (current->serverClusterInterface == what)
82 : {
83 : // take the item out of the current list and return it.
84 412 : ServerClusterRegistration * next = current->next;
85 :
86 412 : if (prev == nullptr)
87 : {
88 17 : mRegistrations = next;
89 : }
90 : else
91 : {
92 395 : prev->next = next;
93 : }
94 :
95 412 : if (mCachedInterface == current->serverClusterInterface)
96 : {
97 407 : mCachedInterface = nullptr;
98 : }
99 :
100 412 : current->next = nullptr; // Make sure current does not look like part of a list.
101 412 : if (mContext.has_value())
102 : {
103 7 : current->serverClusterInterface->Shutdown();
104 : }
105 :
106 412 : 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 45312 : ServerClusterInterface * ServerClusterInterfaceRegistry::Get(const ConcreteClusterPath & clusterPath)
117 : {
118 : // Check the cache to speed things up
119 45312 : if ((mCachedInterface != nullptr) && mCachedInterface->PathsContains(clusterPath))
120 : {
121 18 : return mCachedInterface;
122 : }
123 :
124 : // The cluster searched for is not cached, do a linear search for it
125 45294 : ServerClusterRegistration * current = mRegistrations;
126 :
127 1743614 : while (current != nullptr)
128 : {
129 1699740 : if (current->serverClusterInterface->PathsContains(clusterPath))
130 : {
131 1420 : mCachedInterface = current->serverClusterInterface;
132 1420 : return mCachedInterface;
133 : }
134 :
135 1698320 : current = current->next;
136 : }
137 :
138 : // not found
139 43874 : return nullptr;
140 : }
141 :
142 161 : CHIP_ERROR ServerClusterInterfaceRegistry::SetContext(ServerClusterContext && context)
143 : {
144 161 : if (mContext.has_value())
145 : {
146 : // if there is no difference, do not re-initialize.
147 8 : VerifyOrReturnError(*mContext != context, CHIP_NO_ERROR);
148 6 : ClearContext();
149 : }
150 :
151 159 : mContext.emplace(std::move(context));
152 159 : bool had_failure = false;
153 :
154 171 : 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 159 : if (had_failure)
174 : {
175 1 : return CHIP_ERROR_HAD_FAILURES;
176 : }
177 :
178 158 : return CHIP_NO_ERROR;
179 : }
180 :
181 173 : void ServerClusterInterfaceRegistry::ClearContext()
182 : {
183 173 : if (!mContext.has_value())
184 : {
185 18 : return;
186 : }
187 165 : for (ServerClusterRegistration * registration = mRegistrations; registration != nullptr; registration = registration->next)
188 : {
189 10 : registration->serverClusterInterface->Shutdown();
190 : }
191 :
192 155 : mContext.reset();
193 : }
194 :
195 5 : ServerClusterInterfaceRegistry::ServerClusterInstances ServerClusterInterfaceRegistry::AllServerClusterInstances()
196 : {
197 5 : return { mRegistrations };
198 : }
199 :
200 : } // namespace app
201 : } // namespace chip
|