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