Mailing List Archive

1 2  View All
Re: $SIG{__DIE__} (was Re: Over-zealous warnings ignore eval) [ In reply to ]
: Again, the C<die> in the sub can't call $SIG{__DIE__}, or it'll loop. Do we
: have to say
: print STDERR $msg;
: exit $!; # or some analog which mimics current behavior
: explicitly?

No, the __DIE__ handler is explicitly disabled during the call, so that you
can die from a __DIE__ handler. Similarly for __WARN__.

Larry
Re: $SIG{__DIE__} (was Re: Over-zealous warnings ignore eval) [ In reply to ]
Larry Wall wrote:
> : Again, the C<die> in the sub can't call $SIG{__DIE__}, or it'll loop. Do we
> : have to say
> : print STDERR $msg;
> : exit $!; # or some analog which mimics current behavior
> : explicitly?
>
> No, the __DIE__ handler is explicitly disabled during the call, so that you
> can die from a __DIE__ handler. Similarly for __WARN__.

Um, right. I should've checked util.c. This means that a handler can't itself
signal an exception; I'm not sure this is a good thing. "Resignaling" the
current exception is just a matter of adopting as a convention the rule that if
a handler returns TRUE, execution should resume normally, while if it returns
FALSE, the next handler out should be called. We're sorta stuck, however, if a
handler needs to create a new exception. For instance, consider something like
this:
1. A routine in package Foo::Bar fails to complete a task, so signals
an exception, which is caught by a handler set up in package Foo::Bar.
2. The handler tries a fall-back approach to the problem, and fails
as well.
At this point, it would be ideal for the handler to signal an exception that
indicates that it has failed as well, so that handlers in Foo and main and
wherever get a chance to respond. In order for this to work, either
Foo::Bar::die_handler must be able to signal an exception itself, or it must be
able to modify the "exception record" passed to it. I think the former option
would be better, as the latter would involve replicating the common code
proposed for DieHandler::new in each handler that might want to resignal.

I guess the model which seems most natural to me is that an exception can be
signalled at any time, and it is passed to from handler to handler, working
from the innermost enclosing scope out, until a handler dismisses the
exception, or the program terminates. For purposes of this chain, a handler is
considered to live just "outside" the scope in which it was established, so
that if it signals an exception itself, and it hasn't explicitly established a
handler, the first handler to see an exception is that for the scope just
"outside" the scope in which that handler was established. For instance, of a
handler established at stack depth 5 signals an exception, the handlers
established at depth 5,4,3,2,1,0,last-chance are given a shot at the exception,
in that order. If the handler at depth 3 signals an exception itself, then
that's handed off immediately to handlers at depth 2,1,0,last-chance. If this
new exception is dismissed, then processing of the original exception resumes
where it was interrupted.

I'm sure my VMS background is showing here, but I've found this approach to be
quite useful.

Regards,
Charles Bailey bailey@genetics.upenn.edu
Re: $SIG{__DIE__} (was Re: Over-zealous warnings ignore eval) [ In reply to ]
On Mon, 11 Sep 1995, Charles Bailey wrote:

> Sounds fine -- the only reason I suggested changing the way Perl sets up the
> error messages is to improve efficiency. Coding the initial pass in Perl
> shouldn't be a problem; if exception-driven code in the future needs better
> performance, we can address the issue then.

Hmmm. Didn't think about exceptions... Well, I don't think they'll be an
issue. Beside, if someone is exception happy, it might behoove them to
add a handler like:

$SIG{__DIE__} = new DieHandler sub {
my($file,$line,$msg);
if($msg =~ /^Exception: /) {
die $msg;
}
next;
};

which cuts short any previous handlers. (Hmmm. Maybe this actually should
go into a _pre_handler chain, which can get control before the normal
handlers. Something to think about here.)

> Sounds OK. I've no preference between $msg and @msg. IWBNI if the format of
> $file made it easy to pick out the spec for the file in which $line is located.
> Perhaps it could be quoted somehow, or, in the format you use above, /(\S+)\n/
> should do the trick.

Perhaps this?

my($file,$evalscope,$line,$msg)=@_;
print "$msg at line $line of $evalscope$file\n";

Whatever gets done won't please someone, so it probably doesn't matter
enourmously.

> Sounds fine, if the local($SIG{__DIE__}) is used to handle scoping. We're
> still left with ways to deal with concurrent exceptions (e.g. what effect do we
> want if &$sub generates an exception?). For now, it may be best to wrap &$sub
> and &$oldhandler in an eval, to insure that any exceptions it generates aren't
> passed to $SIG{__DIE__}, and around and around . . .

As Larry has mentioned, $SIG{__DIE__} disables any die-handling
(regardless of assignments to $SIG{__DIE__}) within the handler.

> Again, the C<die> in the sub can't call $SIG{__DIE__}, or it'll loop. Do we
> have to say
> print STDERR $msg;
> exit $!; # or some analog which mimics current behavior
> explicitly?

