/*
- * Copyright (C) 1996-2014 The Squid Software Foundation and contributors
+ * Copyright (C) 1996-2018 The Squid Software Foundation and contributors
*
* Squid software is distributed under GPLv2+ license and includes
* contributions from numerous individuals and organizations.
* Please see the COPYING and CONTRIBUTORS files for details.
*/
-#ifndef SQUID_CBDATA_H
-#define SQUID_CBDATA_H
-
-#include "typedefs.h"
+#ifndef SQUID_SRC_CBDATA_H
+#define SQUID_SRC_CBDATA_H
/**
- \defgroup CBDATAAPI Callback Data Allocator API
- \ingroup Components
+\page CBDATA Callback Data Allocator API
+
+ \section Introduction
+
\par
- * Squid's extensive use of callback functions makes it very
- * susceptible to memory access errors. To address this all callback
- * functions make use of a construct called cbdata. This allows
- * functions doing callbacks to verify that the caller is still
- * valid before making the callback.
- *
- \note cbdata is intended for callback data and is tailored specifically
- * to make callbacks less dangerous leaving as few windows of errors as
- * possible. It is not suitable or intended as a generic RefCount
- * memory allocator.
- *
- *
+ Squid's extensive use of callback functions makes it very
+ susceptible to memory access errors. To address this all callback
+ functions make use of a construct called cbdata. This allows
+ functions doing callbacks to verify that the caller is still
+ valid before making the callback.
+
+ \note cbdata is intended for callback data and is tailored specifically
+ to make callbacks less dangerous leaving as few windows of errors as
+ possible. It is not suitable or intended as a generic RefCount
+ memory allocator.
+
+ \par
+ The AsyncJob/AsyncCall mechanism is preferred over CBDATA.
+ It replaces cbdata with an AsyncCall::Pointer object which
+ performs the same memory protection duties via other means.
+
\section Examples Examples
\par
- * Here you can find some examples on how to use cbdata, and why.
- *
+ Here you can find some examples on how to use cbdata, and why.
+
\subsection AsyncOpWithoutCBDATA Asynchronous operation without cbdata, showing why cbdata is needed
\par
- * For a asyncronous operation with callback functions, the normal
- * sequence of events in programs NOT using cbdata is as follows:
- *
+ For a asyncronous operation with callback functions, the normal
+ sequence of events in programs NOT using cbdata is as follows:
+
\code
// initialization
- type_of_data our_data;
- ...
- our_data = malloc(...);
+ type_of_data our_data = new ...;
...
// Initiate a asyncronous operation, with our_data as callback_data
fooOperationStart(bar, callback_func, our_data);
// The asyncronous operation completes and makes the callback
callback_func(callback_data, ....);
// Some time later we clean up our data
- free(our_data);
+ delete our_data;
\endcode
- *
+
\par
- * However, things become more interesting if we want or need
- * to free the callback_data, or otherwise cancel the callback,
- * before the operation completes. In constructs like this you
- * can quite easily end up with having the memory referenced
- * pointed to by callback_data freed before the callback is invoked
- * causing a program failure or memory corruption:
- *
+ However, things become more interesting if we want or need
+ to free the callback_data, or otherwise cancel the callback,
+ before the operation completes. In constructs like this you
+ can quite easily end up with having the memory referenced
+ pointed to by callback_data freed before the callback is invoked
+ causing a program failure or memory corruption:
+
\code
// initialization
- type_of_data our_data;
- ...
- our_data = malloc(...);
+ type_of_data our_data = new ...;
...
// Initiate a asyncronous operation, with our_data as callback_data
fooOperationStart(bar, callback_func, our_data);
...
// ouch, something bad happened elsewhere.. try to cleanup
// but the programmer forgot there is a callback pending from
- // fooOperationsStart() (an easy thing to forget when writing code
+ // fooOperationsStart(). An easy thing to forget when writing code
// to deal with errors, especially if there may be many different
- // pending operation)
- free(our_data);
+ // pending operations.
+ delete our_data;
...
// The asyncronous operation completes and makes the callback
callback_func(callback_data, ....);
// CRASH, the memory pointer to by callback_data is no longer valid
// at the time of the callback
\endcode
- *
+
\subsection AsyncOpWithCBDATA Asyncronous operation with cbdata
- *
+
\par
- * The callback data allocator lets us do this in a uniform and
- * safe manner. The callback data allocator is used to allocate,
- * track and free memory pool objects used during callback
- * operations. Allocated memory is locked while the asyncronous
- * operation executes elsewhere, and is freed when the operation
- * completes. The normal sequence of events is:
- *
+ The callback data allocator lets us do this in a uniform and
+ safe manner. The callback data allocator is used to allocate,
+ track and free memory pool objects used during callback
+ operations. Allocated memory is locked while the asyncronous
+ operation executes elsewhere, and is freed when the operation
+ completes. The normal sequence of events is:
+
\code
// initialization
- type_of_data our_data;
- ...
- our_data = cbdataAlloc(type_of_data);
+ type_of_data our_data = new type_of_data;
...
// Initiate a asyncronous operation, with our_data as callback_data
fooOperationStart(..., callback_func, our_data);
void *cbdata;
if (cbdataReferenceValidDone(local_pointer, &cbdata))
callback_func(...., cbdata);
- ...
- cbdataFree(our_data);
+ delete our_data;
\endcode
- *
+
\subsection AsynchronousOpCancelledByCBDATA Asynchronous operation cancelled by cbdata
- *
+
\par
- * With this scheme, nothing bad happens if cbdataFree() gets called
- * before fooOperantionComplete(...).
- *
+ With this scheme, nothing bad happens if delete gets called
+ before fooOperantionComplete(...).
+
\par Initalization
\code
- type_of_data our_data;
+ // initialization
+ type_of_data our_data = new type_of_data;
...
- our_data = cbdataAlloc(type_of_data);
- \endcode
- * Initiate a asyncronous operation, with our_data as callback_data
- \code
+ // Initiate a asyncronous operation, with our_data as callback_data
fooOperationStart(..., callback_func, our_data);
- \endcode
- * do some stuff with it
- \code
+ ...
+ // do some stuff with it
void *local_pointer = cbdataReference(callback_data);
- \endcode
- * something bad happened elsewhere.. cleanup
- \code
- cbdataFree(our_data);
- \endcode
- * The asyncronous operation completes and tries to make the callback
- \code
+ ...
+ // something bad happened elsewhere.. cleanup
+ delete our_data;
+ ....
+ // The asyncronous operation completes and makes the callback
void *cbdata;
if (cbdataReferenceValidDone(local_pointer, &cbdata))
- {
- \endcode
- * won't be called, as the data is no longer valid
- \code
+ // won't be called, as the data is no longer valid
callback_func(...., cbdata);
- }
+ delete our_data;
\endcode
- *
+
\par
- * In this case, when cbdataFree() is called before
- * cbdataReferenceValidDone(), the callback_data gets marked as invalid.
- * When the callback_data is invalid before executing the callback
- * function, cbdataReferenceValidDone() will return 0 and
- * callback_func is never executed.
- *
+ In this case, when delete is called before cbdataReferenceValidDone(),
+ the callback_data gets marked as invalid.
+ When the callback_data is invalid before executing the callback
+ function, cbdataReferenceValidDone() will return 0 and
+ callback_func is never executed.
+
\subsection AddingCBDATAType Adding a new cbdata registered type
- *
+
\par
- * To add new module specific data types to the allocator one uses the
- * macro CBDATA_CLASS() in the class private section, and CBDATA_CLASS_INIT()
- * or CBDATA_NAMESPACED_CLASS_INIT() in the .cc file.
- * This creates new(), delete() and toCbdata() methods
- * definition in class scope. Any allocate calls must be made with
- * new() and destruction with delete(), they may be called from anywhere.
+ To add new module specific data types to the allocator one uses
+ the macro CBDATA_CLASS() in the class private section, and
+ CBDATA_CLASS_INIT() or CBDATA_NAMESPACED_CLASS_INIT() in the
+ class .cc file.
+
+ \code
+ class Foo
+ {
+ CBDATA_CLASS(Foo);
+
+ public:
+ Foo() {}
+ ~Foo() {}
+ };
+ ...
+ CBDATA_CLASS_INIT(Foo);
+ \endcode
+
+ \par
+ These macros create new(), delete() and toCbdata() methods
+ definition in class scope. Any allocate calls must be made with
+ new() and destruction with delete(), they may be called from
+ anywhere.
+
+ \par
+ The class constructor must make sure that all member
+ variables are initialized, and the class destructor that all
+ dynamic memory is released.
+
+ \par
+ The CbcPointer<> template should be used to create a smart-pointer
+ type for simple reference tracking. It provides get() and valid()
+ accessors for use instead of cbdataReferenceValid(), and performs
+ reliable automatic cbdataReference() and cbdataReferenceDone()
+ tracking.
+ Note that it does NOT provide a replacement for cbdataReferenceValidDone().
+
*/
/**
- *\ingroup CBDATAAPI
* cbdata types. Similar to the MEM_* types, but managed in cbdata.cc
* A big difference is that cbdata types are dynamically allocated.
- * Initially only UNKNOWN type is predefined. Other types are added runtime.
+ *
+ * Initially only UNKNOWN type is predefined.
+ * Other types are added at runtime by CBDATA_CLASS().
*/
typedef int cbdata_type;
static const cbdata_type CBDATA_UNKNOWN = 0;
-/// \ingroup CBDATAAPI
+/**
+ * Create a run-time registration of CBDATA component with
+ * the Squid cachemgr
+ */
void cbdataRegisterWithCacheManager(void);
/**
* Allocates a new entry of a registered CBDATA type.
- * \deprecated use CBDATA_CLASS() instead
+ *
+ * \note For internal CBDATA use only.
*/
void *cbdataInternalAlloc(cbdata_type type, const char *, int);
-/// \deprecated use CBDATA_CLASS() instead
-#define cbdataAlloc(type) ((type *)cbdataInternalAlloc(CBDATA_##type,__FILE__,__LINE__))
/**
- * Frees a entry allocated by cbdataAlloc().
+ * Frees a entry allocated by cbdataInternalAlloc().
+ *
+ * Once this has been called cbdataReferenceValid() and
+ * cbdataReferenceValidDone() will return false regardless
+ * of whether there are remaining cbdata references.
+ *
+ * cbdataReferenceDone() must still be called for any active
+ * references to the cbdata entry. The cbdata entry will be freed
+ * only when the last reference is removed.
*
- \note If there are active references to the entry then the entry
- * will be freed with the last reference is removed. However,
- * cbdataReferenceValid() will return false for those references.
- * \deprecated use CBDATA_CLASS() instead
+ * \note For internal CBDATA use only.
*/
void *cbdataInternalFree(void *p, const char *, int);
-/// \deprecated use CBDATA_CLASS() instead
-#define cbdataFree(var) do {if (var) {cbdataInternalFree(var,__FILE__,__LINE__); var = NULL;}} while(0)
#if USE_CBDATA_DEBUG
void cbdataInternalLockDbg(const void *p, const char *, int);
-#define cbdataInternalLock(a) cbdataInternalLockDbg(a,__FILE__,__LINE__)
+#define cbdataInternalLock(a) cbdataInternalLockDbg(a,__FILE__,__LINE__)
void cbdataInternalUnlockDbg(const void *p, const char *, int);
-#define cbdataInternalUnlock(a) cbdataInternalUnlockDbg(a,__FILE__,__LINE__)
+#define cbdataInternalUnlock(a) cbdataInternalUnlockDbg(a,__FILE__,__LINE__)
int cbdataInternalReferenceDoneValidDbg(void **p, void **tp, const char *, int);
#define cbdataReferenceValidDone(var, ptr) cbdataInternalReferenceDoneValidDbg((void **)&(var), (ptr), __FILE__,__LINE__)
callback(..., cbdata);
\endcode
*
- \param var The reference variable. Will be automatically cleared to NULL.
- \param ptr A temporary pointer to the referenced data (if valid).
+ * \param var The reference variable. Will be automatically cleared to NULL.
+ * \param ptr A temporary pointer to the referenced data (if valid).
*/
int cbdataInternalReferenceDoneValid(void **p, void **tp);
#define cbdataReferenceValidDone(var, ptr) cbdataInternalReferenceDoneValid((void **)&(var), (ptr))
/**
* \param p A cbdata entry reference pointer.
*
- * \retval 0 A reference is stale. The pointer refers to a entry freed by cbdataFree().
+ * \retval 0 A reference is stale. The pointer refers to a entry already freed.
* \retval true The reference is valid and active.
*/
int cbdataReferenceValid(const void *p);
-/// \ingroup CBDATAAPI
-cbdata_type cbdataInternalAddType(cbdata_type type, const char *label, int size, FREE * free_func);
-
/**
- * This needs to be defined FIRST in the class definition.
- * It plays with private/public states in C++.
+ * Create a run-time registration for the class type with cbdata memory allocator.
+ *
+ * \note For internal CBDATA use only.
*/
-#define CBDATA_CLASS(type) \
+cbdata_type cbdataInternalAddType(cbdata_type type, const char *label, int size);
+
+/// declaration-generator used internally by CBDATA_CLASS() and CBDATA_CHILD()
+#define CBDATA_DECL_(type, methodSpecifiers) \
public: \
void *operator new(size_t size) { \
assert(size == sizeof(type)); \
- if (!CBDATA_##type) \
- CBDATA_##type = cbdataInternalAddType(CBDATA_##type, #type, sizeof(type), NULL); \
+ if (!CBDATA_##type) CBDATA_##type = cbdataInternalAddType(CBDATA_##type, #type, sizeof(type)); \
return (type *)cbdataInternalAlloc(CBDATA_##type,__FILE__,__LINE__); \
} \
void operator delete (void *address) { \
- if (address) cbdataInternalFree(address,__FILE__,__LINE__);\
+ if (address) cbdataInternalFree(address,__FILE__,__LINE__); \
} \
- void *toCbdata() { return this; } \
+ void *toCbdata() methodSpecifiers { return this; } \
private: \
- static cbdata_type CBDATA_##type;
+ static cbdata_type CBDATA_##type;
-/**
- \par
- * Creates a new reference to a cbdata entry. Used when you need to
- * store a reference in another structure. The reference can later
- * be verified for validity by cbdataReferenceValid().
- *
- \param var
- * The reference variable is a pointer to the entry, in all
- * aspects identical to the original pointer. But semantically it
- * is quite different. It is best if the reference is thought of
- * and handled as a "void *".
- */
-#define cbdataReference(var) (cbdataInternalLock(var), var)
+/// Starts cbdata-protection in a class hierarchy.
+/// Child classes in the same hierarchy should use CBDATA_CHILD().
+class CbdataParent
+{
+public:
+ virtual ~CbdataParent() {}
+ virtual void *toCbdata() = 0;
+};
+
+/// cbdata-enables a stand-alone class that is not a CbdataParent child
+/// sets the class declaration section to "private"
+/// use this at the start of your class declaration for consistency sake
+#define CBDATA_CLASS(type) CBDATA_DECL_(type, noexcept)
+
+/// cbdata-enables a CbdataParent child class (including grandchildren)
+/// sets the class declaration section to "private"
+/// use this at the start of your class declaration for consistency sake
+#define CBDATA_CHILD(type) CBDATA_DECL_(type, override final)
/**
- \ingroup CBDATAAPI
- * Removes a reference created by cbdataReference().
+ * Creates a global instance pointer for the CBDATA memory allocator
+ * to allocate and free objects for the matching CBDATA_CLASS().
+ *
+ * Place this in the appropriate .cc file for the class being registered.
*
- \param var The reference variable. Will be automatically cleared to NULL.
+ * May be placed inside an explicit namespace scope declaration,
+ * or CBDATA_NAMESPACED_CLASS_INIT() used instead.
*/
-#define cbdataReferenceDone(var) do {if (var) {cbdataInternalUnlock(var); var = NULL;}} while(0)
-
-/// \ingroup CBDATAAPI
#define CBDATA_CLASS_INIT(type) cbdata_type type::CBDATA_##type = CBDATA_UNKNOWN
-#define CBDATA_NAMESPACED_CLASS_INIT(namespace, type) cbdata_type namespace::type::CBDATA_##type = CBDATA_UNKNOWN
/**
- * Macro that defines a new cbdata datatype. Similar to a variable
- * or struct definition. Scope is always local to the file/block
- * where it is defined and all calls to cbdataAlloc() for this type
- * must be within the same scope as the CBDATA_TYPE declaration.
- * Allocated entries may be referenced or freed anywhere with no
- * restrictions on scope.
- * \deprecated Use CBDATA_CLASS() instead
+ * Creates a global instance pointer for the CBDATA memory allocator
+ * to allocate and free objects for the matching CBDATA_CLASS().
+ *
+ * Place this in the appropriate .cc file for the class being registered.
*/
-#define CBDATA_TYPE(type) static cbdata_type CBDATA_##type = CBDATA_UNKNOWN
+#define CBDATA_NAMESPACED_CLASS_INIT(namespace, type) cbdata_type namespace::type::CBDATA_##type = CBDATA_UNKNOWN
/**
- \ingroup CBDATAAPI
- *
- * Initializes the cbdatatype. Must be called prior to the first use of cbdataAlloc() for the type.
+ * Creates a new reference to a cbdata entry. Used when you need to
+ * store a reference in another structure. The reference can later
+ * be verified for validity by cbdataReferenceValid().
*
- \par
- * Alternative to CBDATA_INIT_TYPE()
+ * \deprecated Prefer the use of CbcPointer<> smart pointer.
*
- \param type Type being initialized
- \param free_func The freehandler called when the last known reference to an allocated entry goes away.
+ * \param var
+ * The reference variable is a pointer to the entry, in all
+ * aspects identical to the original pointer. But semantically it
+ * is quite different. It is best if the reference is thought of
+ * and handled as a "void *".
*/
-#define CBDATA_INIT_TYPE_FREECB(type, free_func) do { if (!CBDATA_##type) CBDATA_##type = cbdataInternalAddType(CBDATA_##type, #type, sizeof(type), free_func); } while (false)
+#define cbdataReference(var) (cbdataInternalLock(var), var)
/**
- * Initializes the cbdatatype. Must be called prior to the first use of cbdataAlloc() for the type.
- *
- \par
- * Alternative to CBDATA_INIT_TYPE_FREECB()
+ * Removes a reference created by cbdataReference().
*
- \param type Type being initialized
+ * \deprecated Prefer the use of CbcPointer<> smart pointer.
*
- * \deprecated Use CBDATA_CLASS() instead
+ * \param var The reference variable. Will be automatically cleared to NULL.
*/
-#define CBDATA_INIT_TYPE(type) CBDATA_INIT_TYPE_FREECB(type, NULL)
+#define cbdataReferenceDone(var) do {if (var) {cbdataInternalUnlock(var); var = NULL;}} while(0)
/**
- \ingroup CBDATA
- *
- * A generic wrapper for passing objects through cbdata.
+ * A generic wrapper for passing object pointers through cbdata.
* Use this when you need to pass callback data to a blocking
- * operation, but you don't want to/cannot have that pointer be cbdata itself.
+ * operation, but you don't want to/cannot have that pointer be
+ * cbdata itself.
*/
class generic_cbdata
{
CBDATA_CLASS(generic_cbdata);
public:
-
- generic_cbdata(void * aData) : data(aData) {}
+ generic_cbdata(void *aData) : data(aData) {}
template<typename wrapped_type>void unwrap(wrapped_type **output) {
*output = static_cast<wrapped_type *>(data);
delete this;
}
- /**
- * The wrapped data - only public to allow the mild abuse of this facility
- * done by store_swapout - it gives a wrapped StoreEntry to StoreIO as the
- * object to be given to the callbacks. That needs to be fully cleaned up!
- * - RBC 20060820
- \todo CODE: make this a private field.
- */
- void *data; /* the wrapped data */
+private:
+ void *data;
+};
+
+// Discouraged: Use CbcPointer<> and asynchronous calls instead if possible.
+/// an old-style void* callback parameter
+class CallbackData
+{
+public:
+ CallbackData(): data_(nullptr) {}
+ CallbackData(void *data): data_(cbdataReference(data)) {}
+ CallbackData(const CallbackData &other): data_(cbdataReference(other.data_)) {}
+ CallbackData(CallbackData &&other): data_(other.data_) { other.data_ = nullptr; }
+ ~CallbackData() { cbdataReferenceDone(data_); }
+
+ CallbackData &operator =(const CallbackData &other);
+ CallbackData &operator =(CallbackData &&other) { cbdataReferenceDone(data_); data_ = other.data_; other.data_ = nullptr; return *this; }
+
+ bool valid() const { return cbdataReferenceValid(data_); }
+ void *validDone() { void *result; return cbdataReferenceValidDone(data_, &result) ? result : nullptr; }
+
+private:
+ void *data_; ///< raw callback data, maybe invalid
};
#endif /* SQUID_CBDATA_H */