Squid's extensive use of callback functions makes it very susceptible to memory access errors. For a blocking operation with callback functions, the normal sequence of events is as follows:
callback_data = malloc(...);
...
fooOperationStart(bar, callback_func, callback_data);
...
fooOperationComplete(...);
callback_func(callback_data, ....);
...
free(callback_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.
The callback data database lets us do this in a uniform and safe manner. Every callback_data pointer must be added to the database. It is then locked while the blocking operation executes elsewhere, and is freed when the operation completes. The normal sequence of events is:
callback_data = malloc(...);
cbdataAdd(callback_data);
...
cbdataLock(callback_data);
fooOperationStart(bar, callback_func, callback_data);
...
fooOperationComplete(...);
if (cbdataValid(callback_data)) {
callback_func(callback_data, ....);
cbdataUnlock(callback_data);
cbdataFree(callback_data);
With this scheme, nothing bad happens if cbdataFree gets called
before cbdataUnlock:
callback_data = malloc(...);
cbdataAdd(callback_data);
...
cbdataLock(callback_data);
fooOperationStart(bar, callback_func, callback_data);
...
cbdataFree(callback_data);
...
fooOperationComplete(...);
if (cbdataValid(callback_data)) {
callback_func(callback_data, ....);
cbdataUnlock(callback_data);
In this case, when cbdataFree is called before
cbdataUnlock, the callback_data gets marked as invalid. Before
executing the callback function, cbdataValid will return 0
and callback_func is never executed. When cbdataUnlock gets
called, it notices that the callback_data is invalid and will
then call cbdataFree.