We need to use die(), otherwise exception handling won't work. (Thanks
for reminding me about exceptions ;-)

> Seems like a good first cut. I'm sure I'm missing something that I'll realize
> only when it comes up in a concrete example, but then, that's what v1.0 is for.

Exactly. I'll work out a full module and post it.

BTW, do you think this should be a separate module, or part of Carp?

> Thanks.
>
> Regards,
> Charles Bailey bailey@genetics.upenn.edu
>

--
Kenneth Albanowski (kjahds@kjahds.com, CIS: 70705,126)
Re: $SIG{__DIE__} (was Re: Over-zealous warnings ignore eval) [ In reply to ]
On Mon, 11 Sep 1995 09:57:28 PDT, Larry Wall wrote:
>: Again, the C<die> in the sub can't call $SIG{__DIE__}, or it'll loop. Do we
>: have to say
>: print STDERR $msg;
>: exit $!; # or some analog which mimics current behavior
>: explicitly?
>
>No, the __DIE__ handler is explicitly disabled during the call, so that you
>can die from a __DIE__ handler. Similarly for __WARN__.
>
>Larry
>

The same ought to be true for DESTROY also, but it isn't. Try this devious
little infinite looper for kicks:

package Foo;
sub new { bless {} }
sub DESTROY { my($s) = Foo->new(); print "." }
package main;
Foo->new();
__END__
......<forever>

We probably ought to check CvDEPTH in sv_clear() as well.


- Sarathy.
gsar@engin.umich.edu
Re: $SIG{__DIE__} (was Re: Over-zealous warnings ignore eval) [ In reply to ]
On Mon, 11 Sep 1995, Charles Bailey wrote:

> Um, right. I should've checked util.c. This means that a handler can't itself
> signal an exception; I'm not sure this is a good thing. "Resignaling" the
> current exception is just a matter of adopting as a convention the rule that if
> a handler returns TRUE, execution should resume normally, while if it returns
> FALSE, the next handler out should be called. We're sorta stuck, however, if a
> handler needs to create a new exception. For instance, consider something like
> this:
> 1. A routine in package Foo::Bar fails to complete a task, so signals
> an exception, which is caught by a handler set up in package Foo::Bar.
> 2. The handler tries a fall-back approach to the problem, and fails
> as well.
> At this point, it would be ideal for the handler to signal an exception that
> indicates that it has failed as well, so that handlers in Foo and main and
> wherever get a chance to respond. In order for this to work, either
> Foo::Bar::die_handler must be able to signal an exception itself, or it must be
> able to modify the "exception record" passed to it. I think the former option
> would be better, as the latter would involve replicating the common code
> proposed for DieHandler::new in each handler that might want to resignal.

In the DieHandler model that I've been using, if a handler says "next" in
it's outermost scope, then the next handler in the chain executes, using
the same error message that this handler got. If it says "last", then the
handler chain is canceled, and control returns to the routine that called
die(). If "return" is used (or falling of the end of the handler) then
the returned array is used as the data to pass to any further handlers.

Is this sufficient?

> I guess the model which seems most natural to me is that an exception can be
> signalled at any time, and it is passed to from handler to handler, working
> from the innermost enclosing scope out, until a handler dismisses the
> exception, or the program terminates. For purposes of this chain, a handler is
> considered to live just "outside" the scope in which it was established, so
> that if it signals an exception itself, and it hasn't explicitly established a
> handler, the first handler to see an exception is that for the scope just
> "outside" the scope in which that handler was established. For instance, of a
> handler established at stack depth 5 signals an exception, the handlers
> established at depth 5,4,3,2,1,0,last-chance are given a shot at the exception,
> in that order. If the handler at depth 3 signals an exception itself, then
> that's handed off immediately to handlers at depth 2,1,0,last-chance. If this
> new exception is dismissed, then processing of the original exception resumes
> where it was interrupted.

Hmm. The last bit wouldn't happen right now. The exception chain can be
traced down, but it cannot be invoked in the middle to start a parellel
trace. I'll have to think about exceptions a bit more, and see how this
plays into it. (I probably could set something up like that by have the
DieHandler execute the real handler inside an eval block to capture
actual dies, and patch that through...)

I think we need a standardized exception mechanism, by the way, and this
seems to be a perfect place to put it in.

> I'm sure my VMS background is showing here, but I've found this approach to be
> quite useful.
>
> Regards,
> Charles Bailey bailey@genetics.upenn.edu
>

--
Kenneth Albanowski (kjahds@kjahds.com, CIS: 70705,126)
Re: $SIG{__DIE__} (was Re: Over-zealous warnings ignore eval) [ In reply to ]
While I'm thinking about exceptions, does anyone have an comments on
which of the following are preferable? (I know these aren't complete
and/or workable. These are just off-the-cuff examples of technique.)

1. The OO approach

package Exception;

sub throw {
@main::_tuckexception=@_;
die "EXCEPTION\n";
};

#$SIG{__DIE__} and catch left as an exercise for the reader

package FileException;

@ISA = ("Exception");

