[comp.lang.perl] Signal handlers, scoping & control confusion

lgy@phys.washington.edu (Laurence G. Yaffe) (03/07/91)

After reading 'the Book' & scanning the man page, I'm still confused about
signal handlers & scoping rules for things like statement labels &
subroutine names.  As an example, consider a structure like:

    {
    loop:
    foreach $file (...)
	{
	$SIG {'INT'} = 'interrupt' ;
	&compare (...) ;

	sub interrupt
	    {
	    last loop if (...) ;
	    }
	}
    return;
    }
    sub compare
	{
	if (...) { &interrupt ;}
	}

(a) The 'last loop' statement inside the handler 'interrupt' does seem
    to work.  If I omit the statement label on the 'last' statement inside
    the handler, then perl occasionally dumps core (this is at PL37).
    Should a label-less 'last', 'redo', etc. statement inside a
    nested subroutine work?  Even if the subroutine is a signal
    handler?  What determines if a statement label is in scope -
    lexical (static) scoping or dynamic scoping?

(b) Calling the 'interrupt' from within the 'compare' routine works.
    Should it?  Although this is very convenient for my purposes,
    I would have expected 'interrupt' not to be in scope within the
    'compare' routine.

--
--------------------------------------------------------------------------
Laurence G. Yaffe		Internet: lgy@newton.phys.washington.edu
University of Washington	Bitnet:   yaffe@uwaphast.bitnet

tchrist@convex.COM (Tom Christiansen) (03/07/91)

From the keyboard of lgy@phys.washington.edu (Laurence G. Yaffe):
:After reading 'the Book' & scanning the man page, I'm still confused about
:signal handlers & scoping rules for things like statement labels &
:subroutine names.  As an example, consider a structure like:

[ i reformatted this for personal legibility --tom]

     sub foo {
     loop:  foreach $file (...) {
	    	$SIG {'INT'} = 'interrupt' ;
		&compare (...) ;
 
		sub interrupt {
		    last loop if (...) ;
		}

	     } # end foreach
	     return;
     }
     sub compare { 
	 if (...) { 
	     &interrupt ;
	 }
     }
 
:(a) The 'last loop' statement inside the handler 'interrupt' does seem
:    to work.  If I omit the statement label on the 'last' statement inside
:    the handler, then perl occasionally dumps core (this is at PL37).

:    Should a label-less 'last', 'redo', etc. statement inside a
:    nested subroutine work?  

It's a shock to me that any of them work.   There's another thread around
here talking about it.  I don't think that a next/last/redo should look up
through your callstack to find a loop.  If there's not one in the current
subr, I think you should be out of luck.

:    Even if the subroutine is a signal handler?  

Signal handlers have a slightly different context than normal
subroutines -- notice how caller() doesn't work right with them.

:    What determines if a statement label is in scope -
:        lexical (static) scoping or dynamic scoping?

I always thought that labels were lexically scoped.  If 
Larry lets this current obfuscation of nexting up through 
subs work, then I guess it would have to be dynamic.

:(b) Calling the 'interrupt' from within the 'compare' routine works.
:    Should it?  Although this is very convenient for my purposes,
:    I would have expected 'interrupt' not to be in scope within the
:    'compare' routine.

I think you're making a bad assumption here.  Subroutine 
declarations do not nest as they do in Pascal.  No matter
where it appears, a subroutine declaration will be globally
visible for the whole package.  It is not like local()s, 
which provide for dynamic scoping.

As for calling interrupt from within your &compare function,
this too is relying on the weird lasting out of a subroutine
behavior.  

