🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: comp.sys.mac.programmer.codewarrior
Article #1126

Re: throw-ing exceptions at interrupt time?

#1126
From: Jason Lamport
Date: Wed, 17 Oct 2001 21:15
190 lines
7526 bytes
I'm crossposting this to codewarrior.mac, since there seem to be more
people on that list who could give definitive answers (i.e. Metroworks
tech support people). That's why I'm including the original post at
the bottom -- apologies for the extra bandwidth.

Billy Harris <wharris@mail.airmail.net> wrote in message news:<8CA031356232A730.29E614E5B9E85DCC.837A0ADCBC1AE7F6@lp.airnews.net>...
> In article <b53899e6.0110152106.7d06e0fb@posting.google.com>, Jason
> Lamport <elgoog@strangelight.com> wrote:
[....]
> > In any case, when exactly does the thrown object get destructed? (E.g.
> > in the above example: when will *e_ptr no longer be valid?)
> 
> The thrown object gets destructed after the closing '}' of the final
> catch block which handled it.

Interesting. Is it *guaranteed* to get destructed at that point, or is
it merely guaranteed *not* to get destructed *before* that point? (Do
you see the difference?)

> 
> > If an exception is thrown from an
> > interrupt handler, what happens?
> 
> If the throw passes out of your interrupt handler, all hell breaks
> loose. 

It sure does. I figured this one out empirically just after sending
the post. :)

I still don't have answers to my most important questions: is it safe
to use C++ exception handling inside a MacOS interrupt handler at all?
And if so, are there any non-obvious issues when using exceptions
inside a re-entrant interrupt handler? (I took a closer look and
realized that the documentation for the API I'm coding to is somewhat
ambiguous: it's not clear whether some of my functions could get
called re-entrantly or not, so I'll have to play it safe and assume
that they will.)

As I mentioned above, I figured out the first time I tried it that
throwing an exception *out* of an interrupt handler is a Bad Thing.
Using exceptions *inside* the interrupt handler *seems* to work fine
-- but I would rather have some assurances that this isn't actually
moving memory around or doing other interrupt-unsafe things. Just
because it hasn't broken yet doesn't mean that it's interrupt-safe.

I noticed some interesting behaviour when playing with a little test
program:
________________test.cpp__________
class Error {
	public:
		Error();
		virtual ~Error();
};

Error::Error() {
	printf( "Instantiating Error object %X\n", (long) this );
}

Error::~Error() {
	printf( "Destructing Error object %X\n", (long) this );
}

void funcA();
void funcB();

void funcA() {
	printf( "Entering funcA()\n");
	try {
		printf( "Trying funcB()\n");
		funcB();
	} catch (Error& e2) {
		printf( "Rethrowing object %X\n", (long) &e2 );
		throw( e2 );
	}
}

void funcB() {
	printf( "Throwing Error object....\n");
	throw( Error() );
}

int main() {
	printf( "Entering main()\n");
	try {
		printf( "Trying funcA()\n");
		funcA();
	} catch (Error& e1) {
		printf( "Caught Error object %X\n", (long) &e1 );
	}
	printf( "leaving main()\n");
	return 0;
}
___________end____________

Two interesting things when stepping through it with the debugger:

1. The variable pane showed an entry for e1 as soon as main() was
entered, so I'm guessing the compiler allocates memory in a function's
stack frame for every *catch* statement in that function. This makes
sense: it guarantees a place for the thrown object when the throwing
function's stack frame goes away.

2. I could not get the debugger to step into funcA()'s source, or to
stop on breakpoints set inside funcA() or funcB(). If I stepped into
funcA(), all the debugger would show me was disassembled object code.
Similarly, if I selected "break on C++ exceptions" from the debugger
menu, the program would break at the exception, but all the debugger
wouldn't show me the source, only the disassembled object code. It
seems that function calls from within a try-block don't generate the
normal debugging code.

The output was also interesting:

_____________output of test.cpp____________
Entering main()
Trying funcA()
Entering funcA()
Trying funcB()
Throwing Error object....
Instantiating Error object 598CC58
Rethrowing object 598CC58
Destructing Error object 598CC58
Caught Error object 598CC74
Destructing Error object 598CC74
leaving main()
________________end________________

It seems that re-throwing the object copies the object to another
memory location (further up the stack?), destructs the old copy, but
does *not* call the new copy's constructor. (Notice that the output
indicates two destructor calls but only one constructor call.) This
could cause some very hard-to-find bugs if one were relying on the
usual assumption that constructor calls and destructor calls always
occur in matched pairs.

-jason


----[original post]----------------

Can anyone tell me in some detail how exceptions are implemented in
C++ on the Mac?  E.g. supposing I've got something like this:

class MyException { .... };

MyException *e_ptr;

void function_A() {
    throw( MyException() );
}

void function_B() {
    try {
        function_A();
    } catch( MyException& e ) {
        e_ptr = &e;
        ....
    }
}

Where exactly is the storage for the MyException object being
allocated? On the stack or on the heap or with the application
globals? If it's on the stack, why doesn't the object disappear along
with function_A's stack frame when control passes back to function_B? 
If it's on the heap, does that mean that it's never safe to throw an
exception during interrupt time? If it's with the application globals,
does that mean that you can't reliably throw an object from a function
that might be called re-entrantly?

In any case, when exactly does the thrown object get destructed? (E.g.
in the above example: when will *e_ptr no longer be valid?)

In particular I need to know if and how I can use exception handling
inside an interrupt handler. (In my particular case, a non-reentrant
one; but I'd like to know about the case of reentrant handlers as
well, just for future reference.) If an exception is thrown from an
interrupt handler, what happens? I can see three somewhat reasonable
possibilities:

1) it halts the program, just as if the exception were thrown from
main();

2) it disappears/is ignored.

3) the exception gets re-thrown from wherever the thread of execution
was at the time of the interrupt.

Can anyone enlighten me on these things?

-jason
Technical Details
Message-ID: <b53899e6.0110171315.5036e57c@posting.google.com>
Path: arc1-ca.pugleaf.net!archiver1.google.com!postnews1.google.com!not-for-mail
References: <b53899e6.0110152106.7d06e0fb@posting.google.com> <8CA031356232A730.29E614E5B9E85DCC.837A0ADCBC1AE7F6@lp.airnews.net>

Show Full Headers
Complete Article Headers
X-Google-Language: ENGLISH,ASCII-7-bit
X-Google-Thread: 10bcce,d3234953ce62cee2,start
X-Google-Attributes: gid10bcce,public
X-Google-Thread: f635e,54ce753f0fa01891
X-Google-Attributes: gidf635e,public
X-Google-ArrivalTime: 2001-10-17 14:15:37 PST
Path: arc1-ca.pugleaf.net!archiver1.google.com!postnews1.google.com!not-for-mail
From: elgoog@strangelight.com (Jason Lamport)
Newsgroups: comp.sys.mac.programmer.codewarrior,codewarrior.mac
Subject: Re: throw-ing exceptions at interrupt time?
Date: 17 Oct 2001 14:15:37 -0700
Organization: http://groups.google.com/
Lines: 190
Message-ID: <b53899e6.0110171315.5036e57c@posting.google.com>
References: <b53899e6.0110152106.7d06e0fb@posting.google.com> <8CA031356232A730.29E614E5B9E85DCC.837A0ADCBC1AE7F6@lp.airnews.net>
NNTP-Posting-Host: 64.255.216.47
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: 8bit
X-Trace: posting.google.com 1003353337 6479 127.0.0.1 (17 Oct 2001 21:15:37 GMT)
X-Complaints-To: groups-abuse@google.com
NNTP-Posting-Date: 17 Oct 2001 21:15:37 GMT