# 尝试 Reserve & Handover 元素

### 什么是 Reserve & Handover？

Reserve 也即保留、预订，是 Vector Helper 模块中被设计用于单个或多个元素的连续预订特性；Handover 也即交出、撤销，是 Vector Helper 模块中被设计用于安全地快速撤销由 Reserve 预订的单个或多个元素的特性。

> 您依旧不明白？没关系，我们暂时暂不考虑这些特性，继续往下看场景示例。

### 为什么需要 Reserve & Handover？

例如如下场景，在函数 `thread_helper_create` 中实现创建线程，并将线程 ID 和句柄存入线程信息结构 `ThreadInfo` 中，之后将该结构体 Push 进 Vector 对象实例 `vector_object` 中。

> 以下示例代码为伪代码，仅为表达其含义，不具有可执行性。

首先我们定义 `ThreadInfo` 数据结构：

{% code title="C" %}

```c
typedef struct _ThreadInfo
{
    unsigned int thread_id;
    void* thread_handle;
} ThreadInfo;
```

{% endcode %}

然后编写 `thread_helper_create`函数：

{% code title="C" %}

```c
bool thread_helper_create(...)
{
    bool create_result = false;
    ThreadInfo thread_info = { 0 };
    thread_info.thread_handle = create_thread(..., &thread_info.thread_id);
    if(thread_info.thread_handle)
    {
        create_result = vector_helper_push_element(vector_object, &thread_info, 
            sizeof(thread_info), NULL);
        if(!create_result)
        {
            // 在此处实现安全地终止线程后关闭线程句柄
            close_handle(thread_info.thread_handle);
            printf("thread_helper_create -> vector_helper_push_element error");
        }
    }
    else printf("thread_helper_create -> create_thread error");
    return create_result;
}
```

{% endcode %}

通常情况下，函数 `thread_helper_create` 会执行成功，但处于运行时安全考虑，我们依旧需要检查函数  `vector_helper_push_element`的返回值，以确保变量 `thread_info`成功被 Push 进 Vector 对象实例。

当函数 `vector_helper_push_element` 失败时，我们便需要安全地终止之前创建的线程，这在某些平台上或许是一件非常复杂的事情，因为您不能直接调用 `terminate_thread` 来终止线程，因为这可能会造成线程所使用的内存泄漏。而线程的创建与终止涉及内核对象的创建与销毁，这会带来非常大的性能开销，一个设计良好的高性能应用程序应避免这种问题的发生。

换个角度，若我们在线程真正被创建之前，先 Push 一个空 ThreadInfo 元素，再根据线程创建成功与否来决定是填充元素（Set）还是移除元素（Remove）。的确，问题得到解决，但先后对元素进行的 Push & Set 操作造成了多次无意义的函数调用与内存拷贝开销，一个设计良好的高性能应用程序应避免这种问题的发生。

> PS：我们虽然可以通过创建挂起线程来侧面解决此问题，但并不是最优的解决方案。

若我们使用 Reserve & Handover 特性，这些问题就可以迎刃而解。我们在创建线程之前，先预订 1 个 ThreadInfo 元素，若线程创建失败则快速撤销预订。

{% code title="C" %}

```c
size_t reserve_element_index = 0;
if(vector_helper_try_reserve_element(vector_object, 1, &reserve_element_index))
{
    ThreadInfo thread_info = { 0 };
    thread_info.thread_handle = create_thread(..., &thread_info.thread_id);
    if(thread_info.thread_handle)
    {
        // 线程创建成功？设置之前预订的 ThreadInfo 元素
        create_result = vector_helper_set_element(vector_object, 
            reserve_element_index, &thread_info, sizeof(thread_info));
        // 确定之前预订的所有元素已被使用
        vector_helper_used_reserve_element(vector_object, 
            VECTOR_USED_ALL_RESERVED_ELEMENT);
    }
    else
    {
        // 线程创建失败？快速撤销之前预订的 1 个 ThreadInfo 元素
        vector_helper_handover_element(vector_object, 0, 0, false);
        printf("thread_helper_create -> create_thread error");
    }
}
else printf("thread_helper_create -> vector_helper_try_reserve_element error");
```

