【问题标题】:Reasons for stack unwinding fail堆栈展开失败的原因
【发布时间】:2009-03-05 19:28:29
【问题描述】:

我在调试一个应用程序时遇到了以下代码:

int Func()
{

 try 
 {

   CSingleLock aLock(&m_CriticalSection, TRUE);
   {
     //user code
   }
 }
 catch(...)
 {
     //exception handling
 }
 return -1;

}

m_CriticalSection 是 CCrialSection。

我发现用户代码抛出了一个异常,使得 m_CriticalSection 根本没有被释放。这意味着由于某些原因堆栈已损坏,因此展开失败。

我的问题是: 1) 在哪些不同情况下堆栈展开可能会失败?

2) 有哪些不同的异常可能引发堆栈展开失败。

3) 我可以通过将 CSingleLock 放在 try 块之外来解决这个问题吗?

谢谢,

【问题讨论】:

  • 如果你提到抛出了什么样的异常,如果它实际上在 catch(...) 处被捕获,将会有所帮助。
  • 为什么不在用户代码周围放置 try ... catch 块呢?

标签: c++ winapi mfc


【解决方案1】:

您是否遇到程序异常终止?

我相信你的CCriticalSection 对象将被释放CSingleLock 的析构函数。析构函数总是会被调用,因为这是堆栈上的一个对象。当用户代码抛出时,throw 和函数中的 catch 之间的所有堆栈都将被解除。

但是,您的用户代码中的某些其他对象甚至CSingleLock 析构函数可能在此期间引发了另一个异常。在这种情况下,m_CriticalSection 对象将无法正确释放,std::terminate 会被调用并且您的程序会终止。

这里有一些示例来演示。注意:我正在使用std::terminate 处理函数来通知我状态。您还可以使用std::uncaught_exception 查看是否有任何未捕获的异常。在 here 上有一个很好的讨论和示例代码。

struct S {
    S() { std::cout << __FUNCTION__ << std::endl; }
    ~S() { throw __FUNCTION__; std::cout << __FUNCTION__ << std::endl;  }
};

void func() {
    try {
        S s;
        {
            throw 42;
        }
    } catch(int e) {            
         std::cout << "Exception: " << e << std::endl; 
    }
}

void rip() {
    std::cout << " help me, O mighty Lord!\n"; // pray
}

int main() {
    std::set_terminate(rip);
    try {
        func();
    }
    catch(char *se) {
        std::cout << "Exception: " << se << std::endl;
    }
}

请阅读 this 常见问题以了解清楚。

我可以通过将 CSingleLock 放在 try 块之外来解决这个问题吗?

如果不查看堆栈和错误/崩溃就很难说。你为什么不试一试。它还可能通过隐藏真正的问题来引入一个微妙的错误。

【讨论】:

    【解决方案2】:

    首先让我说我不知道​​ CSingleLock 和 CCriticalSection 是做什么的。

    我所知道的是,在您的“用户代码”部分中引发的异常应该展开堆栈并销毁在 try { } 块中创建的所有变量。

    在我看来,我希望您的 aLock 变量会被异常破坏,而不是 m_CriticalSection。您正在将指向 m_CriticalSection 的指针传递给 aLock 变量,但 m_CriticalSection 对象已经存在,并且是在其他地方创建的。

    【讨论】:

      【解决方案3】:
      • 您确定 m_CriticalSection 的生命周期比 CSingleLock 长吗?
      • 可能有人损坏了您的堆栈?
      • 3) 我可以通过将 CSingleLock 放在 try 块之外来解决这个问题吗?

        在这种情况下 - 是的。但是请记住,将大块放入互斥锁中对性能不利。

      • 顺便说一句,catch(...) 通常不是好习惯。在 Win32 中,它 (catch(...)) 也捕获 SEH 异常,而不仅仅是 c++ 异常。也许你在这个函数中有核心并用 catch(...) 捕获它。

      【讨论】:

      • VC++ catch (...) 自 VC++2005 以来没有捕获 SEH 异常。
      • 好消息和不错的补充:)
      【解决方案4】:

      我的问题是:
      1) 在哪些不同情况下堆栈展开可能会失败?

      2) 有哪些不同的异常可能引发堆栈展开失败。

      • throw 表达式的构造函数抛出异常
      • 异常传播时析构函数抛出异常。
      • 抛出的异常从未被捕获(如果这实际上展开堆栈,则实现定义)。
      • 抛出的异常未在异常规范中指定。
      • 跨 C ABI 引发异常。
      • 在未捕获的线程内抛出异常(实现定义了发生的情况)

      3) 我可以通过将 CSingleLock 放在 try 块之外来解决这个问题吗?

      没有。以上所有情况都会导致应用程序终止而无需进一步展开堆栈。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-12-02
        • 2012-08-31
        • 2014-04-17
        • 1970-01-01
        • 2015-07-30
        • 2021-12-29
        • 2011-07-10
        • 1970-01-01
        相关资源
        最近更新 更多