【问题标题】:cds library: michael_deque causes crash when pushing back derived type of custom type (only in release mode)cds 库:michael_deque 在推回自定义类型的派生类型时导致崩溃(仅在发布模式下)
【发布时间】:2013-08-31 17:27:07
【问题描述】:

我使用的是带有默认优化设置(/O2)的VS2012,这个问题只存在于发布模式。

我有一些代码使用michael_deque(使用标准GC)和指向(抽象)类型T的指针。
当我尝试将指针推回从T 派生的类型时,应用程序在退出michael_dequepush_back() 函数时崩溃。

问题似乎完全取决于这种特定类型T,因为编写了一个虚拟类foo,从它派生到类bar(并在构造函数中打印一些东西以避免它被优化掉)然后将 new bar() 推回 michael_deque 不会导致崩溃。

有问题的班级T 是这样的:

class Task
{
public:
    Task() : started(false), unfinishedTasks(1), taskID(++taskIDCounter) {};
    Task(unsigned int parentID_) : started(false), unfinishedTasks(1), taskID(++taskIDCounter), parentID(parentID_)/*, taken(0)*/ {};
    virtual ~Task() = 0 {};

    virtual void execute() final
    {
        this->doActualWork();
        unfinishedTasks--;
    }

    virtual void doActualWork() = 0;
public:
    unsigned int taskID;    //ID of this task
    unsigned int parentID;  //ID of the parent of this task
    bool started;
    std::atomic<unsigned int> unfinishedTasks; //Number of child tasks that are still unfinished
    std::vector<unsigned int> dependencies; //list of IDs of all tasks that this task depends on

};

这个错误可以在一个最小的程序中重现(如果你碰巧有一个环境可以像我一样执行这个,只需在 Task 类可以看到的地方放一个std::atomic&lt;unsigned int&gt; taskIDCounter):

#include <cds/container/michael_deque.h>
#include "task.hpp"
class a : public Task
{
a()
    {
        std::cout<<"dummy print"<<std::endl;
    }
    virtual ~a()
    {
    }

    virtual void doActualWork()
    {
        std::cout<<"whatever"<<std::endl;
    }
};



int main()
{
    cds::Initialize();

    {
        cds::gc::HP hpGC;
        cds::gc::HP::thread_gc myThreadGC;

        cds::container::MichaelDeque<cds::gc::HP,Task*> tasks;
        tasks.push_back(new a()); //will crash at the end of push_back
    }
        cds::Terminate();
}

这可能是什么原因?我是否在类 Task 中做了一些未定义的事情,这会导致优化问题出现问题?

【问题讨论】:

  • 在VS中不能调试吗?
  • 调试模式下不会发生错误。在发布模式下,调试非常困难(即使激活了调试符号),因为该函数中的大部分内容都被优化掉了。
  • 运气不好,崩溃的可能性是当我们取消引用一个悬空指针时。但我认为不是push_back引起的。
  • 是的。当我在程序崩溃后停止程序时,应用程序当前正在退出 push_back 函数(可能在超出范围后解构某些东西)。但是,它并没有显示它在做什么。
  • 您正在使用额外的一对大括号 { } 在本地范围内创建 tasks 容器。可能是cds:Terminate() 试图释放已经释放的东西。

标签: c++ libcds


【解决方案1】:

这确实是一个编译器错误。更具体地说,这是与 Visual Studio 2012 的原子实现相关的错误。

std::atomic 类modify the stack frame pointer (ebp) without backing it up on and popping it form the stack before/after the modification. 的一些模板特化@libcds 库使用这些特化之一,并且产生的不正确的帧指针有时会导致非法内存访问(未定义的行为似乎可以防止调试模式下的灾难性故障)在函数范围之外运行。

在这种特殊情况下的修复是让 libcds 使用不同于 Visual Studio 提供的标准原子库的原子库。库决定在 cxx11_atomic.h 中使用哪个实现:

#if defined(CDS_USE_BOOST_ATOMIC)
#   error "Boost.atomic is not supported"
//#   include <boost/version.hpp>
//#   if BOOST_VERSION >= 105300
//#       include <boost/atomic.hpp>
//#       define CDS_ATOMIC boost
//#       define CDS_CXX11_ATOMIC_BEGIN_NAMESPACE namespace boost {
//#       define CDS_CXX11_ATOMIC_END_NAMESPACE }
//#   else
//#       error "Boost version 1.53 or above is needed for boost.atomic"
//#   endif

#elif CDS_CXX11_ATOMIC_SUPPORT == 1
    // Compiler supports C++11 atomic (conditionally defined in cds/details/defs.h)
#   include <cds/compiler/cxx11_atomic_prepatches.h>
#   include <atomic>
#   define CDS_ATOMIC std
#   define CDS_CXX11_ATOMIC_BEGIN_NAMESPACE namespace std {
#   define CDS_CXX11_ATOMIC_END_NAMESPACE }
#   include <cds/compiler/cxx11_atomic_patches.h>
#else
#   include <cds/compiler/cxx11_atomic.h>
#   define CDS_ATOMIC cds::cxx11_atomics
#   define CDS_CXX11_ATOMIC_BEGIN_NAMESPACE namespace cds { namespace cxx11_atomics {
#   define CDS_CXX11_ATOMIC_END_NAMESPACE }}
#endif

if 语句的第二个分支可以改成类似

#elif CDS_CXX11_ATOMIC_SUPPORT == 255

这将导致库始终使用自己的原子实现。

【讨论】:

  • 哦。啊哈哈哈。时机很好。我刚刚完成了对 linux 的分析:/ +1
  • 这是一个痛苦的错误。作为记录,该错误已针对 VS2013(!)报告,因此只能使用 VS2013 RTM 修复
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-20
  • 2017-06-13
  • 1970-01-01
  • 1970-01-01
  • 2016-09-16
  • 1970-01-01
相关资源
最近更新 更多