package FileOpenException;

@ISA = ("FileException");

package main;


open FILE, "<foo" or throw FileOpenException "foo";


2. The frozen text approach

sub throw {
die "EXCEPTION: ",freezearray(@_);
}

$SIG{__DIE__} = sub {
if($_[0] =~ /^EXCEPTION: /) {
@exception = thawarray($');
# base further execution on $execution[0], or
# something like that
} else {
die(@_);
}
};

open FILE, "<foo" or throw "fileopen","foo";


3. The array approach

sub throw {
@mail::_tuckexception = @_;
die "EXCEPTION\n";
}

...

The key thing seems to be with the OO style, you get a rigourous
inheritance relationship, while with text, you need to do "fileopen" =~
/^file/ matching, or something equally prone to error.

--
Kenneth Albanowski (kjahds@kjahds.com, CIS: 70705,126)
Re: $SIG{__DIE__} (was Re: Over-zealous warnings ignore eval) [ In reply to ]
On Tue, 12 Sep 1995, Dale Amon wrote:

> Basically there are several cases a handler needs to deal with
>
> 1) The error is something that this handler can fix. It does
> so and returns control to the site of the error.

Resumption.

> 2) The error is not repairable, but survivable. The handler
> cleans up loose ends and returns control to the context
> in which the exception was caught.

Resumption again, but this one is a bit iffy. I think OS's need this more
then applications do. GC would come under this or the previous one.

> 3) The error is not repairable at this level. The handler
> punts the error to the next enclosing error context. It
> should have the option of changing the text, codes, etc
> that are passed upwards.

Rethrow.

> 4) The error is recognized at the level as absolutely fatal.
> The handler causes the program to exit and possibly dump
> immediately.

Die.

> As Charles noted, you always have a default level 0 error handling
>
> context that is of type 4. It catches exceptions if one raises
> exceptions outside of a handler context or does not have an such
> contexts at all; or if the users top level context is of type 3 and
> tries to pass the exception on up.
>
> Just a few cents worth from an old OS hacker...

In playing with exception handling in relation to die(), I'm a little
surprised to say that I don't think die() is needed for much at all, now
that we have closures. If you set the handlers for exceptions up as
closures instead of code at an outer scope, then you can do all sorts of
neat stuff, including resumption.

Given this, I think I'll just ignore exceptions for now, and finish up my
DieHandler module, and see how it flies.

--
Kenneth Albanowski (kjahds@kjahds.com, CIS: 70705,126)
Re: $SIG{__DIE__} (was Re: Over-zealous warnings ignore eval) [ In reply to ]
: Given this, I think I'll just ignore exceptions for now, and finish up my
: DieHandler module, and see how it flies.

In other words, if setting up the closures on scope entry imposes too
much overhead, the DieHandler will die a (hopefully) natural death.
Mmm. I have a sneakin' suspicion that eval {} is going to be
significantly faster for non-exceptional code, especially if the
closures in question access any external lexical variables. Though
the constructor overhead will certainly dominate.

Larry
Re: $SIG{__DIE__} (was Re: Over-zealous warnings ignore eval) [ In reply to ]
On Tue, 12 Sep 1995, Larry Wall wrote:

> : Given this, I think I'll just ignore exceptions for now, and finish up my
> : DieHandler module, and see how it flies.
>
> In other words, if setting up the closures on scope entry imposes too
> much overhead, the DieHandler will die a (hopefully) natural death.

Just about, yes.

> Mmm. I have a sneakin' suspicion that eval {} is going to be
> significantly faster for non-exceptional code, especially if the
> closures in question access any external lexical variables. Though
> the constructor overhead will certainly dominate.

Well, the constructor has virtually _no_ overhead, other then setting up
the closure, and that will dominate. I should run some tests to see how
the speed stack up against eval {}.

> Larry

--
Kenneth Albanowski (kjahds@kjahds.com, CIS: 70705,126)
Re: $SIG{__DIE__} (was Re: Over-zealous warnings ignore eval) [ In reply to ]
Although I'm not working on Perl source, I've written many an

exception handling system...

Basically there are several cases a handler needs to deal with

1) The error is something that this handler can fix. It does
so and returns control to the site of the error.

2) The error is not repairable, but survivable. The handler
cleans up loose ends and returns control to the context
in which the exception was caught.

3) The error is not repairable at this level. The handler
punts the error to the next enclosing error context. It
should have the option of changing the text, codes, etc
that are passed upwards.

4) The error is recognized at the level as absolutely fatal.
The handler causes the program to exit and possibly dump
immediately.

As Charles noted, you always have a default level 0 error handling

context that is of type 4. It catches exceptions if one raises
exceptions outside of a handler context or does not have an such
contexts at all; or if the users top level context is of type 3 and
tries to pass the exception on up.

Just a few cents worth from an old OS hacker...

Dale Amon
Re: $SIG{__DIE__} (was Re: Over-zealous warnings ignore eval) [ In reply to ]
On Tue, 12 Sep 1995, Larry Wall wrote:

> : Given this, I think I'll just ignore exceptions for now, and finish up my
> : DieHandler module, and see how it flies.
>
> In other words, if setting up the closures on scope entry imposes too
> much overhead, the DieHandler will die a (hopefully) natural death.
> Mmm. I have a sneakin' suspicion that eval {} is going to be
> significantly faster for non-exceptional code, especially if the
> closures in question access any external lexical variables. Though
> the constructor overhead will certainly dominate.

Here's the result of a quick trial, using semi-functional code (these
tests probably don't represent actually useful code). I must admit that
some of these are a bit surprising.

0.000722 sec/new closure via constructor
0.000445 sec/new anonymous sub via constructor
0.000038 sec/assignment of anonymous sub to $SIG{__DIE__}
0.000029 sec/assignment of "" to $SIG{__DIE__}
0.000039 sec/eval {} without any mention of $SIG{__DIE__} or subs
0.000617 sec/new closure via subroutine
0.000176 sec/new closure without any calls to subs or methods
0.000199 sec/no-op call to a sub
0.000218 sec/no-op call to a method

All in all, making a closure is definitely more expensive then an
anonymous sub, but not to a degree that it hinders use of it. Just doing
a call to a subroutine or method swamps things down.


In summary, this:

$SIG{__DIE__} = new DieHandler sub {
#whatever, but this isn't a closure, just an anon sub
};

appears to be about four times as expensive as

$SIG{__DIE__} = sub {
#whatever, but this is a closure, not an anon sub
};

or it does in this round of tests. A difference, yes. A difference worthy
of discarding DieHandler: no, I think not.

This is a good point for using eval {} over DieHandlers at all, though, at
least if you are repeated bouncing between scopes that have their own
handler. I doubt it would matter to a Tk dialog error handler.

For a hypothetical exception handler that executes exceptions as closures
_within_ the scope of the failed code (to faciliate resumption) and only
die() if there no exception handler can cope -- I have absolutely no idea
how efficient it would be, and whether it would be affected by these
stats.

> Larry

--
Kenneth Albanowski (kjahds@kjahds.com, CIS: 70705,126)
Re: $SIG{__DIE__} (was Re: Over-zealous warnings ignore eval) [ In reply to ]
> From: Kenneth Albanowski <kjahds@kjahds.com>
>
> On Tue, 12 Sep 1995, Larry Wall wrote:
>
> > : Given this, I think I'll just ignore exceptions for now, and finish up my
> > : DieHandler module, and see how it flies.
> >
> > In other words, if setting up the closures on scope entry imposes too
> > much overhead, the DieHandler will die a (hopefully) natural death.
> > Mmm. I have a sneakin' suspicion that eval {} is going to be
> > significantly faster for non-exceptional code, especially if the
> > closures in question access any external lexical variables. Though
> > the constructor overhead will certainly dominate.

> In summary, this:
>
> $SIG{__DIE__} = new DieHandler sub {
> #whatever, but this isn't a closure, just an anon sub
> };
>
> appears to be about four times as expensive as
>
> $SIG{__DIE__} = sub {
> #whatever, but this is a closure, not an anon sub
> };
>
> or it does in this round of tests. A difference, yes. A difference worthy
> of discarding DieHandler: no, I think not.
>
Forgive me but I've not been following this very closely (no time).

I've always viewed $SIG{__DIE__} as a debugging intervention tool and
not something you could build an exception handling mechanism out of.

Could someone recap in very simple terms what problem is being solved here?

What advantages are being offered over the current 'standard' approach of:

eval {

};
if ($@) {

... handle error in $@ else die ...
}

Tim.
Re: $SIG{__DIE__} (was Re: Over-zealous warnings ignore eval) [ In reply to ]
On Wed, 13 Sep 1995, Tim Bunce wrote:

> Forgive me but I've not been following this very closely (no time).
>
> I've always viewed $SIG{__DIE__} as a debugging intervention tool and
> not something you could build an exception handling mechanism out of.

Quite right. But $SIG{__DIE__} incidentally gets control of an
"exception" before the exception handler will, so it's something to think
about.

> Could someone recap in very simple terms what problem is being solved here?
>
> What advantages are being offered over the current 'standard' approach of:
>
> eval {
>
> };
> if ($@) {
>
> ... handle error in $@ else die ...
> }

Using $SIG{__DIE__}? None. Indeed, if you have some sort of global
handler, it actually makes it more complex. But there is no direct
usefulness in trapping exceptions.

The point is that if I have:

$SIG{__DIE__} = new DieHandler sub {
if(tk_error(@_) eq "Resume") { last } else { next}
};

Then to do exceptions you might also need to say:

local($SIG{__DIE__}) = new DieHandler sub {
die(@_) if $_[0] =~ /^Exception/;
};

Or whatever message you are trying to trap -- pass it along so it can pop
out of the eval, in other words.

Of course, if your:

if ($@) {
... handle error in $@ else die ...
}