{% endcode %}

我们可以发现，通过使用 Reserve & Handover 特性，确保了元素与其余逻辑的操作完整性。您不必担心函数 `vector_helper_try_reserve_element` `vector_helper_used_reserve_element` `vector_helper_handover_element` 的性能开销，因为它们的执行性能最够快。

{% hint style="info" %}
您不必担心函数 vector\_helper\_set\_element 返回 false，因为 **Reserve & Handover** 特性已经确保被预订的单个或多个元素在撤销之前是可用的，因此您可以忽略 **Set** 操作的返回值。
{% endhint %}

{% hint style="warning" %}
您必须确保 **Reserve & Handover** 特性的执行顺序，不能对预订的单个或多个元素执行 **Remove** 操作，否则可能会引发不可预料的错误。
{% endhint %}

### 相关 Vector Helper API 的详细介绍

{% content-ref url="../apis/reserve" %}
[reserve](https://smallso.gitbook.io/vector/apis/reserve)
{% endcontent-ref %}

### 函数原型

我们在上述示例中使用了函数 `vector_helper_try_reserve_element` `vector_helper_handover_element` `vector_helper_used_reserve_element`，以下是它们的函数原型：

{% code title="C" %}

```c
bool vector_helper_try_reserve_element
(
    VectorContext* vector_object_context, 
    size_t reserve_element_count, 
    size_t* reserve_element_start_index
);

bool vector_helper_handover_element
(
    VectorContext* vector_object_context, 
    size_t reserve_element_start_index,
    size_t handover_element_count, 
    bool used_reserve_element
);

bool vector_helper_used_reserve_element
(
    VectorContext* vector_object_context, 
    size_t used_reserve_element_count
);
```

{% endcode %}

函数 `vector_helper_try_reserve_element` 的参数表如下：

| 参数名                            | 数据类型            | 必选    | 描述              |
| ------------------------------ | --------------- | ----- | --------------- |
| vector\_object\_context        | VectorContext\* | True  | Vector 对象实例描述符  |
| reserve\_element\_count        | size\_t         | True  | 需要尝试预订的元素个数     |
| reserve\_element\_start\_index | size\_t\*       | False | 接收被成功预订的首个元素索引号 |

函数 `vector_helper_handover_element` 的参数表如下：

| 参数名                            | 数据类型            | 必选    | 描述             |
| ------------------------------ | --------------- | ----- | -------------- |
| vector\_object\_context        | VectorContext\* | True  | Vector 对象实例描述符 |
| reserve\_element\_start\_index | size\_t         | False | 需要撤销的预订元素起始索引号 |
| handover\_element\_count       | size\_t         | False | 需要撤销的预订元素个数    |
| used\_reserve\_element         | bool            | True  | 被撤销的预订元素是否使用过  |

> 参数 reserve\_element\_start\_index 若为 0，则表示使用之前所预订的第一个元素作为起始索引号。
>
> 参数 handover\_element\_count 若为 0，则表示使用之前所预订的所有元素个数。
>
> 参数 used\_reserve\_element 若为 true，则在预订元素被撤销之前安全地将其从内存格式化，确保所使用的内存区块被重置为空白，防止预订元素被撤销后内存数据被读取。

{% hint style="info" %}
通常情况下，您应该确保并将参数 reserve\_element\_start\_index 和 handover\_element\_count 设置为 0，这将会触发快速撤销机制。
{% endhint %}

函数 `vector_helper_used_reserve_element` 的参数表如下：

| 参数名                           | 数据类型            | 必选    | 描述             |
| ----------------------------- | --------------- | ----- | -------------- |
| vector\_object\_context       | VectorContext\* | True  | Vector 对象实例描述符 |
| used\_reserve\_element\_count | size\_t         | False | 已使用的预订元素个数     |

> 参数 `used_reserve_element_count` 若为 0，则表示之前所预订的元素均已使用。您也可以将此参数设置为  `VECTOR_USED_ALL_RESERVED_ELEMENT` 宏。
