终止处理程序可以抛出异常吗? [英] Can a terminate handler throw an exception?

查看:223
本文介绍了终止处理程序可以抛出异常吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果有的话,下列程序的定义行为是什么?

  #include< iostream> 
#include< exception>
#include< cstdlib>

void i_throw()
{
std :: cout<<< i_throw()<<的std :: ENDL;
// std :: terminate()是noexcept,所以如果终止处理程序抛出...
//然后终止处理程序被调用...
// std :: terminate是[ [noreturn]]所以不要返回
try
{
throw 7;
}
catch(...)
{
std :: cout<<< 捕获异常,重新抛出() - 的std :: ENDL;
throw;
}
std :: cout<<< 去那里! <<的std :: ENDL;
std :: abort();
}

int main()
{
std :: set_terminate(i_throw);
throw;
std :: terminate();
}

使用gcc和clang,我得到以下输出:

  i_throw()
捕获异常,重掷() - ing
中止(核心转储)

前几条评论后编辑的示例。



(我不知道为什么我同时拥有 throw; std :: terminate(); 不想改变这个例子,所以只是假装只有其中的一个在那里。)

解决方案

上面的问题可以归结为了解以下两个代码片段的行为。



示例1:抛出无活动异常

  int main()
{
try {
throw;
} catch(...){
std :: cout<catch<< endl; //我们从未到达
}
return 0;
}

如果您运行上述代码,它将崩溃如下

 终止调用没有活动异常
中止(核心转储)

示例2:抛出活动异常

  int main()
{
try {
throw 7;
} catch(...){
std :: cout<catch<< endl; //将被抓住
}
return 0;
}

运行它提供可预测的输出

 抓住

如果您生成代码( g ++ -S选项)。你会注意到以下cxx_abi调用throw vs throw 7



throw; 转换为调用__cxa_rethrow





throw 7 ; 转换为调用__cxa_throw



这里是 __ cxa_throw

  externCvoid 
__cxxabiv1 :: __ cxa_throw(void * obj ,std :: type_info * tinfo,
void(_GLIBCXX_CDTOR_CALLABI * dest)(void *))
{
PROBE2(throw,obj,tinfo);

__cxa_eh_globals * globals = __cxa_get_globals();
globals-> uncaughtExceptions + = 1;

//为简洁起见删除代码
// .......
//下面的代码抛出一个异常,被调用者

#ifdef _GLIBCXX_SJLJ_EXCEPTIONS
_Unwind_SjLj_RaiseException(& header-> exc.unwindHeader);
#else
_Unwind_RaiseException(& header-> exc.unwindHeader);
#endif

//某种展开错误。请注意,terminate是一个处理程序。
__cxa_begin_catch(& header-> exc.unwindHeader);
std :: terminate();
}

所以,在OP代码 throw 7; / code>将被相应的 catch(...)捕获,并将被 throw重新抛出;



以下是 __ cxa__rethrow的代码

  externCvoid 
__cxxabiv1 :: __ cxa_rethrow()
{
__cxa_eh_globals * globals = __cxa_get_globals();
__cxa_exception * header = globals-> stuckExceptions; //我们不是

globals-> uncaughtExceptions + = 1;

//注意没有活动异常的luser重新抛出。
if(header)
{
//为简洁而删除的代码
// .....
//下面的代码重新抛出异常
# ifdef _GLIBCXX_SJLJ_EXCEPTIONS
_Unwind_SjLj_Resume_or_Rethrow(& header-> unwindHeader);
#else
#if defined(_LIBUNWIND_STD_ABI)
_Unwind_RaiseException(& header-> unwindHeader);
#else
_Unwind_Resume_or_Rethrow(& header-> unwindHeader);
#endif
#endif
}
std :: terminate();
}

在这两种情况下,我们可以看到 std :: terminate()尚未从 __ cxx _ * 中调用。在上面的abi被抛出之后,我们在代码中的下列位置。



请参考 cxx_abi 终止代码。

  void 