is very concientious about die'ing if it doesn't know how to handle the
error, then a standard DieHandler might be able to get away with _not_
triggering massive changes in the error flow if it's inside an eval...

> Tim.
>

--
Kenneth Albanowski (kjahds@kjahds.com, CIS: 70705,126)
Re: $SIG{__DIE__} (was Re: Over-zealous warnings ignore eval) [ In reply to ]
Kenneth Albanowski wrote:
| On Wed, 13 Sep 1995, Tim Bunce wrote:
|
| > Forgive me but I've not been following this very closely (no time).
| >
| > I've always viewed $SIG{__DIE__} as a debugging intervention tool and
| > not something you could build an exception handling mechanism out of.
|
| Quite right. But $SIG{__DIE__} incidentally gets control of an
| "exception" before the exception handler will, so it's something to think
| about.

Hmm. From what I can see, $SIG{__DIE__} is the only thing out of which one
could build an exception handler. Perhaps the difference in view lies in the
definition of exception. I thinking of exceptions as events which interrupt
the normal flow of execution because the programmer felt a condition required
immediate action. This is distinct from errors which are reported back to the
caller, with or without terminating the execution of the current routine.

| > Could someone recap in very simple terms what problem is being solved here?
| >
| > What advantages are being offered over the current 'standard' approach of:
| >
| > eval {
| >
| > };
| > if ($@) {
| >
| > ... handle error in $@ else die ...
| > }
|
| Using $SIG{__DIE__}? None. Indeed, if you have some sort of global
| handler, it actually makes it more complex. But there is no direct
| usefulness in trapping exceptions.

Apart from conceptual differences, there is one class of problem which can't be
handled by the C<eval { ... } if ($@) { ... }> approach: recoverable errors.
By the time one checks $@, execution has moved past the point of error, the
scope in which the exception was generated has been exited, lexicals have been
destroyed, etc. Building the exception handling around pseudo-signal-handlers
like $SIG{__DIE__} could allow one to react to the exception as soon as it
occurs, and then resume normal execution if the error is recoverable.

From the perspective of handlers that report the error, clean up, and get out,
I don't see much conceptual difference. There may be a small advantage in
efficiency using $SIG{__DIE__}, since one doesn't have to construct (possible
several nested) eval scope(s) when executing code which one expects will
not generate exceptions in the normal case.

Regards,
Charles Bailey bailey@genetics.upenn.edu
Re: $SIG{__DIE__} (was Re: Over-zealous warnings ignore eval) [ In reply to ]
On Wed, 13 Sep 1995, Charles Bailey wrote:

> Hmm. From what I can see, $SIG{__DIE__} is the only thing out of which one
> could build an exception handler. Perhaps the difference in view lies in the
> definition of exception. I thinking of exceptions as events which interrupt
> the normal flow of execution because the programmer felt a condition required
> immediate action. This is distinct from errors which are reported back to the
> caller, with or without terminating the execution of the current routine.

No, $SIG{__DIE__} isn't the only way to build an exception handler, as
you'll see below.

> Apart from conceptual differences, there is one class of problem which can't be
> handled by the C<eval { ... } if ($@) { ... }> approach: recoverable errors.
> By the time one checks $@, execution has moved past the point of error, the
> scope in which the exception was generated has been exited, lexicals have been
> destroyed, etc. Building the exception handling around pseudo-signal-handlers
> like $SIG{__DIE__} could allow one to react to the exception as soon as it
> occurs, and then resume normal execution if the error is recoverable.

Yes, $SIG{__DIE__} can be used for that, but you can also use other
techniques.

> From the perspective of handlers that report the error, clean up, and get out,
> I don't see much conceptual difference. There may be a small advantage in
> efficiency using $SIG{__DIE__}, since one doesn't have to construct (possible
> several nested) eval scope(s) when executing code which one expects will
> not generate exceptions in the normal case.

It's hard to say.

Here's the basic plan for an exception system that is mostly clear of
$SIG{__DIE__}:

catch sub {
# Code to execute that may throw an exception
}, FileOpenException => sub {
# execute this code on a FileOpen exception
}, finally => sub {
# execute this code after leaving the code scope, no matter what.
};

Throw looks like this:

throw FileOpenException; #optional, @moredata;

Inside each of the catch handlers, you can use:

rethrow #optional exception, @moredata;


I don't have the internals of catch and throw worked out yet, but the
basic idea is this: keep a stack of exception actions (with each action
being a closure), and on an exception (when throw() is invoked)
_directly_ call the closure that handles that exception, if one exists.
If no handler is found, the "finally" closure gets executed, and then the
exception stack is peeled back by one to look for handlers in the next
scope. Note that _all_ of this is happening within the throw() call, and
the real stack scope never changes, we are relying on the closures to figure
out where they belong. It's only after a handler finishes that we figure
out how far back in the stack we need to be, and _then_ use die() to peel
back the perl stack.

