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 4026 : Cancelable()
70 4026 : {
71 4026 : mNext = mPrev = this;
72 4026 : mCancel = nullptr;
73 4026 : }
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 3960 : Cancelable * Cancel()
81 : {
82 3960 : if (mCancel != nullptr)
83 : {
84 0 : CancelFn cancel = mCancel;
85 0 : mCancel = nullptr;
86 0 : cancel(this);
87 : }
88 3960 : return this;
89 : }
90 3960 : ~Cancelable() { Cancel(); }
91 :
92 : Cancelable(const Cancelable &) = delete;
93 : };
94 :
95 : typedef void (*CallFn)(void *);
96 :
97 : /**
98 : * @class Callback
99 : *
100 : * Base struct used for registration of items of interest, includes
101 : * memory for list management and storing information about the registration's
102 : * meaning. Callback also defines cancellation.
103 : * Callbacks can be registered with exactly one callee at a time. While
104 : * registered (as indicated by a non-null mCancel function), all fields of
105 : * the Callback save usercontext are "owned" by the callee, and should not
106 : * be touched unless Cancel() has first been called.
107 : * When a callee accepts a Callback for registration, step one is always Cancel(),
108 : * in order to take ownership of Cancelable members next, prev, info_ptr, and info_scalar.
109 : * This template class also defines a default notification function prototype.
110 : *
111 : * One-shot semantics can be accomplished by calling Cancel() before calling mCall.
112 : * Persistent registration semantics would skip that.
113 : *
114 : * There is no provision for queueing data passed as arguments to a Callback's mCall
115 : * function. If such a thing is required, the normal pattern is to take an output
116 : * parameter at Callback registration time.
117 : *
118 : */
119 : template <class T = CallFn>
120 : class Callback : private Cancelable
121 : {
122 : public:
123 : /**
124 : * pointer to owner context, normally passed to the run function
125 : */
126 : void * mContext;
127 :
128 : /**
129 : * where to call when the event of interest has occurred
130 : */
131 : T mCall;
132 :
133 : /**
134 : * Indication that the Callback is registered with a notifier
135 : */
136 : bool IsRegistered() { return (mCancel != nullptr); }
137 :
138 : /**
139 : * Cancel, i.e. de-register interest in the event,
140 : * This is the only way to get access to the Cancelable, to enqueue,
141 : * store any per-registration state.
142 : * There are 3 primary use cases for this API:
143 : * 1. For the owner of the Callback, Cancel() means "where-ever this Callback
144 : * was put in a list or registered for an event, gimme back, remove interest".
145 : * 2. To a new registrar, during a registration call, it means "hey cleanup any
146 : * current registrations, let me use the internal fields of Cancelable
147 : * to keep track of what the owner is interested in.
148 : * 3. To any current registrar (i.e. when mCancel is non-null), Cancel() means:
149 : * "remove this Callback from any internal lists and free any resources
150 : * you've allocated to track the interest".
151 : *
152 : * For example: a sockets library with an API like Socket::Readable(Callback<> *cb)
153 : * using an underlying persistent registration API with the OS (like epoll())
154 : * might store the file descriptor and interest mask in the scalar, put the
155 : * Callback in a list. Cancel() would dequeue the callback and remove
156 : * the socket from the interest set
157 : *
158 : */
159 0 : Cancelable * Cancel() { return Cancelable::Cancel(); }
160 :
161 : /**
162 : * public constructor
163 : */
164 2013 : Callback(T call, void * context) : mContext(context), mCall(call) { Cancelable(); }
165 :
166 : /**
167 : * TODO: type-safety? It'd be nice if Cancelables that aren't Callbacks returned null
168 : * here. https://github.com/project-chip/connectedhomeip/issues/1350
169 : */
170 0 : static Callback * FromCancelable(Cancelable * ca) { return static_cast<Callback *>(ca); }
171 : };
172 :
173 : /**
174 : * @brief core of a simple doubly-linked list Callback keeper-tracker-of
175 : *
176 : */
177 : class CallbackDeque : public Cancelable
178 : {
179 : public:
180 : /**
181 : * @brief appends with overridden cancel function, in case the
182 : * list change requires some other state update.
183 : */
184 0 : void Enqueue(Cancelable * ca, void (*cancel)(Cancelable *))
185 : {
186 : // add to a doubly-linked list, set cancel function
187 0 : InsertBefore(ca, this, cancel);
188 0 : }
189 : /**
190 : * @brief appends
191 : */
192 0 : void Enqueue(Cancelable * ca) { Enqueue(ca, Dequeue); }
193 :
194 : /**
195 : * @brief dequeue, but don't cancel, all cas that match the by()
196 : */
197 : void DequeueBy(bool (*by)(uint64_t, const Cancelable *), uint64_t p, Cancelable & dequeued)
198 : {
199 : for (Cancelable * ca = mNext; ca != this;)
200 : {
201 : Cancelable * next = ca->mNext;
202 : if (by(p, ca))
203 : {
204 : _Dequeue(ca);
205 : _InsertBefore(ca, &dequeued);
206 : }
207 : ca = next;
208 : }
209 : }
210 :
211 : /**
212 : * @brief insert the node in a queue in order, sorted by "sortby(a, b)"
213 : * sortby(a, b) should return 1 if a > b, -1 if a < b and 0 if a == b
214 : */
215 : void InsertBy(Cancelable * ca, int (*sortby)(void *, const Cancelable *, const Cancelable *), void * p,
216 : void (*cancel)(Cancelable *))
217 : {
218 : Cancelable * where; // node before which we need to insert
219 : for (where = mNext; where != this; where = where->mNext)
220 : {
221 : if (sortby(p, ca, where) <= 0)
222 : {
223 : break;
224 : }
225 : }
226 : InsertBefore(ca, where, cancel);
227 : }
228 :
229 : void InsertBy(Cancelable * ca, int (*sortby)(void *, const Cancelable *, const Cancelable *), void * p)
230 : {
231 : InsertBy(ca, sortby, p, Dequeue);
232 : }
233 :
234 : /**
235 : * @brief insert the node in a the list at a specific point
236 : */
237 0 : void InsertBefore(Cancelable * ca, Cancelable * where, void (*cancel)(Cancelable *))
238 : {
239 0 : ca->Cancel(); // make doubly-sure we're not corrupting another list somewhere
240 0 : ca->mCancel = cancel;
241 0 : _InsertBefore(ca, where);
242 0 : }
243 : void InsertBefore(Cancelable * ca, Cancelable * where) { InsertBefore(ca, where, Dequeue); }
244 :
245 : /**
246 : * @brief returns first item unless list is empty, otherwise returns NULL
247 : */
248 0 : Cancelable * First() { return (mNext != this) ? mNext : nullptr; }
249 :
250 : /**
251 : * @brief Dequeue all, return in a stub. does not cancel the cas, as the list
252 : * members are still in use
253 : */
254 0 : void DequeueAll(Cancelable & ready)
255 : {
256 0 : if (mNext != this)
257 : {
258 0 : ready.mNext = mNext;
259 0 : ready.mPrev = mPrev;
260 0 : ready.mPrev->mNext = &ready;
261 0 : ready.mNext->mPrev = &ready;
262 :
263 0 : mNext = mPrev = this;
264 : }
265 0 : }
266 :
267 : /**
268 : * @brief dequeue but don't cancel, useful if
269 : * immediately putting on another list
270 : */
271 0 : static void Dequeue(Cancelable * ca)
272 : {
273 0 : _Dequeue(ca);
274 0 : ca->mCancel = nullptr;
275 0 : }
276 :
277 : /**
278 : * @brief empty?
279 : */
280 : bool IsEmpty() { return mNext == this; }
281 :
282 : private:
283 0 : static void _Dequeue(Cancelable * ca)
284 : {
285 0 : ca->mNext->mPrev = ca->mPrev;
286 0 : ca->mPrev->mNext = ca->mNext;
287 0 : ca->mNext = ca->mPrev = ca;
288 0 : }
289 0 : void _InsertBefore(Cancelable * ca, Cancelable * where)
290 : {
291 0 : ca->mPrev = where->mPrev;
292 0 : where->mPrev->mNext = ca;
293 0 : where->mPrev = ca;
294 0 : ca->mNext = where;
295 0 : }
296 : };
297 :
298 : } // namespace Callback
299 : } // namespace chip
|