You don't need to set $SIG{'INT'} at every loop iteration.
You're not declaring it to be of local scope or anything 
that way.  That handler will be in effect forever that way.
Either reset it, or declare it local:

    { local($SIG{'INT'}) = 'interrupt';

Signal handlers are global unless localized.

It looks very much like you're trying to do some exception
handling here.  In perl, you want to use an eval and a die
for such things.  May I suggest using die/eval, which is perl's
mechanism for this?  Something like this:

    sub interrupt {
       die 'Interrupted';
    }

    sub foo {
	local ( $SIG{'INT'} ) = 'interrupt';
    LOOP:
	foreach $file (...) {
	    &compare (...) ;
	    ....
	 } # end foreach
	 ....
    }

    sub compare { 
	die "Bad compare" if (...);
	....
    }

Now having set if up like this, you call your foo function in an eval, to
trap the dies and see what they were.  (die == raise, eval == catch):


    eval '&foo';
    if ($@) { # caught an exception
	if ($@ =~ /interrupted/i) {
	    warn "interrupted, continuing";
	} elsif ( $@ =~ /bad compare/i) {
	    warn "bad compare, continuing";
	} else {
	    die $@; 
	} 
    }

Notice I don't call exit even if it's something I don't
expect.  I re-raise the exception so someone higher up 
can catch me if he wants.


--tom
--
	I get so tired of utilities with arbitrary, undocumented,
	compiled-in limits.  Don't you?

Tom Christiansen		tchrist@convex.com	convex!tchrist

lwall@jpl-devvax.JPL.NASA.GOV (Larry Wall) (03/07/91)

In article <lgy.668295480@newton> lgy@phys.washington.edu (Laurence G. Yaffe) writes:
: (a) The 'last loop' statement inside the handler 'interrupt' does seem
:     to work.  If I omit the statement label on the 'last' statement inside
:     the handler, then perl occasionally dumps core (this is at PL37).
:     Should a label-less 'last', 'redo', etc. statement inside a
:     nested subroutine work?

Some think it should, and some think it shouldn't.  I think that it should,
but that it's bad programming style to make use of it.  :-)

As Tom mentioned, eval '&some_routine' is the approved method of trapping
exceptions.  Use "die" to raise exceptions.

:     Even if the subroutine is a signal handler?

Next, last and redo are done via setjmp/longjmp, so if you can longjmp out
of a signal handler, Perl should be able to also.

: What determines if a statement label is in scope -
:     lexical (static) scoping or dynamic scoping?

Purely dynamic.  It keeps a label stack and walks up it whenever you refer
to an outer label.  You can even use the same label on different (non-nested)
loops--Perl doesn't care, and will terminate the loop you're in rather than
some other loop.

: (b) Calling the 'interrupt' from within the 'compare' routine works.
:     Should it?  Although this is very convenient for my purposes,
:     I would have expected 'interrupt' not to be in scope within the
:     'compare' routine.

You can put a subroutine declaration anywhere, and it will be visible from
anywhere else within the same package.  Likewise for formats.  The only
way to hide such declarations is with a package declaration, which is Perl's
only static scoping mechanism.

Larry

friedman@chekov.UU.NET (Barry Friedman) (03/07/91)

In article <11708@jpl-devvax.JPL.NASA.GOV> lwall@jpl-devvax.JPL.NASA.GOV (Larry Wall) writes:
>
>As Tom mentioned, eval '&some_routine' is the approved method of trapping
>exceptions.  Use "die" to raise exceptions.
>

An 'exit' issued in an eval'd routine is not trapped.  IMNSHO it should be.

here's an example:

#!/usr/bin/perl

sub test1 { die("bailing out"); }
sub test2 { exit; }

eval "&test1"; print "test1 returned: ",$@,"\n";
eval "&test2"; print "test2 returned: ",$@,"\n";

prints: 

test1 returned: bailing out at /users/friedman/tstp line 6.

--
Barry Friedman                INTERNET: friedman%chekov@uunet.uu.net
Phone: (613) 782-2389         UUCP: ...!uunet!chekov!friedman
Emax Computer Systems Inc.  440 Laurier Ave. W., Ottawa, Ont. Canada K1R 5C4

tchrist@convex.COM (Tom Christiansen) (03/08/91)

From the keyboard of friedman%chekov@uunet.uu.net:
:An 'exit' issued in an eval'd routine is not trapped.  IMNSHO it should be.

It's true.  I've trained myself to use die instead of exit because of that.
I don't know that I agree that it shouldn't really exit though.  I might
want a way out no matter what, and killing myself is inelegant.

--tom
--
	I get so tired of utilities with arbitrary, undocumented,
	compiled-in limits.  Don't you?

Tom Christiansen		tchrist@convex.com	convex!tchrist