__cxxabiv1 :: __ terminate(std :: terminate_handler handler)throw()
{
__try
{
handler(); //我们的处理程序抛出一个int异常
std :: abort();
}
__catch(...)//这里捕获异常,进程中止。
{std :: abort();


void
std :: terminate()throw()
{
__terminate(get_terminate());
}

摘要


根据我的理解,从处理程序重新抛出异常导致在 __ cxxabiv1 :: __ terminate 中捕获重新抛出的异常。在哪里调用 abort()。显然,<_ c $ c> std :: terminate()
[from __cxa_rethrow]方法没有出现在图片中,这就是为什么控件从未达到 std :: cout< 去那里! << std :: endl;



无限递归



如果将terminate_handler更改为以下内容,将会发生什么:

  void i_throw()
{
std :: cout< i_throw()<<的std :: ENDL;
throw;
std :: cout<<< 去那里! <<的std :: ENDL;
std :: abort();
}

要了解这一点,我们可以看看 __ cxa_rethrow( )如上所述。



由于没有被抛出的活动异常, __ cxa_rethrow()将最终调用 std :: terminate(),从而导致无限递归。


What is the defined behavior of the following program, if any?

#include <iostream>
#include <exception>
#include <cstdlib>

void i_throw()
{
    std::cout << "i_throw()" << std::endl;
    // std::terminate() is noexcept so if the terminate handler throws...
    // then the terminate handler is called...
    // std::terminate is [[noreturn]] so don't return
    try
    {
        throw 7;
    }
    catch(...)
    {
        std::cout << "caught exception, re-throw()-ing" << std::endl;
        throw;
    }
    std::cout << "got here!" << std::endl;
    std::abort();
}

int main()
{
    std::set_terminate(i_throw);
    throw;
    std::terminate();
}

With gcc and clang I get the following output:

i_throw()
caught exception, re-throw()-ing
Aborted (core dumped)

Example edited after first few comments.

(I don't know why I have both throw; and std::terminate();. I don't want to change the example so just pretend only one of those two is there.)

解决方案

The above question can be boiled down to understanding the behavior of the following two code snippets.

Sample 1: throw with no active exception

int main()
{
    try{
        throw;
    }catch(...){
        std::cout<<"caught"<<endl;  //we never reach here
    }
    return 0;
}

If you run the above code it crashes as below

terminate called without an active exception
Aborted (core dumped)

Sample 2: throw with active exception

int main()
{
    try{
        throw 7;
    }catch(...){
        std::cout<<"caught"<<endl;  //will be caught
    }
    return 0;
}

Running it gives a predictable output

caught

If you generate the assembly of the code ( g++ -S option ). You'll notice the following cxx_abi calls for throw vs throw 7

throw; gets converted to call __cxa_rethrow

and

throw 7; gets converted to call __cxa_throw

Here's the code for __cxa_throw

extern "C" void
__cxxabiv1::__cxa_throw (void *obj, std::type_info *tinfo,
             void (_GLIBCXX_CDTOR_CALLABI *dest) (void *))
{
  PROBE2 (throw, obj, tinfo);

  __cxa_eh_globals *globals = __cxa_get_globals ();
  globals->uncaughtExceptions += 1;

  // code removed for brevity 
  //.......
  // Below code throws an exception to be caught by caller

  #ifdef _GLIBCXX_SJLJ_EXCEPTIONS
    _Unwind_SjLj_RaiseException (&header->exc.unwindHeader);
  #else
    _Unwind_RaiseException (&header->exc.unwindHeader);
  #endif

  // Some sort of unwinding error.  Note that terminate is a handler.
  __cxa_begin_catch (&header->exc.unwindHeader);
  std::terminate ();
}

So, in the OP Code throw 7; will be caught by corresponding catch(...) and will be re-thrown by throw;

Here's the code for __cxa__rethrow

extern "C" void
__cxxabiv1::__cxa_rethrow ()
{
  __cxa_eh_globals *globals = __cxa_get_globals ();
  __cxa_exception *header = globals->caughtExceptions; // We are not re

  globals->uncaughtExceptions += 1;

  // Watch for luser rethrowing with no active exception.
  if (header)
    {
      // Code removed for brevity
      // .....
      // Below code rethrows the exception
      #ifdef _GLIBCXX_SJLJ_EXCEPTIONS
      _Unwind_SjLj_Resume_or_Rethrow (&header->unwindHeader);
      #else
      #if defined(_LIBUNWIND_STD_ABI)
      _Unwind_RaiseException (&header->unwindHeader);
      #else
      _Unwind_Resume_or_Rethrow (&header->unwindHeader);
      #endif
      #endif
    }
  std::terminate ();
}

In both the cases, we could see that std::terminate() is not yet called from the __cxx_*. After being thrown by above abi's we are at the following location in the code.

refer to the cxx_abi for terminate the code.

void
__cxxabiv1::__terminate (std::terminate_handler handler) throw ()
{
  __try 
    {
      handler ();      // Our handler has thrown an int exception
      std::abort ();
    } 
  __catch(...)  // Exception is caught here and process is aborted.
    { std::abort (); } 
}

void
std::terminate () throw()
{
  __terminate (get_terminate ());
}

Summary

As per my understanding, the re-throwing of the exception from the handler is resulting in catching the re-thrown exception in __cxxabiv1::__terminate. Where it calls abort(). Clearly, the std::terminate() [from __cxa_rethrow] method didn't come into the picture, that's why the control never reached std::cout << "got here!" << std::endl;

Infinite Recursion

What happens if we changed the terminate_handler to the following:

void i_throw()
{
    std::cout << "i_throw()" << std::endl;
    throw;
    std::cout << "got here!" << std::endl;
    std::abort();
}

To understand this, we could look at __cxa_rethrow() as mentioned above.

Since, there's no active exception that is being thrown, __cxa_rethrow() would end-up calling std::terminate(), thereby, causing infinite recursion.

这篇关于终止处理程序可以抛出异常吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