I'm not even sure that final die() is needed, though, as it may be
possible to do this with a global variable and a return. I'm a little
fuzzy on the exact implementation details, but it seems feasible.
(Whether it would be efficient is unknown.)

I must say that, all things considered, closures in perl are a great deal
of fun. :-)

> Regards,
> Charles Bailey bailey@genetics.upenn.edu

--
Kenneth Albanowski (kjahds@kjahds.com, CIS: 70705,126)
Re: $SIG{__DIE__} (was Re: Over-zealous warnings ignore eval) [ In reply to ]
Kenneth Albanowski wrote:
| On Wed, 13 Sep 1995, Charles Bailey wrote:
|
| > Hmm. From what I can see, $SIG{__DIE__} is the only thing out of which one
| > could build an exception handler. Perhaps the difference in view lies in the
| > definition of exception. I thinking of exceptions as events which interrupt
| > the normal flow of execution because the programmer felt a condition required
| > immediate action. This is distinct from errors which are reported back to the
| > caller, with or without terminating the execution of the current routine.
|
| No, $SIG{__DIE__} isn't the only way to build an exception handler, as
| you'll see below.

Ah. Sorry; I was imprecise in my original statement. As far as I can see,
$SIG{__DIE__} is the only mechanism extant in Perl from which one could build
an exception handling mechenism. Perl would have to be modified to allow
execution to resume if a handler returned an "OK" status, and we'd need to sort
out actions for "nested" exception chains, but the basic principle is there.

If one is willing to build an exception handling mechanism into Perl de novo,
then yes, there are several approaches one might take, one of which you've
described below. To my untrained eye, it looks like mechanisms I've seen in
some object-ish C implementations and in POSIX threads, but I haven't used
either enough to really know their ins and outs.

| [text deleted]
|
| Here's the basic plan for an exception system that is mostly clear of
| $SIG{__DIE__}:
|
| catch sub {
| # Code to execute that may throw an exception
| }, FileOpenException => sub {
| # execute this code on a FileOpen exception
| }, finally => sub {
| # execute this code after leaving the code scope, no matter what.
| };
|
| Throw looks like this:
|
| throw FileOpenException; #optional, @moredata;
|
| Inside each of the catch handlers, you can use:
|
| rethrow #optional exception, @moredata;
|
|
| I don't have the internals of catch and throw worked out yet, but the
| basic idea is this: keep a stack of exception actions (with each action
| being a closure), and on an exception (when throw() is invoked)
| _directly_ call the closure that handles that exception, if one exists.
| If no handler is found, the "finally" closure gets executed, and then the
| exception stack is peeled back by one to look for handlers in the next
| scope. Note that _all_ of this is happening within the throw() call, and
| the real stack scope never changes, we are relying on the closures to figure
| out where they belong. It's only after a handler finishes that we figure
| out how far back in the stack we need to be, and _then_ use die() to peel
| back the perl stack.

Sounds basically fine, except that I wouldn't advocate executing the "finally"
block when handling an exception. I think that an exception shouldn't of
itself do anything which would alter the current thread of normal execution, so
that if some handler dismisses the exception, normal execution can resume from
the statement after the C<throw>, with the same state as before the execution
(aside modifications made by the handlers). This isn't to say that the
handlers don't execute in their own scope, and may not be able to see lexicals
in the caller; only that the process of throwing the exception shouldn't close
out the calling scope. (BTW, I'd say that handlers ought to execute in a scope
internal to the caller for purposes of symbol lookup -- they should see the
caller's C<local> variables -- but outside for purposes of handling nested
exceptions -- if a handler throws an exception, it should be passed first to
any handlers the handler established itself, then to handlers established by
the handler's caller's caller, and so on out. (Try that for a
tongue-twister. :-)) The point here is that if the handler throws an
exception, it shouldn't reinvoke itself.

Ideally, I'd want a handler to be able to cause one of four things to happen:
- exception is dismissed; execution resumes in the normal thread at the
statement just after the one which threw the exception
- exception is dismissed; call stack is unwound by a specified number
of frames, and execution is resumed at the statement just after the
last call that was unwound
- exception is passed out to the next handler
- a new exception is thrown, which is handled as a new, "nested" event
If it is eventually dismissed, handling of the current exception
is resumed, as though it were the normal execution path, and
so on . . .

| I must say that, all things considered, closures in perl are a great deal
| of fun. :-)

Indeed.

Regards,
Charles Bailey bailey@genetics.upenn.edu
Re: $SIG{__DIE__} (was Re: Over-zealous warnings ignore eval) [ In reply to ]
Here's a wacky one:

$ ./perl
$SIG{__DIE__} = sub { goto recover };
die "oops";
recover:
print "wow\n";
^D
wow
oops at - line 2.

Talk about snatching defeat from the jaws of victory...

Larry
Re: $SIG{__DIE__} (was Re: Over-zealous warnings ignore eval) [ In reply to ]
>Here's a wacky one:
>
> $ ./perl
> $SIG{__DIE__} = sub { goto recover };
> die "oops";
> recover:
> print "wow\n";
> ^D
> wow
> oops at - line 2.
>
>Talk about snatching defeat from the jaws of victory...

