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 : #pragma once
18 :
19 : #include <app/AppConfig.h>
20 : #include <app/ConcreteClusterPath.h>
21 : #include <app/server-cluster/ServerClusterInterface.h>
22 : #include <lib/core/CHIPError.h>
23 : #include <lib/core/DataModelTypes.h>
24 : #include <lib/support/logging/CHIPLogging.h>
25 :
26 : #include <cstdint>
27 : #include <new>
28 : #include <optional>
29 :
30 : namespace chip {
31 : namespace app {
32 :
33 : /// Represents an entry in the server cluster interface registry for
34 : /// a specific interface.
35 : ///
36 : /// In practice this is a single-linked list element.
37 : struct ServerClusterRegistration
38 : {
39 : // A single-linked list of clusters registered for the given `endpointId`
40 : ServerClusterInterface * const serverClusterInterface;
41 : ServerClusterRegistration * next;
42 :
43 493 : constexpr ServerClusterRegistration(ServerClusterInterface & interface, ServerClusterRegistration * next_item = nullptr) :
44 493 : serverClusterInterface(&interface), next(next_item)
45 493 : {}
46 : ServerClusterRegistration(ServerClusterRegistration && other) = default;
47 :
48 : // we generally do not want to allow copies as those may have different "next" entries.
49 : ServerClusterRegistration(const ServerClusterRegistration & other) = delete;
50 : ServerClusterRegistration & operator=(const ServerClusterRegistration & other) = delete;
51 : };
52 :
53 : /// It is very typical to join together a registration and a Server
54 : /// This templates makes this registration somewhat easier/standardized.
55 : template <typename SERVER_CLUSTER>
56 : struct RegisteredServerCluster
57 : {
58 : template <typename... Args>
59 26 : RegisteredServerCluster(Args &&... args) : cluster(std::forward<Args>(args)...), registration(cluster)
60 26 : {}
61 :
62 26 : [[nodiscard]] constexpr ServerClusterRegistration & Registration() { return registration; }
63 : [[nodiscard]] constexpr const ServerClusterRegistration & Registration() const { return registration; }
64 :
65 71 : [[nodiscard]] constexpr SERVER_CLUSTER & Cluster() { return cluster; }
66 30 : [[nodiscard]] constexpr const SERVER_CLUSTER & Cluster() const { return cluster; }
67 :
68 : private:
69 : SERVER_CLUSTER cluster;
70 : ServerClusterRegistration registration;
71 : };
72 :
73 : /// Lazy-construction of a RegisteredServerCluster to allow at-runtime lifetime management
74 : ///
75 : /// If using this class, manamement of Create/Destroy MUST be done correctly.
76 : template <typename SERVER_CLUSTER>
77 : struct LazyRegisteredServerCluster
78 : {
79 : public:
80 10 : constexpr LazyRegisteredServerCluster() = default;
81 11 : ~LazyRegisteredServerCluster()
82 : {
83 11 : if (IsConstructed())
84 : {
85 4 : Destroy();
86 : }
87 11 : }
88 :
89 11 : void Destroy()
90 : {
91 11 : if (!IsConstructed())
92 : {
93 : // Should not happen — Init/Shutdown are paired at the CodegenDataModelProvider level.
94 : #if CHIP_CONFIG_ENABLE_SERVER_RESTART_SUPPORT
95 0 : ChipLogError(AppServer, "Destroy() called on already-destroyed cluster — this should not happen");
96 : #else
97 : chipDie();
98 : #endif
99 0 : return;
100 : }
101 11 : Registration().~ServerClusterRegistration();
102 11 : memset(mRegistration, 0, sizeof(mRegistration));
103 :
104 11 : Cluster().~SERVER_CLUSTER();
105 11 : memset(mCluster, 0, sizeof(mCluster));
106 : }
107 :
108 : template <typename... Args>
109 11 : void Create(Args &&... args)
110 : {
111 11 : VerifyOrDie(!IsConstructed());
112 :
113 11 : new (mCluster) SERVER_CLUSTER(std::forward<Args>(args)...);
114 11 : new (mRegistration) ServerClusterRegistration(Cluster());
115 11 : }
116 :
117 79 : [[nodiscard]] constexpr bool IsConstructed() const
118 : {
119 : // mRegistration is supposed to containt a serverClusterInterface that is NOT null
120 : // so we check for non-zero content. This relies that nullptr is 0
121 79 : return Registration().serverClusterInterface != nullptr;
122 : }
123 :
124 22 : [[nodiscard]] constexpr ServerClusterRegistration & Registration()
125 : {
126 22 : return *std::launder(reinterpret_cast<ServerClusterRegistration *>(mRegistration));
127 : }
128 :
129 79 : [[nodiscard]] constexpr const ServerClusterRegistration & Registration() const
130 : {
131 79 : return *std::launder(reinterpret_cast<const ServerClusterRegistration *>(mRegistration));
132 : }
133 :
134 50 : [[nodiscard]] constexpr SERVER_CLUSTER & Cluster() { return *std::launder(reinterpret_cast<SERVER_CLUSTER *>(mCluster)); }
135 :
136 4 : [[nodiscard]] constexpr const SERVER_CLUSTER & Cluster() const
137 : {
138 4 : return *std::launder(reinterpret_cast<const SERVER_CLUSTER *>(mCluster));
139 : }
140 :
141 : private:
142 : alignas(SERVER_CLUSTER) uint8_t mCluster[sizeof(SERVER_CLUSTER)] = { 0 };
143 : alignas(ServerClusterRegistration) uint8_t mRegistration[sizeof(ServerClusterRegistration)] = { 0 };
144 : };
145 :
146 : /// Allows registering and retrieving ServerClusterInterface instances for specific cluster paths.
147 : class ServerClusterInterfaceRegistry
148 : {
149 : public:
150 : ~ServerClusterInterfaceRegistry();
151 :
152 : /// Add the given entry to the registry.
153 : ///
154 : /// Requirements:
155 : /// - entry MUST NOT be part of any other registration
156 : /// - LIFETIME of entry must outlive the Registry (or entry must be unregistered)
157 : ///
158 : /// There can be only a single registration for a given `endpointId/clusterId` path.
159 : [[nodiscard]] CHIP_ERROR Register(ServerClusterRegistration & entry);
160 :
161 : /// Remove an existing registration
162 : ///
163 : /// Will return CHIP_ERROR_NOT_FOUND if the given registration is not found.
164 : CHIP_ERROR Unregister(ServerClusterInterface *,
165 : ClusterShutdownType clusterShutdownType = ClusterShutdownType::kClusterShutdown);
166 :
167 : /// Return the interface registered for the given cluster path or nullptr if one does not exist
168 : ServerClusterInterface * Get(const ConcreteClusterPath & path);
169 :
170 : // Set up the underlying context for all clusters that are managed by this registry.
171 : //
172 : // The values within context will be moved and used as-is.
173 : //
174 : // Returns:
175 : // - CHIP_NO_ERROR on success
176 : // - CHIP_ERROR_HAD_FAILURES if some cluster `Startup` calls had errors (Startup
177 : // will be called for all clusters).
178 : CHIP_ERROR SetContext(ServerClusterContext && context);
179 :
180 : // Invalidates current context.
181 : void ClearContext();
182 :
183 : // Represents an iterable list of all clusters registered in this registry.
184 : // The list is only valid as long as the registry is not modified.
185 : // The list is not guaranteed to be in any particular order.
186 : class ServerClusterInstances
187 : {
188 : public:
189 : class Iterator
190 : {
191 : public:
192 438 : Iterator(ServerClusterRegistration * registration) : mRegistration(registration) {}
193 :
194 75 : Iterator & operator++()
195 : {
196 75 : if (mRegistration)
197 : {
198 75 : mRegistration = mRegistration->next;
199 : }
200 75 : return *this;
201 : }
202 : bool operator==(const Iterator & other) const { return mRegistration == other.mRegistration; }
203 283 : bool operator!=(const Iterator & other) const { return mRegistration != other.mRegistration; }
204 97 : ServerClusterInterface * operator*() { return mRegistration ? mRegistration->serverClusterInterface : nullptr; }
205 :
206 : private:
207 : ServerClusterRegistration * mRegistration;
208 : };
209 :
210 300 : constexpr ServerClusterInstances(ServerClusterRegistration * start) : mStart(start) {}
211 230 : Iterator begin() { return { mStart }; }
212 208 : Iterator end() { return { nullptr }; }
213 :
214 : private:
215 : ServerClusterRegistration * mStart;
216 : };
217 :
218 : ServerClusterInstances AllServerClusterInstances();
219 :
220 : protected:
221 : ServerClusterRegistration * mRegistrations = nullptr;
222 :
223 : // A one-element cache to speed up finding a cluster within an endpoint.
224 : // The endpointId specifies which endpoint the cache belongs to.
225 : ServerClusterInterface * mCachedInterface = nullptr;
226 :
227 : // Managing context for this registry
228 : std::optional<ServerClusterContext> mContext;
229 : };
230 :
231 : } // namespace app
232 : } // namespace chip
|