【问题标题】:Issue with stack allocator with embedded arena带有嵌入式竞技场的堆栈分配器问题
【发布时间】:2014-08-07 23:38:58
【问题描述】:

我在使用 Howard Hinnant 的 stack-based allocator 时遇到崩溃,两者都在 带有 Clang 3.4 的 64 位 Linux 和 MacOS。这是一个最小的例子 在容器的析构函数中触发崩溃:

#include <vector>
#include "stack_alloc.h"

template <template <typename...> class C, typename T, size_t N>
using stack_container = C<T, stack_alloc<T, N>>;

using stack_vector = stack_container<std::vector, size_t, 4>;

int main()
{
  auto s = stack_vector{1, 2, 3};
  auto m = std::move(s);
}

编译如下:

clang++ -std=c++11 -stdlib=libc++ -g -Wall crash.cc && ./a.out

您知道为什么会发生这种崩溃吗?我也试过 在竞技场实现方面重新实现stack_alloc short_alloc,但我在移动基于堆栈的容器时仍然会崩溃。

这是一个 Linux 回溯:

#0  _int_free (av=0x394f5b8760 <main_arena>, p=0x7fffffffe0f0, have_lock=0) at malloc.c:3901
#1  0x00000000004013eb in stack_alloc<unsigned long, 4ul>::deallocate (this=0x7fffffffe080, p=0x7fffffffe100, n=3)
    at ./stack_alloc.h:71
#2  0x0000000000401343 in capacity (this=0x7fffffffe060, this=0x7fffffffe060, __a=..., __p=0x7fffffffe100, __n=3)
    at .../include/c++/v1/memory:1443
#3  std::__1::__vector_base<unsigned long, stack_alloc<unsigned long, 4> >::~__vector_base (this=0x7fffffffe060)
    at .../include/c++/v1/vector:476
#4  0x0000000000400fa5 in std::__1::vector<unsigned long, stack_alloc<unsigned long, 4> >::~vector (this=0x7fffffffe060)
    at .../include/c++/v1/vector:481
#5  0x0000000000400f6e in main () at crash.cc:13

如果人们可以重现错误,我会很感兴趣 (i),以及 (ii) 如何解决它。

【问题讨论】:

    标签: c++ c++11 move-semantics allocator


    【解决方案1】:

    您正在使用不符合标准的 C++11 stack_alloc。作为欣南特himself wrote

    我已经用完全 C++11 的新分配器更新了这篇文章 符合。它取代的分配器不是完全 C++03 也不是 C++11 符合,因为副本不相等。

    更正后的版本名为short_alloc,为found here

    使用需要将堆栈缓冲区放在分配器之外(使用Hinnant's notation):

    int main()
    {
      arena<N> a; // change N to required space
      auto s = SmallVector({1, 2, 3}, Alloc{a});
      auto m = std::move(s);
    }
    

    【讨论】:

    • 确认。这是“竞技场分配器”的标准概念(例如,see here 带有用户提供的竞技场。
    • 我了解“旧”stack_alloc 不符合标准,在这种情况下,移动操作会导致 UB。虽然short_alloc 解决了这个问题,但我选择stack_alloc 的原因是界面:它是一个不需要任何用户代码更改的透明 插入式替换。使用short_alloc,我需要为竞技场簿记添加新代码,这是我试图避免的。是否可以创建一个可用于上述stack_container&lt;C, T, N&gt; 样式的分配器?界面?
    • @MatthiasVallentin 不,这正是 Hinnant 引用的重点:旧版本在调用站点更干净,但它不符合 C++11 标准,因为副本不相等。编写自己的分配器提案以使其合法化是没有办法解决这个问题的 :-) 但我感到你的痛苦(我经常使用 short_alloc,它伤害了我的眼睛,特别是因为为了性能,你还需要做一个 @987654333 @为了占领整个竞技场)。
    • @TemplateRex:对。您只是无法拥有完全隐形的自动存储。也就是说,我认为对于需要这种内存局部性的少数情况,自动 arena 变量是完全合适的、可管理的和合理的。下一个最好的事情是具有可配置内部存储量的“小尺寸优化”向量;像 basic_string 但更大,并且在迭代器交换保证之前。您可能不会同时想要两者。
    【解决方案2】:

    当您移动构造第二个容器时,容器移动构造分配器并接管指针。当第二个容器死机时,它会尝试释放指针并错误地执行,从而导致 deallocate() 中的指针比较中出现 UB。

    “分配器”不满足分配器要求,特别是:

    X a1(move(a)) Post: a1 等于 a 的先验值。

    如果一个分配器可以释放另一个分配的内存,则两个分配器是“相等的”,这显然不是这里的情况。

    【讨论】:

    • 另一种理解方式是分配器的状态永远不能是分配器的对象身份本身。
    • 有趣,所以这个错误归结为分配器相等的概念。但是如果分配器包含它自己的内存,那么根据定义,两个不同的实例永远不会相等。这让我想知道什么是正确的解决方案?实现propagate_on_container_move_assignment(和*_copy_*)是否足以表明两个不同的包含应始终使用它们自己的分配器?
    猜你喜欢
    • 1970-01-01
    • 2015-09-20
    • 1970-01-01
    • 2012-03-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-13
    • 2019-08-19
    相关资源
    最近更新 更多