gcc unwind ABI change for forced unwind

Cary Coutant cary at cup.hp.com
Wed May 21 22:00:30 UTC 2003


Many of you are probably aware of (and several of you participated in) 
a discussion thread on the gcc-patches mailing list about a new unwind 
API that Richard Henderson had to add to support forced unwinds 
resulting from (among possibly other things) thread cancellation. I 
thought it would be appropriate to bring this issue to this mailing 
list.

Courtesy of Jim Wilson (who posted a note to the libunwind mailing 
list, which brought it to my attention), here are some pointers to the 
discussion threads leading up to this.

http://gcc.gnu.org/ml/gcc-patches/2003-04/msg00008.html
http://gcc.gnu.org/ml/gcc-patches/2003-04/msg02246.html
http://gcc.gnu.org/ml/gcc-patches/2003-05/msg00473.html
http://gcc.gnu.org/ml/gcc-patches/2003-05/msg00160.html

As I understand the central issue, we would like to run C++ cleanups on 
a thread cancellation, in addition to the cleanups registered through 
the POSIX C bindings to the pthreads library. Cleanups resulting from 
local automatic objects that need destruction are easy, but the problem 
is what to do about catch(...) blocks. Richard's approach was to end 
such blocks with a call to the new API, "_Unwind_Resume_or_Rethrow()", 
if the block did not already end with a rethrow.

I think Jason Merrill hit the nail on the head when he said (on 4/30):

> The problem is that catch(...) is overloaded in C++.  It's used both 
> for
> code that wants to write a cleanup inline and rethrow and for code that
> wants to trap all exceptions.

There was some discussion about whether catch(...) blocks should run at 
all when doing a forced unwind, and whether forced unwinds should be 
allowed to penetrate a function declared throw(). I think I saw a 
consensus on the latter issue that thread cancellation and 
longjmp_unwind are not really exceptions, and must be allowed to 
proceed. On the former issue, however, there didn't seem to be a clear 
resolution.

Ideally, one would take the position that good C++ code would 
encapsulate any cleanups it needs into local automatic objects, so that 
the compiler-generated cleanups would invoke the destructor. Real code, 
however, doesn't seem to work that way -- we see catch(...) blocks 
written with the intent to do cleanups. Given this real code, we should 
try to run those cleanups. But what happens when we hit a catch(...) of 
the other flavor -- the kind that just want to catch all exceptions? 
Ideally, we wouldn't want to run them at all on a forced unwind, since 
they're exception handlers, not cleanups. Without Richard's approach, 
if we execute such a block on a forced unwind, and that block doesn't 
end with a rethrow, the forced unwind doesn't resume (until, in the 
case of thread cancellation, the thread next reaches a cancellation 
point, and the process gets repeated). With Richard's new routine, a 
forced unwind gets the opportunity to rethrow, while a normal exception 
gets to resume execution.

The last time I considered this dilemma, I took the (naive?) approach 
that a catch(...) block ending with a rethrow must be a cleanup, while 
any other catch(...) block must be a catch-all exception handler. On a 
forced unwind, we would run the cleanups, including the first kind of 
catch(...) block, but not the second kind. On a normal thrown 
exception, we would execute both kinds.

The current IA-64 C++ ABI leaves it unstated whether or not catch(...) 
blocks run on a forced unwind (probably for this very reason). What's 
wrong with the approach I'm suggesting? It would obviate the need for 
the additional unwind API, and I think it would typically do the right 
thing with respect to executing cleanup code and not executing 
exception handling code on a forced unwind.

My apologies if I've misstated anyone's position, or summarized this 
inaccurately. I welcome any corrections or further clarification.

-cary




More information about the cxx-abi-dev mailing list