Squid Web Cache Wiki

Squid Web Cache 文档

🔗 回调数据分配器

Squid广泛使用回调函数,使其非常容易出现内存访问错误。为了解决这个问题,所有回调函数都使用一个名为“cbdata”的结构。这允许进行回调的函数在执行回调之前验证调用方仍然有效。

注意:cbdata用于回调数据,并专门用于减少回调的危险性,尽量减少错误的可能性。它不适用于或不打算作为通用的引用计数内存分配器。

🔗 API

🔗 CBDATA_TYPE

CBDATA_TYPE(datatype);

定义一个新的cbdata数据类型的宏。类似于变量或结构定义。范围始终局限于定义它的文件/块。为此类型的所有 cbdataAlloc 调用必须与 CBDATA_TYPE 声明在同一作用域内。分配的条目可以在任何地方被引用或释放,不受范围限制。

🔗 CBDATA_GLOBAL_TYPE

/* Module header file */
external CBDATA_GLOBAL_TYPE(datatype);

/* Module main C file */
CBDATA_GLOBAL_TYPE(datatype);

定义一个可以在代码中任何位置引用的全局 cbdata 类型。

🔗 CBDATA_INIT_TYPE

CBDATA_INIT_TYPE(datatype);
/* or */
CBDATA_INIT_TYPE_FREECB(datatype, FREE *freehandler);

初始化 cbdatatype。必须在首次使用该类型的 cbdataAlloc() 之前调用。

当分配的条目的最后一个已知引用消失时,将调用 freehandler。

🔗 cbdataAlloc

pointer = cbdataAlloc(datatype);

为已注册的 cbdata 类型分配一个新条目。

🔗 cbdataFree

cbdataFree(pointer);

释放由 cbdataAlloc() 分配的条目。

注意:如果条目存在活动的引用,则在移除最后一个引用时将释放该条目。但是,cbdataReferenceValid() 将对这些引用返回 false。

🔗 cbdataReference

reference = cbdataReference(pointer);

创建对 cbdata 条目的新引用。当您需要将引用存储在另一个结构中时使用。之后可以通过 cbdataReferenceValid() 验证引用的有效性。

注意:引用变量是指向条目的指针,在所有方面与原始指针相同。但语义上却大不相同。最好将引用视为“void *”并以此处理。

🔗 cbdataReferenceDone

cbdataReferenceDone(reference);

移除由 cbdataReference() 创建的引用。

注意:引用变量将自动清除为 NULL。

🔗 cbdataReferenceValid

if (cbdataReferenceValid(reference)) {
    ...
}

如果引用陈旧(指向被 cbdataFree 释放的条目),cbdataReferenceValid() 将返回 false。

🔗 cbdataReferenceValidDone

void *pointer;
bool cbdataReferenceValidDone(reference, &pointer);

移除由 cbdataReference() 创建的引用并检查其有效性。指向被引用数据的临时指针(如果有效)在 \&pointer 参数中返回。

用于最后一次解引用,通常用于进行回调。

void *cbdata;
...
if (cbdataReferenceValidDone(reference, &cbdata)) != NULL)
    callback(..., cbdata);

注意:引用变量将自动清除为 NULL。

🔗 示例

在这里,您可以找到一些关于如何使用 cbdata 的示例,以及为什么

🔗 不使用 cbdata 的异步操作,展示了 cbdata 的必要性

对于带有回调函数的异步操作,在不使用 cbdata 的程序中,正常的事件顺序如下

    /* initialization */
    type_of_data our_data;
    ...
    our_data = malloc(...);
    ...
    /* 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);

然而,如果我们想要或需要释放 callback_data,或者在操作完成之前取消回调,情况会变得更有趣。在这种结构中,您很容易导致 callback_data 指向的内存被释放,而在回调被调用之前,这会导致程序失败或内存损坏

    /* initialization */
    type_of_data our_data;
    ...
    our_data = malloc(...);
    ...
    /* 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
     * to deal with errors, especially if there may be many different
     * pending operation)
     */
    free(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
     */

🔗 使用 cbdata 的异步操作

回调数据分配器使我们能够以统一且安全的方式进行此操作。回调数据分配器用于分配、跟踪和释放回调操作期间使用的内存池对象。分配的内存将在异步操作在其他地方执行时被锁定,并在操作完成后被释放。正常的事件顺序是

    /* initialization */
    type_of_data our_data;
    ...
    our_data = cbdataAlloc(type_of_data);
    ...
    /* Initiate a asyncronous operation, with our_data as callback_data */
    fooOperationStart(..., callback_func, our_data);
    ...
    /* foo */
    void *local_pointer = cbdataReference(callback_data);
    ....
    /* The asyncronous operation completes and makes the callback */
    void *cbdata;
    if (cbdataReferenceValidDone(local_pointer, &cbdata))
        callback_func(...., cbdata);
    ...
    cbdataFree(our_data);

🔗 通过 cbdata 取消的异步操作

在这种机制下,如果 cbdataFree 在 fooOperantionComplete(…) 之前被调用,也不会发生任何 bad thing。

    /* initialization */
    type_of_data our_data;
    ...
    our_data = cbdataAlloc(type_of_data);
    ...
    /* Initiate a asyncronous operation, with our_data as callback_data */
    fooOperationStart(..., callback_func, our_data);
    ...
    /* foo */
    void *local_pointer = cbdataReference(callback_data);
    ....
    /* something bad happened elsewhere.. cleanup */
    cbdataFree(our_data);
    ...
    /* The asyncronous operation completes and tries to make the callback */
    void *cbdata;
    if (cbdataReferenceValidDone(local_pointer, &cbdata))
        /* won't be called, as the data is no longer valid */
        callback_func(...., cbdata);

在这种情况下,当 cbdataFreecbdataReferenceValidDone 之前被调用时,callback_data 会被标记为无效。当 callback_data 在执行回调函数之前无效时,cbdataReferenceValidDone 将返回 0,并且 callback_func 永远不会被执行。

🔗 添加新的 cbdata 注册类型

要向分配器添加新的模块特定数据类型,可以使用宏 CBDATA_TYPE 和 CBDATA_INIT_TYPE。这些宏会创建一个本地的 cbdata 定义(文件或块作用域)。任何 cbdataAlloc 调用都必须在该作用域内进行。但是,cbdataFree 可以从任何地方调用。

    /* First the cbdata type needs to be defined in the module. This
     * is usually done at file scope, but it can also be local to a
     * function or block..
     */
    CBDATA_TYPE(type_of_data);
    
    /* Then in the code somewhere before the first allocation
     * (can be called multiple times with only a minimal overhead)
     */
    CBDATA_INIT_TYPE(type_of_data);
    /* Or if a free function is associated with the data type. This
     * function is responsible for cleaning up any dependencies etc
     * referenced by the structure and is called on cbdataFree or
     * when the last reference is deleted by cbdataReferenceDone /
     * cbdataReferenceValidDone
     */
    CBDATA_INIT_TYPE_FREECB(type_of_data, free_function);

🔗 全局添加新的 cbdata 注册数据类型

要添加可以在代码中任何位置分配的新全局数据类型,必须将其添加到 enums.h 中的 cbdata_type 枚举中,并在 cbdata.c:cbdataInit() 中进行相应的 CREATE_CBDATA 调用。或者,可以像下面所示那样在 globals.h 中添加 CBDATA_GLOBAL_TYPE 定义,并在适当的位置使用 CBDATA_INIT_TYPE,如上所述。

    extern CBDATA_GLOBAL_TYPE(type_of_data);        /* CBDATA_UNDEF */
导航:网站搜索网站页面分类🔼 向上