Sorta closer, though still a kludge:
$ perl
$SIG{__DIE__} = sub { $__excpt = 0; goto recover; };
die "oops";
recover:
print "wow\n";
END { exit 0 unless $__excpt; }
the idea here being that
if one wants to exit now,
one just doesn't C<goto> a label in the main thread,
elsif one wants to go back to normal,
one sets $__excpt = 0 and executes a C<goto> back into the main thread,
elsif one wants to recover, but report the error out
one sets $__excpt = !0 and executes a C<goto> back into the main thread
endif.

Regards,
Charles Bailey bailey@genetics.upenn.edu
Re: $SIG{__DIE__} (was Re: Over-zealous warnings ignore eval) [ In reply to ]
On Wed, 13 Sep 1995, Larry Wall wrote:

> Here's a wacky one:
>
> $ ./perl
> $SIG{__DIE__} = sub { goto recover };
> die "oops";
> recover:
> print "wow\n";
> ^D
> wow
> oops at - line 2.
>
> Talk about snatching defeat from the jaws of victory...

That looks like a flushing conflict. But it evidentally isn't reliable
behaviour. I get this with 5.001m (no patches):

wow
àé

> Larry

--
Kenneth Albanowski (kjahds@kjahds.com, CIS: 70705,126)
Re: $SIG{__DIE__} (was Re: Over-zealous warnings ignore eval) [ In reply to ]
: On Wed, 13 Sep 1995, Larry Wall wrote:
:
: > Here's a wacky one:
: >=20
: > $ ./perl
: > $SIG{__DIE__} =3D sub { goto recover };
: > die "oops";
: > recover:
: > print "wow\n";
: > ^D
: > wow
: > oops at - line 2.
: >=20
: > Talk about snatching defeat from the jaws of victory...
:
: That looks like a flushing conflict. But it evidentally isn't reliable=20
: behaviour. I get this with 5.001m (no patches):
:
: =09wow
: =09=E0=E9

Gad, I hate MIME.

There is a buffer conflict there. The more basic problem is that
there's a runlevel confusion. The "goto" should realize that it has to
longjmp() out of the die, but doesn't. So it ends up doing the
equivalent of

$SIG{__DIE__} = sub { print "wow" };

Larry
Re: $SIG{__DIE__} (was Re: Over-zealous warnings ignore eval) [ In reply to ]
On Wed, 13 Sep 1995, Charles Bailey wrote:

> Ah. Sorry; I was imprecise in my original statement. As far as I can see,
> $SIG{__DIE__} is the only mechanism extant in Perl from which one could build
> an exception handling mechenism. Perl would have to be modified to allow
> execution to resume if a handler returned an "OK" status, and we'd need to sort
> out actions for "nested" exception chains, but the basic principle is there.
>
> If one is willing to build an exception handling mechanism into Perl de novo,
> then yes, there are several approaches one might take, one of which you've
> described below. To my untrained eye, it looks like mechanisms I've seen in
> some object-ish C implementations and in POSIX threads, but I haven't used
> either enough to really know their ins and outs.

But a closure based exception system doesn't actually require any
_changes_ to the Perl core, merely a module of code. So yes, it has to be
built from scratch, but no, there isn't any particular barrier to doing
so. Since the handlers are executing in the scope of the thrown execption,
returning (resuming) where the exception happened is no problem. And then
normal mechanisms, either die() or return(), can be used to pop stack
frames if a handler needs to end up outside the calling code.

> Sounds basically fine, except that I wouldn't advocate executing the "finally"
> block when handling an exception.

That's one of the things I'm a little fuzzy on. ;-)

> I think that an exception shouldn't of
> itself do anything which would alter the current thread of normal execution, so
> that if some handler dismisses the exception, normal execution can resume from
> the statement after the C<throw>, with the same state as before the execution
> (aside modifications made by the handlers). This isn't to say that the
> handlers don't execute in their own scope, and may not be able to see lexicals
> in the caller; only that the process of throwing the exception shouldn't close
> out the calling scope.

Agreed. I think "finally" actions should be taken when the callers stack
frame is discarded, either via the catch block ending without error or by
an unhandled exception. If you don't have "finally" actions then you end
up doing the same clean-up over in all your exception handlers, and that's
a pain. But having "finally" step on the handler's toes isn't much fun
either.

> (BTW, I'd say that handlers ought to execute in a scope
> internal to the caller for purposes of symbol lookup -- they should see the
> caller's C<local> variables -- but outside for purposes of handling nested
> exceptions -- if a handler throws an exception, it should be passed first to
> any handlers the handler established itself, then to handlers established by
> the handler's caller's caller, and so on out. (Try that for a
> tongue-twister. :-)) The point here is that if the handler throws an
> exception, it shouldn't reinvoke itself.

Thank you for that... I'll get back to you if I ever understand it. :-)

