2 * Copyright (C) 1996-2021 The Squid Software Foundation and contributors
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
9 #ifndef SQUID_SRC_CBDATA_H
10 #define SQUID_SRC_CBDATA_H
13 \page CBDATA Callback Data Allocator API
15 \section CbDataIntro Introduction
18 Squid's extensive use of callback functions makes it very
19 susceptible to memory access errors. To address this all callback
20 functions make use of a construct called cbdata. This allows
21 functions doing callbacks to verify that the caller is still
22 valid before making the callback.
24 \note cbdata is intended for callback data and is tailored specifically
25 to make callbacks less dangerous leaving as few windows of errors as
26 possible. It is not suitable or intended as a generic RefCount
30 The AsyncJob/AsyncCall mechanism is preferred over CBDATA.
31 It replaces cbdata with an AsyncCall::Pointer object which
32 performs the same memory protection duties via other means.
34 \section Examples Examples
36 Here you can find some examples on how to use cbdata, and why.
38 \subsection AsyncOpWithoutCBDATA Asynchronous operation without cbdata, showing why cbdata is needed
40 For a asynchronous operation with callback functions, the normal
41 sequence of events in programs NOT using cbdata is as follows:
45 type_of_data our_data = new ...;
47 // Initiate a asynchronous operation, with our_data as callback_data
48 fooOperationStart(bar, callback_func, our_data);
50 // The asynchronous operation completes and makes the callback
51 callback_func(callback_data, ....);
52 // Some time later we clean up our data
57 However, things become more interesting if we want or need
58 to free the callback_data, or otherwise cancel the callback,
59 before the operation completes. In constructs like this you
60 can quite easily end up with having the memory referenced
61 pointed to by callback_data freed before the callback is invoked
62 causing a program failure or memory corruption:
66 type_of_data our_data = new ...;
68 // Initiate a asynchronous operation, with our_data as callback_data
69 fooOperationStart(bar, callback_func, our_data);
71 // ouch, something bad happened elsewhere.. try to cleanup
72 // but the programmer forgot there is a callback pending from
73 // fooOperationsStart(). An easy thing to forget when writing code
74 // to deal with errors, especially if there may be many different
75 // pending operations.
78 // The asynchronous operation completes and makes the callback
79 callback_func(callback_data, ....);
80 // CRASH, the memory pointer to by callback_data is no longer valid
81 // at the time of the callback
84 \subsection AsyncOpWithCBDATA Asynchronous operation with cbdata
87 The callback data allocator lets us do this in a uniform and
88 safe manner. The callback data allocator is used to allocate,
89 track and free memory pool objects used during callback
90 operations. Allocated memory is locked while the asynchronous
91 operation executes elsewhere, and is freed when the operation
92 completes. The normal sequence of events is:
96 type_of_data our_data = new type_of_data;
98 // Initiate a asynchronous operation, with our_data as callback_data
99 fooOperationStart(..., callback_func, our_data);
102 void *local_pointer = cbdataReference(callback_data);
104 // The asynchronous operation completes and makes the callback
106 if (cbdataReferenceValidDone(local_pointer, &cbdata))
107 callback_func(...., cbdata);
111 \subsection AsynchronousOpCancelledByCBDATA Asynchronous operation cancelled by cbdata
114 With this scheme, nothing bad happens if delete gets called
115 before fooOperantionComplete(...).
120 type_of_data our_data = new type_of_data;
122 // Initiate a asynchronous operation, with our_data as callback_data
123 fooOperationStart(..., callback_func, our_data);
125 // do some stuff with it
126 void *local_pointer = cbdataReference(callback_data);
128 // something bad happened elsewhere.. cleanup
131 // The asynchronous operation completes and makes the callback
133 if (cbdataReferenceValidDone(local_pointer, &cbdata))
134 // won't be called, as the data is no longer valid
135 callback_func(...., cbdata);
140 In this case, when delete is called before cbdataReferenceValidDone(),
141 the callback_data gets marked as invalid.
142 When the callback_data is invalid before executing the callback
143 function, cbdataReferenceValidDone() will return 0 and
144 callback_func is never executed.
146 \subsection AddingCBDATAType Adding a new cbdata registered type
149 To add new module specific data types to the allocator one uses
150 the macro CBDATA_CLASS() in the class private section, and
151 CBDATA_CLASS_INIT() or CBDATA_NAMESPACED_CLASS_INIT() in the
164 CBDATA_CLASS_INIT(Foo);
168 These macros create new(), delete() and toCbdata() methods
169 definition in class scope. Any allocate calls must be made with
170 new() and destruction with delete(), they may be called from
174 The class constructor must make sure that all member
175 variables are initialized, and the class destructor that all
176 dynamic memory is released.
179 The CbcPointer<> template should be used to create a smart-pointer
180 type for simple reference tracking. It provides get() and valid()
181 accessors for use instead of cbdataReferenceValid(), and performs
182 reliable automatic cbdataReference() and cbdataReferenceDone()
184 Note that it does NOT provide a replacement for cbdataReferenceValidDone().
189 * cbdata types. Similar to the MEM_* types, but managed in cbdata.cc
190 * A big difference is that cbdata types are dynamically allocated.
192 * Initially only UNKNOWN type is predefined.
193 * Other types are added at runtime by CBDATA_CLASS().
195 typedef int cbdata_type
;
196 static const cbdata_type CBDATA_UNKNOWN
= 0;
199 * Create a run-time registration of CBDATA component with
202 void cbdataRegisterWithCacheManager(void);
205 * Allocates a new entry of a registered CBDATA type.
207 * \note For internal CBDATA use only.
209 void *cbdataInternalAlloc(cbdata_type type
, const char *, int);
212 * Frees a entry allocated by cbdataInternalAlloc().
214 * Once this has been called cbdataReferenceValid() and
215 * cbdataReferenceValidDone() will return false regardless
216 * of whether there are remaining cbdata references.
218 * cbdataReferenceDone() must still be called for any active
219 * references to the cbdata entry. The cbdata entry will be freed
220 * only when the last reference is removed.
222 * \note For internal CBDATA use only.
224 void *cbdataInternalFree(void *p
, const char *, int);
227 void cbdataInternalLockDbg(const void *p
, const char *, int);
228 #define cbdataInternalLock(a) cbdataInternalLockDbg(a,__FILE__,__LINE__)
230 void cbdataInternalUnlockDbg(const void *p
, const char *, int);
231 #define cbdataInternalUnlock(a) cbdataInternalUnlockDbg(a,__FILE__,__LINE__)
233 int cbdataInternalReferenceDoneValidDbg(void **p
, void **tp
, const char *, int);
234 #define cbdataReferenceValidDone(var, ptr) cbdataInternalReferenceDoneValidDbg((void **)&(var), (ptr), __FILE__,__LINE__)
237 void cbdataInternalLock(const void *p
);
238 void cbdataInternalUnlock(const void *p
);
241 * Removes a reference created by cbdataReference() and checks
242 * it for validity. Meant to be used on the last dereference,
243 * usually to make a callback.
248 if (cbdataReferenceValidDone(reference, &cbdata)) != NULL)
249 callback(..., cbdata);
252 * \param var The reference variable. Will be automatically cleared to NULL.
253 * \param ptr A temporary pointer to the referenced data (if valid).
255 int cbdataInternalReferenceDoneValid(void **p
, void **tp
);
256 #define cbdataReferenceValidDone(var, ptr) cbdataInternalReferenceDoneValid((void **)&(var), (ptr))
258 #endif /* !CBDATA_DEBUG */
261 * \param p A cbdata entry reference pointer.
263 * \retval 0 A reference is stale. The pointer refers to a entry already freed.
264 * \retval true The reference is valid and active.
266 int cbdataReferenceValid(const void *p
);
269 * Create a run-time registration for the class type with cbdata memory allocator.
271 * \note For internal CBDATA use only.
273 cbdata_type
cbdataInternalAddType(cbdata_type type
, const char *label
, int size
);
275 /// declaration-generator used internally by CBDATA_CLASS() and CBDATA_CHILD()
276 #define CBDATA_DECL_(type, methodSpecifiers) \
278 void *operator new(size_t size) { \
279 assert(size == sizeof(type)); \
280 if (!CBDATA_##type) CBDATA_##type = cbdataInternalAddType(CBDATA_##type, #type, sizeof(type)); \
281 return (type *)cbdataInternalAlloc(CBDATA_##type,__FILE__,__LINE__); \
283 void operator delete (void *address) { \
284 if (address) cbdataInternalFree(address,__FILE__,__LINE__); \
286 void *toCbdata() methodSpecifiers { return this; } \
288 static cbdata_type CBDATA_##type;
290 /// Starts cbdata-protection in a class hierarchy.
291 /// Child classes in the same hierarchy should use CBDATA_CHILD().
295 virtual ~CbdataParent() {}
296 virtual void *toCbdata() = 0;
299 /// cbdata-enables a stand-alone class that is not a CbdataParent child
300 /// sets the class declaration section to "private"
301 /// use this at the start of your class declaration for consistency sake
302 #define CBDATA_CLASS(type) CBDATA_DECL_(type, noexcept)
304 /// cbdata-enables a CbdataParent child class (including grandchildren)
305 /// sets the class declaration section to "private"
306 /// use this at the start of your class declaration for consistency sake
307 #define CBDATA_CHILD(type) CBDATA_DECL_(type, override final)
310 * Creates a global instance pointer for the CBDATA memory allocator
311 * to allocate and free objects for the matching CBDATA_CLASS().
313 * Place this in the appropriate .cc file for the class being registered.
315 * May be placed inside an explicit namespace scope declaration,
316 * or CBDATA_NAMESPACED_CLASS_INIT() used instead.
318 #define CBDATA_CLASS_INIT(type) cbdata_type type::CBDATA_##type = CBDATA_UNKNOWN
321 * Creates a global instance pointer for the CBDATA memory allocator
322 * to allocate and free objects for the matching CBDATA_CLASS().
324 * Place this in the appropriate .cc file for the class being registered.
326 #define CBDATA_NAMESPACED_CLASS_INIT(namespace, type) cbdata_type namespace::type::CBDATA_##type = CBDATA_UNKNOWN
329 * Creates a new reference to a cbdata entry. Used when you need to
330 * store a reference in another structure. The reference can later
331 * be verified for validity by cbdataReferenceValid().
333 * \deprecated Prefer the use of CbcPointer<> smart pointer.
336 * The reference variable is a pointer to the entry, in all
337 * aspects identical to the original pointer. But semantically it
338 * is quite different. It is best if the reference is thought of
339 * and handled as a "void *".
341 #define cbdataReference(var) (cbdataInternalLock(var), var)
344 * Removes a reference created by cbdataReference().
346 * \deprecated Prefer the use of CbcPointer<> smart pointer.
348 * \param var The reference variable. Will be automatically cleared to NULL.
350 #define cbdataReferenceDone(var) do {if (var) {cbdataInternalUnlock(var); var = NULL;}} while(0)
353 * A generic wrapper for passing object pointers through cbdata.
354 * Use this when you need to pass callback data to a blocking
355 * operation, but you don't want to/cannot have that pointer be
360 CBDATA_CLASS(generic_cbdata
);
363 generic_cbdata(void *aData
) : data(aData
) {}
365 template<typename wrapped_type
>void unwrap(wrapped_type
**output
) {
366 *output
= static_cast<wrapped_type
*>(data
);
374 // Discouraged: Use CbcPointer<> and asynchronous calls instead if possible.
375 /// an old-style void* callback parameter
379 CallbackData(): data_(nullptr) {}
380 CallbackData(void *data
): data_(cbdataReference(data
)) {}
381 CallbackData(const CallbackData
&other
): data_(cbdataReference(other
.data_
)) {}
382 CallbackData(CallbackData
&&other
): data_(other
.data_
) { other
.data_
= nullptr; }
383 ~CallbackData() { cbdataReferenceDone(data_
); }
385 CallbackData
&operator =(const CallbackData
&other
);
386 CallbackData
&operator =(CallbackData
&&other
) { cbdataReferenceDone(data_
); data_
= other
.data_
; other
.data_
= nullptr; return *this; }
388 bool valid() const { return cbdataReferenceValid(data_
); }
389 void *validDone() { void *result
; return cbdataReferenceValidDone(data_
, &result
) ? result
: nullptr; }
392 void *data_
; ///< raw callback data, maybe invalid
395 #endif /* SQUID_CBDATA_H */