[英]Can a terminate handler throw an exception?
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: 使用gcc和clang我得到以下输出:
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.) (我不知道为什么我都
throw;
和std::terminate();
我不想更改示例,所以只假装那两个是其中之一。)
The above question can be boiled down to understanding the behavior of the following two code snippets. 可以将以上问题归结为理解以下两个代码段的行为。
Sample 1: throw with no active exception 示例1:无活动异常时抛出
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 示例2:抛出异常
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
). 如果生成代码的汇编(
g++ -S option
)。 You'll notice the following cxx_abi calls for throw vs throw 7 您会注意到以下cxx_abi调用throw和throw 7
throw;
gets converted to call __cxa_rethrow
被转换为
call __cxa_rethrow
and 和
throw 7;
gets converted to call __cxa_throw
被转换为
call __cxa_throw
Here's the code for __cxa_throw
这是
__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;
因此,在OP Code中
throw 7;
will be caught by corresponding catch(...)
and will be re-thrown by throw;
将被相应的
catch(...)
捕获,并通过throw;
重新throw;
Here's the code for __cxa__rethrow
这是
__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_*
. 在这两种情况下,我们都可以看到
__cxx_*
尚未调用std::terminate()
。 After being thrown by above abi's we are at the following location in the code. 在被上面的abi抛出之后,我们在代码中的以下位置。
refer to the cxx_abi for terminate the code. 请参考cxx_abi终止代码。
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
. 根据我的理解,从处理程序中重新抛出异常会导致在
__cxxabiv1::__terminate
中捕获重新抛出的异常。 Where it calls abort()
. 它在哪里调用
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;
显然,[来自__cxa_rethrow]的
std::terminate()
方法没有出现,这就是为什么控件从未到达std::cout << "got here!" << std::endl;
std::cout << "got here!" << std::endl;
Infinite Recursion 无限递归
What happens if we changed the terminate_handler to the following: 如果将terminate_handler更改为以下内容,会发生什么:
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. 为了理解这一点,我们可以看一下上面提到的
__cxa_rethrow()
。
Since, there's no active exception that is being thrown, __cxa_rethrow()
would end-up calling std::terminate()
, thereby, causing infinite recursion. 因为没有抛出任何活动异常,所以
__cxa_rethrow()
最终将调用std::terminate()
,从而导致无限递归。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.