Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2020 Project CHIP Authors
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 :
18 : /**
19 : * @file
20 : * This file contains definitions for Callback objects for registering with
21 : * Clusters and the Device
22 : */
23 :
24 : #pragma once
25 :
26 : #include <stddef.h>
27 : #include <stdint.h>
28 :
29 : #include <lib/core/CHIPConfig.h>
30 :
31 : namespace chip {
32 :
33 : namespace Callback {
34 :
35 : /**
36 : * @class Cancelable
37 : *
38 : * Private members of a Callback for use by subsystems that accept
39 : * Callbacks for event registration/notification.
40 : *
41 : */
42 : class Cancelable
43 : {
44 : typedef void (*CancelFn)(Cancelable *);
45 :
46 : public:
47 : /**
48 : * @brief for use by Callback callees, i.e. those that accept callbacks for
49 : * event registration. The names suggest how to use these members, but
50 : * implementations can choose.
51 : */
52 : Cancelable * mNext;
53 : Cancelable * mPrev;
54 :
55 : // CHIP_CONFIG_CANCELABLE_HAS_INFO_STRING_FIELD allows consumers that were
56 : // using this field to opt into having it (and the resulting memory bloat)
57 : // while allowing everyone else to save the memory.
58 : #if CHIP_CONFIG_CANCELABLE_HAS_INFO_STRING_FIELD
59 : alignas(uint64_t) char mInfo[24];
60 : #endif // CHIP_CONFIG_CANCELABLE_HAS_INFO_STRING_FIELD
61 :
62 : /**
63 : * @brief when non-null, indicates the Callback is registered with
64 : * a subsystem and that Cancelable members belong to
65 : * that subsystem
66 : */
67 : CancelFn mCancel;
68 :
69 5693 : Cancelable()
70 5693 : {
71 5693 : mNext = mPrev = this;
72 5693 : mCancel = nullptr;
73 5693 : }
74 :
75 : /**
76 : * @brief run whatever function the callee/registrar has specified in order
77 : * to clean up any resource allocation associated with the registration,
78 : * and surrender ownership of the Cancelable's fields
79 : */
80 5759 : Cancelable * Cancel()
81 : {
82 5759 : if (mCancel != nullptr)
83 : {
84 23 : CancelFn cancel = mCancel;
85 23 : mCancel = nullptr;
86 23 : cancel(this);
87 : }
88 5759 : return this;
89 : }
90 5693 : ~Cancelable() { Cancel(); }
91 :
92 : Cancelable(const Cancelable &) = delete;
93 : // A Cancelable links itself into intrusive cancelable lists via mNext/mPrev, so it must not be
94 : // copy-assigned either: a shallow assignment would copy those list links and corrupt the lists
95 : // (and silently shallow-copy any Callback subclass). Complete the rule-of-three; copy construction
96 : // is already deleted above.
97 : Cancelable & operator=(const Cancelable &) = delete;
98 : };
99 :
100 : typedef void (*CallFn)(void *);
101 :
102 : /**
103 : * @class Callback
104 : *
105 : * Base struct used for registration of items of interest, includes
106 : * memory for list management and storing information about the registration's
107 : * meaning. Callback also defines cancellation.
108 : * Callbacks can be registered with exactly one callee at a time. While
109 : * registered (as indicated by a non-null mCancel function), all fields of
110 : * the Callback save usercontext are "owned" by the callee, and should not
111 : * be touched unless Cancel() has first been called.
112 : * When a callee accepts a Callback for registration, step one is always Cancel(),
113 : * in order to take ownership of Cancelable members next, prev, info_ptr, and info_scalar.
114 : * This template class also defines a default notification function prototype.
115 : *
116 : * One-shot semantics can be accomplished by calling Cancel() before calling mCall.
117 : * Persistent registration semantics would skip that.
118 : *
119 : * There is no provision for queueing data passed as arguments to a Callback's mCall
120 : * function. If such a thing is required, the normal pattern is to take an output
121 : * parameter at Callback registration time.
122 : *
123 : */
124 : template <class T = CallFn>
125 : class Callback : private Cancelable
126 : {
127 : public:
128 : /**
129 : * pointer to owner context, normally passed to the run function
130 : */
131 : void * mContext;
132 :
133 : /**
134 : * where to call when the event of interest has occurred
135 : */
136 : T mCall;
137 :
138 : /**
139 : * Indication that the Callback is registered with a notifier
140 : */
141 18 : bool IsRegistered() { return (mCancel != nullptr); }
142 :
143 : /**
144 : * Cancel, i.e. de-register interest in the event,
145 : * This is the only way to get access to the Cancelable, to enqueue,
146 : * store any per-registration state.
147 : * There are 3 primary use cases for this API:
148 : * 1. For the owner of the Callback, Cancel() means "where-ever this Callback
149 : * was put in a list or registered for an event, gimme back, remove interest".
150 : * 2. To a new registrar, during a registration call, it means "hey cleanup any
151 : * current registrations, let me use the internal fields of Cancelable
152 : * to keep track of what the owner is interested in.
153 : * 3. To any current registrar (i.e. when mCancel is non-null), Cancel() means:
154 : * "remove this Callback from any internal lists and free any resources
155 : * you've allocated to track the interest".
156 : *
157 : * For example: a sockets library with an API like Socket::Readable(Callback<> *cb)
158 : * using an underlying persistent registration API with the OS (like epoll())
159 : * might store the file descriptor and interest mask in the scalar, put the
160 : * Callback in a list. Cancel() would dequeue the callback and remove
161 : * the socket from the interest set
162 : *
163 : */
164 47 : Cancelable * Cancel() { return Cancelable::Cancel(); }
165 :
166 : /**
167 : * public constructor
168 : */
169 2837 : Callback(T call, void * context) : mContext(context), mCall(call) { Cancelable(); }
170 :
171 : /**
172 : * TODO: type-safety? It'd be nice if Cancelables that aren't Callbacks returned null
173 : * here. https://github.com/project-chip/connectedhomeip/issues/1350
174 : */
175 17 : static Callback * FromCancelable(Cancelable * ca) { return static_cast<Callback *>(ca); }
176 : };
177 :
178 : /**
179 : * @brief core of a simple doubly-linked list Callback keeper-tracker-of
180 : *
181 : */
182 : class CallbackDeque : public Cancelable
183 : {
184 : public:
185 : /**
186 : * @brief appends with overridden cancel function, in case the
187 : * list change requires some other state update.
188 : */
189 15 : void Enqueue(Cancelable * ca, void (*cancel)(Cancelable *))
190 : {
191 : // add to a doubly-linked list, set cancel function
192 15 : InsertBefore(ca, this, cancel);
193 15 : }
194 : /**
195 : * @brief appends
196 : */
197 12 : void Enqueue(Cancelable * ca) { Enqueue(ca, Dequeue); }
198 :
199 : /**
200 : * @brief dequeue, but don't cancel, all cas that match the by()
201 : */
202 : void DequeueBy(bool (*by)(uint64_t, const Cancelable *), uint64_t p, Cancelable & dequeued)
203 : {
204 : for (Cancelable * ca = mNext; ca != this;)
205 : {
206 : Cancelable * next = ca->mNext;
207 : if (by(p, ca))
208 : {
209 : _Dequeue(ca);
210 : _InsertBefore(ca, &dequeued);
211 : }
212 : ca = next;
213 : }
214 : }
215 :
216 : /**
217 : * @brief insert the node in a queue in order, sorted by "sortby(a, b)"
218 : * sortby(a, b) should return 1 if a > b, -1 if a < b and 0 if a == b
219 : */
220 : void InsertBy(Cancelable * ca, int (*sortby)(void *, const Cancelable *, const Cancelable *), void * p,
221 : void (*cancel)(Cancelable *))
222 : {
223 : Cancelable * where; // node before which we need to insert
224 : for (where = mNext; where != this; where = where->mNext)
225 : {
226 : if (sortby(p, ca, where) <= 0)
227 : {
228 : break;
229 : }
230 : }
231 : InsertBefore(ca, where, cancel);
232 : }
233 :
234 : void InsertBy(Cancelable * ca, int (*sortby)(void *, const Cancelable *, const Cancelable *), void * p)
235 : {
236 : InsertBy(ca, sortby, p, Dequeue);
237 : }
238 :
239 : /**
240 : * @brief insert the node in a the list at a specific point
241 : */
242 15 : void InsertBefore(Cancelable * ca, Cancelable * where, void (*cancel)(Cancelable *))
243 : {
244 15 : ca->Cancel(); // make doubly-sure we're not corrupting another list somewhere
245 15 : ca->mCancel = cancel;
246 15 : _InsertBefore(ca, where);
247 15 : }
248 : void InsertBefore(Cancelable * ca, Cancelable * where) { InsertBefore(ca, where, Dequeue); }
249 :
250 : /**
251 : * @brief returns first item unless list is empty, otherwise returns NULL
252 : */
253 0 : Cancelable * First() { return (mNext != this) ? mNext : nullptr; }
254 :
255 : /**
256 : * @brief Dequeue all, return in a stub. does not cancel the cas, as the list
257 : * members are still in use
258 : */
259 10 : void DequeueAll(Cancelable & ready)
260 : {
261 10 : if (mNext != this)
262 : {
263 7 : ready.mNext = mNext;
264 7 : ready.mPrev = mPrev;
265 7 : ready.mPrev->mNext = &ready;
266 7 : ready.mNext->mPrev = &ready;
267 :
268 7 : mNext = mPrev = this;
269 : }
270 10 : }
271 :
272 : /**
273 : * @brief dequeue but don't cancel, useful if
274 : * immediately putting on another list
275 : */
276 15 : static void Dequeue(Cancelable * ca)
277 : {
278 15 : _Dequeue(ca);
279 15 : ca->mCancel = nullptr;
280 15 : }
281 :
282 : /**
283 : * @brief empty?
284 : */
285 : bool IsEmpty() { return mNext == this; }
286 :
287 : private:
288 15 : static void _Dequeue(Cancelable * ca)
289 : {
290 15 : ca->mNext->mPrev = ca->mPrev;
291 15 : ca->mPrev->mNext = ca->mNext;
292 15 : ca->mNext = ca->mPrev = ca;
293 15 : }
294 15 : void _InsertBefore(Cancelable * ca, Cancelable * where)
295 : {
296 15 : ca->mPrev = where->mPrev;
297 15 : where->mPrev->mNext = ca;
298 15 : where->mPrev = ca;
299 15 : ca->mNext = where;
300 15 : }
301 : };
302 :
303 : } // namespace Callback
304 : } // namespace chip
|