Seriously, I'm not at all sure nested exceptions are needed. Merely being
able to rethrow the original exception, or throw a new exception and have
it continue out of the chain (as if it were originally an unhandled
exception) seems sufficnt. Then again, if this whole thing is done with
closures, fully recursive exception handling might come automatically.

As for the scope of handlers: if they are closures, then they'll see two
scopes, I guess. The scope they were created in, overlayed with any
recent local()'ized changes to that scope. So actually, it looks like not
much of the scope of the catch {} block would be available. So standard
de-allocation handling would probably need to look like this:

$ptr = allocate MassiveObject;
catch sub {
}, finally => sub {
deallocate MassiveObject $ptr;
};

> Ideally, I'd want a handler to be able to cause one of four things to happen:
> - exception is dismissed; execution resumes in the normal thread at the
> statement just after the one which threw the exception

Via a "resume" call. Which probably will trigger a "last", or something.

> - exception is dismissed; call stack is unwound by a specified number
> of frames, and execution is resumed at the statement just after the
> last call that was unwound

This is ideally the behaviour you get when you fall all the end of an
exception handler. You get taken out of the handler and the related catch
{}. I'd also say that a "finally" handler, if one is given, will get
execute somewhere in the middle of this.

> - exception is passed out to the next handler

A rethrow. Probably implemented with a "next", or other magic. A question:
if the next (next innermost, actually) handler "resumes", can it get back
to the code that originally threw the exception? If so, we can't unwind
the call stack (and execute our "finally" blocks) until we know for
certain that that isn't possible. This probably means that a handler might
get some lexical variables it wasn't expecting, if it's called from far
enough "up" the handler chain.

> - a new exception is thrown, which is handled as a new, "nested" event
> If it is eventually dismissed, handling of the current exception
> is resumed, as though it were the normal execution path, and
> so on . . .

Or perhaps this is a rethrow? I've never dealt with a language that
supports resumption of exceptions, so I'm sure I'm missing some
subtleties of implementation here.

But I suppose a handler could include a catch block, complete with it's
own exception handlers, that could, if it contained an unhandled execption
"leak" out to the original exception chain.

> Regards,
> Charles Bailey bailey@genetics.upenn.edu

--
Kenneth Albanowski (kjahds@kjahds.com, CIS: 70705,126)
Re: $SIG{__DIE__} (was Re: Over-zealous warnings ignore eval) [ In reply to ]
On Wed, 13 Sep 1995, Charles Bailey wrote:

> Sorta closer, though still a kludge:
> $ perl
> $SIG{__DIE__} = sub { $__excpt = 0; goto recover; };
> die "oops";
> recover:
> print "wow\n";
> END { exit 0 unless $__excpt; }
> the idea here being that
> if one wants to exit now,
> one just doesn't C<goto> a label in the main thread,
> elsif one wants to go back to normal,
> one sets $__excpt = 0 and executes a C<goto> back into the main thread,
> elsif one wants to recover, but report the error out
> one sets $__excpt = !0 and executes a C<goto> back into the main thread
> endif.

Good grief, that's frightful. I know perl includes some remanents of
BASIC, but do you really want to build something based on goto's?

> Regards,
> Charles Bailey bailey@genetics.upenn.edu

--
Kenneth Albanowski (kjahds@kjahds.com, CIS: 70705,126)
Re: $SIG{__DIE__} (was Re: Over-zealous warnings ignore eval) [ In reply to ]
On Wed, 13 Sep 1995, Larry Wall wrote:

> : On Wed, 13 Sep 1995, Larry Wall wrote:
> :
> : > Here's a wacky one:
> : >=20
> : > $ ./perl
> : > $SIG{__DIE__} =3D sub { goto recover };
> : > die "oops";
> : > recover:
> : > print "wow\n";
> : > ^D
> : > wow
> : > oops at - line 2.
> : >=20
> : > Talk about snatching defeat from the jaws of victory...
> :
> : That looks like a flushing conflict. But it evidentally isn't reliable=20
> : behaviour. I get this with 5.001m (no patches):
> :
> : =09wow
> : =09=E0=E9
>
> Gad, I hate MIME.

My, I'm surprised. This is the first time PINE has used quoted-printable.
Or at least the first time I've seen it... Usually I see it when some
digest has been unpacked without MIME headers and I get the =.. garbage.

> There is a buffer conflict there. The more basic problem is that
> there's a runlevel confusion. The "goto" should realize that it has to
> longjmp() out of the die, but doesn't. So it ends up doing the
> equivalent of
>
> $SIG{__DIE__} = sub { print "wow" };

Yes, I can see how that happens. I'm less certain about whether one would
_want_ it to happen. :-)

Also in a slightly related note, I've been slightly worried about what
happens during END {} processing, and whether it might be called inside
STDIO. If that's the case, killing a program that includes C<END {
ReadMode 0; # reset terminal }> via a signal might cause a bit more havoc
then would be expected.

> Larry

--
Kenneth Albanowski (kjahds@kjahds.com, CIS: 70705,126)

1 2  View All