Callback Data Allocator API

Introduction

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.
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.

Examples

Here you can find some examples on how to use cbdata, and why.

Asynchronous operation without cbdata, showing why cbdata is needed

For a asynchronous operation with callback functions, the normal sequence of events in programs NOT using cbdata is as follows:
// initialization
type_of_data our_data = new ...;
...
// Initiate a asynchronous operation, with our_data as callback_data
fooOperationStart(bar, callback_func, our_data);
...
// The asynchronous operation completes and makes the callback
callback_func(callback_data, ....);
// Some time later we clean up our data
delete our_data;
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:
// initialization
type_of_data our_data = new ...;
...
// Initiate a asynchronous 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
// to deal with errors, especially if there may be many different
// pending operations.
delete our_data;
...
// The asynchronous 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

Asynchronous operation with cbdata

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 asynchronous operation executes elsewhere, and is freed when the operation completes. The normal sequence of events is:
// initialization
type_of_data our_data = new type_of_data;
...
// Initiate a asynchronous operation, with our_data as callback_data
fooOperationStart(..., callback_func, our_data);
...
// foo
void *local_pointer = cbdataReference(callback_data);
....
// The asynchronous operation completes and makes the callback
void *cbdata;
if (cbdataReferenceValidDone(local_pointer, &cbdata))
callback_func(...., cbdata);
delete our_data;
#define cbdataReference(var)
Definition: cbdata.h:343
#define cbdataReferenceValidDone(var, ptr)
Definition: cbdata.h:239
Definition: cbdata.cc:38

Asynchronous operation cancelled by cbdata

With this scheme, nothing bad happens if delete gets called before fooOperantionComplete(...).
Initialization
// initialization
type_of_data our_data = new type_of_data;
...
// Initiate a asynchronous operation, with our_data as callback_data
fooOperationStart(..., callback_func, our_data);
...
// do some stuff with it
void *local_pointer = cbdataReference(callback_data);
...
// something bad happened elsewhere.. cleanup
delete our_data;
....
// The asynchronous operation completes and makes the callback
void *cbdata;
if (cbdataReferenceValidDone(local_pointer, &cbdata))
// won't be called, as the data is no longer valid
callback_func(...., cbdata);
delete our_data;
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.

Adding a new cbdata registered type

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.
class Foo
{
public:
Foo() {}
~Foo() {}
};
...
CBDATA_CLASS_INIT(Foo);
#define CBDATA_CLASS(type)
Definition: cbdata.h:289
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.
The class constructor must make sure that all member variables are initialized, and the class destructor that all dynamic memory is released.
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().

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors