终止处理程序可以抛出异常吗? [英] Can a terminate handler throw an exception?
问题描述
如果有的话,下列程序的定义行为是什么?
#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屋!