Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Setting $! during DESTROY clobbers exit value #15152

Closed
p5pRT opened this issue Jan 27, 2016 · 34 comments
Closed

Setting $! during DESTROY clobbers exit value #15152

p5pRT opened this issue Jan 27, 2016 · 34 comments

Comments

@p5pRT
Copy link

p5pRT commented Jan 27, 2016

Migrated from rt.perl.org#127386 (status was 'resolved')

Searchable as RT127386$

@p5pRT
Copy link
Author

p5pRT commented Jan 27, 2016

From @FGasper

Created by @FGasper

The docs say​:

If an uncaught exception results in interpreter exit, the exit
code is determined from the values of $! and $? with this
pseudocode​:

  exit $! if $!; # errno
  exit $? >> 8 if $? >> 8; # child exit status
  exit 255; # last resort

... however, the below shows an example of where $! is not truthy,
yet the uncaught exception produces a 0 exit value​:

---------------
use strict;
use warnings;

{
  my $f = Finally->new( sub { system $^X, '-e', 'exit 1' } );
  die;
};

package Finally;

sub new { bless [ $_[1] ] }

sub DESTROY { $_[0]->[0]->() }
---------------

Per the documentation, Perl should be exiting 255, not 0.

Perl Info

Flags:
    category=core
    severity=medium

This perlbug was built using Perl 5.10.1 in the Fedora build system.
It is being executed now by Perl 5.10.1 - Sun Nov  6 00:37:43 GMT 2011.

Site configuration information for perl 5.10.1:

Configured by Red Hat, Inc. at Sun Nov  6 00:37:43 GMT 2011.

Summary of my perl5 (revision 5 version 10 subversion 1) configuration:
   
  Platform:
    osname=linux, osvers=2.6.32-44.2.el6.x86_64, archname=x86_64-linux-thread-multi
    uname='linux c6b5.bsys.dev.centos.org 2.6.32-44.2.el6.x86_64 #1 smp wed jul 21 12:48:32 edt 2010 x86_64 x86_64 x86_64 gnulinux '
    config_args='-des -Doptimize=-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic -DDEBUGGING=-g -Dversion=5.10.1 -Dmyhostname=localhost -Dperladmin=root@localhost -Dcc=gcc -Dcf_by=Red Hat, Inc. -Dprefix=/usr -Dvendorprefix=/usr -Dsiteprefix=/usr/local -Dsitelib=/usr/local/share/perl5 -Dsitearch=/usr/local/lib64/perl5 -Dprivlib=/usr/share/perl5 -Darchlib=/usr/lib64/perl5 -Dvendorlib=/usr/share/perl5/vendor_perl -Dvendorarch=/usr/lib64/perl5/vendor_perl -Dinc_version_list=5.10.0 -Darchname=x86_64-linux-thread-multi -Dlibpth=/usr/local/lib64 /lib64 /usr/lib64 -Duseshrplib -Dusethreads -Duseithreads -Duselargefiles -Dd_dosuid -Dd_semctl_semun -Di_db -Ui_ndbm -Di_gdbm -Di_shadow -Di_syslog -Dman3ext=3pm -Duseperlio -Dinstallusrbinperl=n -Ubincompat5005 -Uversiononly -Dpager=/usr/bin/less -isr -Dd_gethostent_r_proto -Ud_endhostent_r_proto -Ud_sethostent_r_proto -Ud_endprotoent_r_proto -Ud_setprotoent_r_proto -Ud_endservent_r_proto -Ud_setservent_r_proto -Dscriptdir=/usr/bin'
    hint=recommended, useposix=true, d_sigaction=define
    useithreads=define, usemultiplicity=define
    useperlio=define, d_sfio=undef, uselargefiles=define, usesocks=undef
    use64bitint=define, use64bitall=define, uselongdouble=undef
    usemymalloc=n, bincompat5005=undef
  Compiler:
    cc='gcc', ccflags ='-D_REENTRANT -D_GNU_SOURCE -fno-strict-aliasing -pipe -fstack-protector -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64',
    optimize='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic',
    cppflags='-D_REENTRANT -D_GNU_SOURCE -fno-strict-aliasing -pipe -fstack-protector -I/usr/local/include'
    ccversion='', gccversion='4.4.5 20110214 (Red Hat 4.4.5-6)', gccosandvers=''
    intsize=4, longsize=8, ptrsize=8, doublesize=8, byteorder=12345678
    d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=16
    ivtype='long', ivsize=8, nvtype='double', nvsize=8, Off_t='off_t', lseeksize=8
    alignbytes=8, prototype=define
  Linker and Libraries:
    ld='gcc', ldflags =' -fstack-protector'
    libpth=/usr/local/lib64 /lib64 /usr/lib64
    libs=-lresolv -lnsl -lgdbm -ldb -ldl -lm -lcrypt -lutil -lpthread -lc
    perllibs=-lresolv -lnsl -ldl -lm -lcrypt -lutil -lpthread -lc
    libc=, so=so, useshrplib=true, libperl=libperl.so
    gnulibc_version='2.12'
  Dynamic Linking:
    dlsrc=dl_dlopen.xs, dlext=so, d_dlsymun=undef, ccdlflags='-Wl,-E -Wl,-rpath,/usr/lib64/perl5/CORE'
    cccdlflags='-fPIC', lddlflags='-shared -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic'

Locally applied patches:
    


@INC for perl 5.10.1:
    /home/fgasper/perl5/lib/perl5
    /home/fgasper/perl5/lib/perl5/x86_64-linux-thread-multi
    /home/fgasper/perl/home/fgasper/perl5/lib/perl5/5.10.1/x86_64-linux-thread-multi
    /home/fgasper/perl/home/fgasper/perl5/lib/perl5/5.10.1
    /home/fgasper/perl/home/fgasper/perl5/lib/perl5/x86_64-linux-thread-multi
    /home/fgasper/perl/home/fgasper/perl5/lib/perl5/5.10.0
    /home/fgasper/perl/home/fgasper/perl5/lib/perl5
    /home/fgasper/perl/usr/local/lib64/perl5
    /home/fgasper/perl/usr/local/share/perl5
    /home/fgasper/perl/usr/lib64/perl5/vendor_perl
    /home/fgasper/perl/usr/share/perl5/vendor_perl
    /home/fgasper/perl/usr/lib64/perl5
    /home/fgasper/perl/usr/share/perl5
    /home/fgasper/perl5/lib/perl5/5.10.1/x86_64-linux-thread-multi
    /home/fgasper/perl5/lib/perl5/5.10.1
    /home/fgasper/perl5/lib/perl5/x86_64-linux-thread-multi
    /home/fgasper/perl5/lib/perl5/5.10.0
    /home/fgasper/perl5/lib/perl5
    /usr/local/lib64/perl5
    /usr/local/share/perl5
    /usr/lib64/perl5/vendor_perl
    /usr/share/perl5/vendor_perl
    /usr/lib64/perl5
    /usr/share/perl5
    .


Environment for perl 5.10.1:
    HOME=/home/fgasper
    LANG (unset)
    LANGUAGE (unset)
    LD_LIBRARY_PATH (unset)
    LOGDIR (unset)
    PATH=/home/fgasper/perl5/bin:/usr/local/jdk/bin:/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin:/usr/local/bin:/usr/X11R6/bin:/opt/dell/srvadmin/bin:/usr/local/cpanel/3rdparty/bin:/home/fgasper/bin
    PERL5LIB=/home/fgasper/perl5/lib/perl5
    PERL_BADLANG (unset)
    PERL_LOCAL_LIB_ROOT=/home/fgasper/perl5
    PERL_MB_OPT=--install_base "/home/fgasper/perl5"
    PERL_MM_OPT=INSTALL_BASE=/home/fgasper/perl5
    SHELL=/bin/bash

@p5pRT
Copy link
Author

p5pRT commented Jan 27, 2016

From @FGasper

This affects every Perl version I have tried.

@p5pRT
Copy link
Author

p5pRT commented Jan 27, 2016

From [Unknown Contact. See original ticket]

This affects every Perl version I have tried.

@p5pRT
Copy link
Author

p5pRT commented Jan 28, 2016

From @tonycoz

On Tue Jan 26 21​:33​:43 2016, felipe@​felipegasper.com wrote​:

If an uncaught exception results in interpreter exit, the exit
code is determined from the values of $! and $? with this
pseudocode​:

exit $! if $!; # errno
exit $? >> 8 if $? >> 8; # child exit status
exit 255; # last resort

... however, the below shows an example of where $! is not truthy,
yet the uncaught exception produces a 0 exit value​:

---------------
use strict;
use warnings;

{
my $f = Finally->new( sub { system $^X, '-e', 'exit 1' } );
die;
};

package Finally;

sub new { bless [ $_[1] ] }

sub DESTROY { $_[0]->[0]->() }
---------------

Per the documentation, Perl should be exiting 255, not 0.

This is a bug in your code.

When you call die(), perl sets $? per the algorithm you describe (see Perl_my_failure_exit() in perl.c), and then it unwinds the stack.

In your case you call system(), which sets $? to the value returned by wait(), which is 256 in this case.

Since POSIX exit() only looks at the bottom 8 bits of the value your program returns 0.

Tony

@p5pRT
Copy link
Author

p5pRT commented Jan 28, 2016

The RT System itself - Status changed from 'new' to 'open'

@p5pRT
Copy link
Author

p5pRT commented Jan 28, 2016

From @FGasper

On 27 Jan 2016 9​:26 PM, Tony Cook via RT wrote​:

On Tue Jan 26 21​:33​:43 2016, felipe@​felipegasper.com wrote​:

If an uncaught exception results in interpreter exit, the exit
code is determined from the values of $! and $? with this
pseudocode​:

exit $! if $!; # errno
exit $? >> 8 if $? >> 8; # child exit status
exit 255; # last resort

... however, the below shows an example of where $! is not truthy,
yet the uncaught exception produces a 0 exit value​:

---------------
use strict;
use warnings;

{
my $f = Finally->new( sub { system $^X, '-e', 'exit 1' } );
die;
};

package Finally;

sub new { bless [ $_[1] ] }

sub DESTROY { $_[0]->[0]->() }
---------------

Per the documentation, Perl should be exiting 255, not 0.

This is a bug in your code.

When you call die(), perl sets $? per the algorithm you describe (see Perl_my_failure_exit() in perl.c), and then it unwinds the stack.

In your case you call system(), which sets $? to the value returned by wait(), which is 256 in this case.

Since POSIX exit() only looks at the bottom 8 bits of the value your program returns 0.

Tony

($? >> 8) is not truthy when $? == 255, so Perl should be exiting 255 …
per that algorithm.

@p5pRT
Copy link
Author

p5pRT commented Jan 28, 2016

From @tonycoz

On Wed, Jan 27, 2016 at 11​:10​:22PM -0500, Felipe Gasper wrote​:

($? >> 8) is not truthy when $? == 255, so Perl should be exiting 255 … per
that algorithm.

$? is initially set to 255 by die() using the algorithm you
described, since neither $! nor $? are truthy.

Later on, after the algorithm has set $?, you set $? to 256, and
that's used as the value passed to exit().

Tony

@p5pRT
Copy link
Author

p5pRT commented Jan 28, 2016

From @FGasper

On 27 Jan 2016 11​:30 PM, Tony Cook via RT wrote​:

On Wed, Jan 27, 2016 at 11​:10​:22PM -0500, Felipe Gasper wrote​:

($? >> 8) is not truthy when $? == 255, so Perl should be exiting 255 … per
that algorithm.

$? is initially set to 255 by die() using the algorithm you
described, since neither $! nor $? are truthy.

Wait, why would $? be set? $? is for a child process. die() has no
business setting $?.

Later on, after the algorithm has set $?, you set $? to 256, and
that's used as the value passed to exit().

Not according to perldoc -f die​:

exit $? >> 8 if $? >> 8; # child exit status

($? >> 8) is 1, so it should be doing exit(1), not exit(0).

I actually pasted the wrong sample code earlier; I meant to post
something with “exit 0”, which produces the same behavior.

The bottom line is that, per the algorithm described in perldoc -f die,
Perl should never exit(0) from an uncaught exception.

-FG

@p5pRT
Copy link
Author

p5pRT commented Jan 28, 2016

From @tonycoz

On Wed, Jan 27, 2016 at 11​:37​:49PM -0500, Felipe Gasper wrote​:

On 27 Jan 2016 11​:30 PM, Tony Cook via RT wrote​:

On Wed, Jan 27, 2016 at 11​:10​:22PM -0500, Felipe Gasper wrote​:

($? >> 8) is not truthy when $? == 255, so Perl should be exiting 255 … per
that algorithm.

$? is initially set to 255 by die() using the algorithm you
described, since neither $! nor $? are truthy.

Wait, why would $? be set? $? is for a child process. die() has no business
setting $?.

Later on, after the algorithm has set $?, you set $? to 256, and
that's used as the value passed to exit().

Not according to perldoc -f die​:

exit $? >> 8 if $? >> 8; # child exit status

($? >> 8) is 1, so it should be doing exit(1), not exit(0).

I actually pasted the wrong sample code earlier; I meant to post something
with “exit 0”, which produces the same behavior.

The bottom line is that, per the algorithm described in perldoc -f die, Perl
should never exit(0) from an uncaught exception.

Both die() or exit(), set $? to the exit value and then starts
clean-up, including running destructors and END blocks. Once clean-up
is done perl pretty much calls exit($?) at the C level.

This isn't explicitly documented for die(), but they both manage the
exit status the same way.

Tony

@p5pRT
Copy link
Author

p5pRT commented Jan 28, 2016

From @FGasper

On 27 Jan 2016 11​:49 PM, Tony Cook wrote​:

On Wed, Jan 27, 2016 at 11​:37​:49PM -0500, Felipe Gasper wrote​:

On 27 Jan 2016 11​:30 PM, Tony Cook via RT wrote​:

On Wed, Jan 27, 2016 at 11​:10​:22PM -0500, Felipe Gasper wrote​:

($? >> 8) is not truthy when $? == 255, so Perl should be exiting 255 … per
that algorithm.

$? is initially set to 255 by die() using the algorithm you
described, since neither $! nor $? are truthy.

Wait, why would $? be set? $? is for a child process. die() has no business
setting $?.

Later on, after the algorithm has set $?, you set $? to 256, and
that's used as the value passed to exit().

Not according to perldoc -f die​:

exit $? >> 8 if $? >> 8; # child exit status

($? >> 8) is 1, so it should be doing exit(1), not exit(0).

I actually pasted the wrong sample code earlier; I meant to post something
with “exit 0”, which produces the same behavior.

The bottom line is that, per the algorithm described in perldoc -f die, Perl
should never exit(0) from an uncaught exception.

Both die() or exit(), set $? to the exit value and then starts
clean-up, including running destructors and END blocks. Once clean-up
is done perl pretty much calls exit($?) at the C level.

This isn't explicitly documented for die(), but they both manage the
exit status the same way.

By definition, then, that is an errant implementation of the
documentation, which does not allow for 0 as an exit value on an
untrapped exception.

-FG

@p5pRT
Copy link
Author

p5pRT commented Jan 28, 2016

From zefram@fysh.org

Felipe Gasper wrote​:

By definition, then, that is an errant implementation of the
documentation, which does not allow for 0 as an exit value on an
untrapped exception.

You're missing the point. It is documented (in perlvar(1)) that $? can
be changed during destruction to affect the exit code that is going
to be used. A non-zero exit code is being correctly generated for the
uncaught exception, but by calling system() during destruction you are
then overriding that, replacing it with a zero exit code. You need to
either not call system() during destruction or localise $? when doing so.

It is poor design that $? has these two different uses. Especially so
that the two uses involve values with conflictingly different structures,
so that you can't meaningfully leave a value in $? to feed it straight
from one use to the other. But there's no bug here.

-zefram

@p5pRT
Copy link
Author

p5pRT commented Jan 28, 2016

From @Leont

On Thu, Jan 28, 2016 at 11​:48 PM, Zefram <zefram@​fysh.org> wrote​:

It is poor design that $? has these two different uses. Especially so
that the two uses involve values with conflictingly different structures,
so that you can't meaningfully leave a value in $? to feed it straight
from one use to the other. But there's no bug here.

Yes, that :-/

I'm wondering what the effect would be of exiting with $? &amp; 0xFF || $? >>
8. It would make more sense, but likely something would be broken.

Leon

@p5pRT
Copy link
Author

p5pRT commented Jan 29, 2016

From @FGasper

On 28 Jan 2016 5​:49 PM, Zefram via RT wrote​:

Felipe Gasper wrote​:

It is poor design that $? has these two different uses. Especially so
that the two uses involve values with conflictingly different structures,
so that you can't meaningfully leave a value in $? to feed it straight
from one use to the other. But there's no bug here.

Hm.

To where may I submit a pull request to add the following to perldoc -f die​:


Note that the determination of which variable to use for the exit value
happens when the to-be-untrapped exception is first thrown. That
variable is not actually *read* for its exit value until the process
actually exits. This can cause Perl to exit 0 on an untrapped exception,
e.g., if ($? >> 8) is nonzero at first but then becomes zero at some
point prior to the end of the process.


-FG

@p5pRT
Copy link
Author

p5pRT commented Jan 29, 2016

From @FGasper

On 28 Jan 2016 6​:09 PM, Leon Timmermans via RT wrote​:

On Thu, Jan 28, 2016 at 11​:48 PM, Zefram <zefram@​fysh.org> wrote​:

It is poor design that $? has these two different uses. Especially so
that the two uses involve values with conflictingly different structures,
so that you can't meaningfully leave a value in $? to feed it straight
from one use to the other. But there's no bug here.

Yes, that :-/

I'm wondering what the effect would be of exiting with $? &amp; 0xFF || $? >>
8. It would make more sense, but likely something would be broken.

For that matter, why not read $? and $! to determine exit value when the
exception reaches the end of the stack rather than when the exception is
first thrown?

I can’t fathom what would depend on the behavior that Perl determines to
use $? at a significant distance from when it actually reads $? for the
exit() value. That’s probably just naïveté on my part, but hey.

-F

@p5pRT
Copy link
Author

p5pRT commented Jan 29, 2016

From zefram@fysh.org

Felipe Gasper wrote​:

                   This can cause Perl to exit 0 on an untrapped

exception, e.g., if ($? >> 8) is nonzero at first but then becomes
zero at some point prior to the end of the process.

That's not quite right. $?>>8 is relevant when initially determining
the exit code caused by the exception, but subsequent changes to that
part of $? are not relevant. Your text is also devoid of reference to
the actual mechanism by which the exit code can be changed. A better
explanatory text would be something like

  As with an ordinary call to C<exit>, the exit code generated
  by an uncaught exception can be examined in C<$?>, and altered
  by writing to C<$?>, by code that runs during unwinding and
  destruction. Such code can even directly terminate the process,
  by means other than a normal exit.

-zefram

@p5pRT
Copy link
Author

p5pRT commented Jan 29, 2016

From zefram@fysh.org

Felipe Gasper wrote​:

For that matter, why not read $? and $! to determine exit value when
the exception reaches the end of the stack rather than when the
exception is first thrown?

The more local that process is to the exception throwing, the better
chance there is that the values in those variables reflect something
useful. $! is somewhat at risk of being clobbered by destructor code.
Also, delaying the read of $? would conflict with the documented use of
$? during exit to advertise (and permit mutation of) the exit code to
be used.

I can't fathom what would depend on the behavior that Perl
determines to use $? at a significant distance from when it actually
reads $? for the exit() value.

Consider a program that has a strict exit code convention. It might
well use an END block to set $?, to make sure it always adheres to
the convention. That would specifically be useful for exits caused by
uncaught exceptions, where the program is not initially in control of
the exit code. I'd be inclined to instead wrap the outermost code in
an eval{}, catch the exception, and exit with the proper code without
using the $? protocol, but that wouldn't handle exceptions that arise
during compilation. An END block can manage even that case.

-zefram

@p5pRT
Copy link
Author

p5pRT commented Jan 29, 2016

From @FGasper

On 28 Jan 2016 9​:00 PM, Zefram via RT wrote​:

Felipe Gasper wrote​:

                    This can cause Perl to exit 0 on an untrapped

exception, e.g., if ($? >> 8) is nonzero at first but then becomes
zero at some point prior to the end of the process.

That's not quite right. $?>>8 is relevant when initially determining
the exit code caused by the exception, but subsequent changes to that
part of $? are not relevant. Your text is also devoid of reference to
the actual mechanism by which the exit code can be changed. A better
explanatory text would be something like

As with an ordinary call to C\<exit>\, the exit code generated
by an uncaught exception can be examined in C\<$?>\, and altered
by writing to C\<$?>\, by code that runs during unwinding and
destruction\.  Such code can even directly terminate the process\,
by means other than a normal exit\.

I think the disconnect, at least for myself, is the idea that an
“uncaught exception” is “uncaught” at the moment that it’s thrown.

Evaluating $! and $? at the moment the exception is thrown seems
pointless since those variables can still change, and, while I guess we
can consider the exception to be “uncaught” merely by virtue of $^S
being false-y, the exception propagation has yet to “escape” the call stack.

Really, I still think this behavior violates the documentation. The
documentation doesn’t describe determining, at throw time, which
variable is to determine the exit code at exit time, which is what the
current throw-time check of $! and ($? >> 8) accomplishes. The
documentation describes the determination of the exit code itself,
simply. The pseudocode in particular strongly implies that the check of
$! and $? immediately precedes the exit().

Would there be a way to make it work that way?

Alternatively to an additional blurb about how $! and $? can change
during shutdown, perhaps the pseudocode could be rewritten more
accurately as something like​:


my $use_bang = $!;
my $use_question = !$use_bang && ($? >> 8);

propagate_exception_and_run_END_blocks();

exit( $use_bang ? $! : $use_question ? $? : 255 );


-F

@p5pRT
Copy link
Author

p5pRT commented Jan 29, 2016

From @FGasper

On 28 Jan 2016 9​:08 PM, Zefram via RT wrote​:

I can't fathom what would depend on the behavior that Perl
determines to use $? at a significant distance from when it actually
reads $? for the exit() value.

Consider a program that has a strict exit code convention. It might
well use an END block to set $?, to make sure it always adheres to
the convention. That would specifically be useful for exits caused by
uncaught exceptions, where the program is not initially in control of
the exit code. I'd be inclined to instead wrap the outermost code in
an eval{}, catch the exception, and exit with the proper code without
using the $? protocol, but that wouldn't handle exceptions that arise
during compilation. An END block can manage even that case.

It makes sense, of course, to preserve that functionality.

My thinking is that reading $! and $? before we’ve traversed up the call
stack serves no useful end. It would seem more sensible to do​:

1) call stack reverse propagation
2) *then*, check $! and $?, set $?.
3) Do END blocks, allowing for the documented behavior of $?.

?

-FG

@p5pRT
Copy link
Author

p5pRT commented Jan 29, 2016

From zefram@fysh.org

Felipe Gasper wrote​:

I think the disconnect, at least for myself, is the idea that an
"uncaught exception" is "uncaught" at the moment that it's
thrown.

Yes, that's counter-intuitive. Possibly worth some explication.

Evaluating $! and $? at the moment the exception is thrown seems
pointless since those variables can still change,

That's exactly why we evaluate them at exception throwing. We want the
exit code (barring overrides) to reflect the exception that is causing
termination. We therefore want to pick up these status variables as
they are at the point the exception arises, and the closest we can
get to that is where it's thrown. If the ultimate exit code were to
reflect a $! value that was set in passing by successful cleanup code,
that would be much less informative.

   The pseudocode in particular strongly implies that the check

of $! and $? immediately precedes the exit().

It does. You have to understand the exit() there as a Perl exit(),
not a C exit(). Since the code shown is clearly Perl code, referring
to the magical Perl variables, this is reasonably indicated. A Perl
exit() allows the exit code to be inspected and altered in $?, and
the documentation for exit() does mention that possibility. I think
it's worth explicating that issue in the die() documentation, but it is
already referenced via exit(), so the arrangement is not contrary to the
current documentation. (Also, the documentation for $? doesn't say that
the ability to alter the exit code doesn't apply to exits resulting from
uncaught exceptions.)

            perhaps the pseudocode could be rewritten more

accurately as something like​:

No, it's accurate as it is.

-zefram

@p5pRT
Copy link
Author

p5pRT commented Jan 29, 2016

From @FGasper

On 29 Jan 2016 11​:01 AM, Zefram via RT wrote​:

Felipe Gasper wrote​:

I think the disconnect, at least for myself, is the idea that an
"uncaught exception" is "uncaught" at the moment that it's
thrown.

Yes, that's counter-intuitive. Possibly worth some explication.

             perhaps the pseudocode could be rewritten more

accurately as something like​:

No, it's accurate as it is.

Perhaps, but misleading, particularly when the lead-in phrase is​:

If an uncaught exception results in interpreter exit, the exit code is
determined from the values of $! and $? …

That does not sound (to me) like it’s talking about Perl’s exit();
“results in interpreter exit” sounds like a C exit().

Perhaps​:


If an exception is thrown outside of an eval {}, then prior to exception
propagation Perl will set $? according to the following pseudocode​: { …
} The value of $? after propagation (including any DESTROY handlers) and
END blocks determines Perl’s exit value. (Note that it is, thus,
entirely possible, though likely undesirable, for Perl to exit 0 on an
untrapped exception.)


Particularly phrases like​: “this means that the value of the exit code
used by "die" can be non-predictable … other than to be non-zero.” This
strongly suggests that we should not get a zero exit from an untrapped
exception.

Again, IMO the more helpful workflow would be to examine $? and $!
*after* the exception has finished propagating up the call stack rather
than before. That would reduce (but not eliminate) the chance of an
unintended zero-status on untrapped exception. If that behavior could be
achieved via a pragma somehow, I’d be all for it. Maybe I’m the only one
who cares.

Anyway. I think I’ve said my mind here. Thank you!

-FG

@p5pRT
Copy link
Author

p5pRT commented Feb 3, 2016

From @tonycoz

On Fri Jan 29 08​:28​:10 2016, felipe@​felipegasper.com wrote​:

On 29 Jan 2016 11​:01 AM, Zefram via RT wrote​:

Felipe Gasper wrote​:

I think the disconnect, at least for myself, is the idea that an
"uncaught exception" is "uncaught" at the moment that it's
thrown.

Yes, that's counter-intuitive. Possibly worth some explication.

             perhaps the pseudocode could be rewritten more

accurately as something like​:

No, it's accurate as it is.

Perhaps, but misleading, particularly when the lead-in phrase is​:

If an uncaught exception results in interpreter exit, the exit code is
determined from the values of $! and $? …

That does not sound (to me) like it’s talking about Perl’s exit();
“results in interpreter exit” sounds like a C exit().

Maybe something like the attached.

Tony

@p5pRT
Copy link
Author

p5pRT commented Feb 3, 2016

From @tonycoz

0001-perl-127386-clarify-that-exit-in-the-die-pseudo-code.patch
From 652a3d4dbd81272ff4c5888dbe42829380f7e124 Mon Sep 17 00:00:00 2001
From: Tony Cook <tony@develop-help.com>
Date: Wed, 3 Feb 2016 14:25:04 +1100
Subject: [perl #127386] clarify that exit in the die pseudo-code is perl's
 exit

---
 pod/perlfunc.pod | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/pod/perlfunc.pod b/pod/perlfunc.pod
index 1dba05a..38f2080 100644
--- a/pod/perlfunc.pod
+++ b/pod/perlfunc.pod
@@ -1491,6 +1491,8 @@ If C<$@> is empty then the string C<"Died"> is used.
 If an uncaught exception results in interpreter exit, the exit code is
 determined from the values of C<$!> and C<$?> with this pseudocode:
 
+    # END blocks and destructors can modify $? and hence the program's
+    # exit code as with an other call to exit()
     exit $! if $!;              # errno
     exit $? >> 8 if $? >> 8;    # child exit status
     exit 255;                   # last resort
-- 
2.1.4

@p5pRT
Copy link
Author

p5pRT commented Feb 3, 2016

From @FGasper

On 2 Feb 2016 10​:25 PM, Tony Cook via RT wrote​:

On Fri Jan 29 08​:28​:10 2016, felipe@​felipegasper.com wrote​:

On 29 Jan 2016 11​:01 AM, Zefram via RT wrote​:

Felipe Gasper wrote​:

I think the disconnect, at least for myself, is the idea that an
"uncaught exception" is "uncaught" at the moment that it's
thrown.

Yes, that's counter-intuitive. Possibly worth some explication.

              perhaps the pseudocode could be rewritten more

accurately as something like​:

No, it's accurate as it is.

Perhaps, but misleading, particularly when the lead-in phrase is​:

If an uncaught exception results in interpreter exit, the exit code is
determined from the values of $! and $? …

That does not sound (to me) like it’s talking about Perl’s exit();
“results in interpreter exit” sounds like a C exit().

Maybe something like the attached.

+ # END blocks and destructors can modify $? and hence the program's
+ # exit code as with an other call to exit()

I think this would be helpful, yes. Thank you!

I personally still think stating that die() behaves differently when $^S
is truthy versus otherwise would be sensible​: specifically, that $? gets
set before any DESTROYs/ENDs. Maybe it’s just me, but in my own mind, an
exception isn’t “uncaught” until we’ve finished going back through the
stack.

FWIW, maybe​:


If die() is called outside an eval {}, $? is set prior to propagation of
the exception. Any DESTROY or END handlers can then alter this value,
and thus Perl’s exit code. (die() within an eval {} has no effect on $?.)


-FG

@p5pRT
Copy link
Author

p5pRT commented Feb 8, 2016

From @tonycoz

On Wed Feb 03 02​:34​:26 2016, felipe@​felipegasper.com wrote​:

+ # END blocks and destructors can modify $? and hence the program's
+ # exit code as with an other call to exit()

I think this would be helpful, yes. Thank you!

I personally still think stating that die() behaves differently when $^S
is truthy versus otherwise would be sensible​: specifically, that $? gets
set before any DESTROYs/ENDs. Maybe it’s just me, but in my own mind, an
exception isn’t “uncaught” until we’ve finished going back through the
stack.

FWIW, maybe​:
----------
If die() is called outside an eval {}, $? is set prior to propagation of
the exception. Any DESTROY or END handlers can then alter this value,
and thus Perl’s exit code. (die() within an eval {} has no effect on $?.)
----------

The attached replaces my original patch.

I originally had both my original text and the new text (based on yours), but that seemed unreasonably repetitive.

Tony

@p5pRT
Copy link
Author

p5pRT commented Feb 8, 2016

From @tonycoz

0001-perl-127386-clarify-that-exit-in-the-die-pseudo-code.patch
From 8d70a8fac8cfb008b3e69b5307503b42772ebed0 Mon Sep 17 00:00:00 2001
From: Tony Cook <tony@develop-help.com>
Date: Tue, 9 Feb 2016 09:39:19 +1100
Subject: [perl #127386] clarify that exit in the die pseudo-code is perl's
 exit

---
 pod/perlfunc.pod | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/pod/perlfunc.pod b/pod/perlfunc.pod
index 87c61f4..afdaded 100644
--- a/pod/perlfunc.pod
+++ b/pod/perlfunc.pod
@@ -1495,6 +1495,10 @@ determined from the values of C<$!> and C<$?> with this pseudocode:
     exit $? >> 8 if $? >> 8;    # child exit status
     exit 255;                   # last resort
 
+As with L</exit>, C<$?> is set prior to unwinding the call stack, any
+DESTROY or END handlers can then alter this value, and thus Perl's
+exit code.
+
 The intent is to squeeze as much possible information about the likely cause
 into the limited space of the system exit
 code.  However, as C<$!> is the value
-- 
2.1.4

@p5pRT
Copy link
Author

p5pRT commented Feb 8, 2016

From @FGasper

+As with L</exit>, C<$?> is set prior to unwinding the call stack, any
+DESTROY or END handlers can then alter this value, and thus Perl's
+exit code.

I like it!

One niggle​: it still doesn’t indicate the different behavior when die()
is called from within or outside an eval. Maybe​:


As with L</exit>, L</die> called outside an L</eval> will set C<$?>
prior to unwinding the call stack. Any DESTROY or END handlers can then
alter this value, and thus Perl's exit code.


(I split the sentences to avoid a comma splice. A semicolon could work
for that, too.)

-FG

On 8 Feb 2016 4​:45 PM, Tony Cook via RT wrote​:

On Wed Feb 03 02​:34​:26 2016, felipe@​felipegasper.com wrote​:

+ # END blocks and destructors can modify $? and hence the program's
+ # exit code as with an other call to exit()

I think this would be helpful, yes. Thank you!

I personally still think stating that die() behaves differently when $^S
is truthy versus otherwise would be sensible​: specifically, that $? gets
set before any DESTROYs/ENDs. Maybe it’s just me, but in my own mind, an
exception isn’t “uncaught” until we’ve finished going back through the
stack.

FWIW, maybe​:
----------
If die() is called outside an eval {}, $? is set prior to propagation of
the exception. Any DESTROY or END handlers can then alter this value,
and thus Perl’s exit code. (die() within an eval {} has no effect on $?.)
----------

The attached replaces my original patch.

I originally had both my original text and the new text (based on yours), but that seemed unreasonably repetitive.

Tony

@p5pRT
Copy link
Author

p5pRT commented Feb 8, 2016

From @tonycoz

On Mon Feb 08 14​:51​:51 2016, felipe@​felipegasper.com wrote​:

+As with L</exit>, C<$?> is set prior to unwinding the call stack, any
+DESTROY or END handlers can then alter this value, and thus Perl's
+exit code.

I like it!

One niggle​: it still doesn’t indicate the different behavior when
die()
is called from within or outside an eval. Maybe​:

It's part of the continuing discussion about what happens when there's no eval, I don't think it's necessary to repeat it.

--------
As with L</exit>, L</die> called outside an L</eval> will set C<$?>
prior to unwinding the call stack. Any DESTROY or END handlers can
then
alter this value, and thus Perl's exit code.
--------

(I split the sentences to avoid a comma splice. A semicolon could work
for that, too.)

That END blocks and object destruction happen later follows on from the previous discussion, a semi-colon would work, I don't think a new sentence is appropriate.

Tony

@p5pRT
Copy link
Author

p5pRT commented Feb 8, 2016

From @FGasper

On 8 Feb 2016 5​:08 PM, Tony Cook via RT wrote​:

It's part of the continuing discussion about what happens when
there's no eval, I don't think it's necessary to repeat it.

Perhaps, then, modify the paragraph that begins the section to​:

-If an uncaught exception results in interpreter exit, the exit
-code is determined from the values of $! and $? with this
-pseudocode​:

+If L</die> is called when there is no L</eval> in the call stack,
+Perl sets C<$?> as per the following pseudocode​:

Anyhow, if $? will continue to be set before propagation rather than
after, then this is kind of splitting hairs. I definitely appreciate
Tony’s proposed change.

Thank you!

-F

@p5pRT
Copy link
Author

p5pRT commented Feb 23, 2016

From @tonycoz

On Mon Feb 08 15​:22​:16 2016, felipe@​felipegasper.com wrote​:

Perhaps, then, modify the paragraph that begins the section to​:

-If an uncaught exception results in interpreter exit, the exit
-code is determined from the values of $! and $? with this
-pseudocode​:

+If L</die> is called when there is no L</eval> in the call stack,
+Perl sets C<$?> as per the following pseudocode​:

Anyhow, if $? will continue to be set before propagation rather than
after, then this is kind of splitting hairs. I definitely appreciate
Tony’s proposed change.

I thought about that, and tried a few variations on the text, but didn't
come up with any changes I liked.

I've applied my most recent patch with a change from a comma to a semi-colon as 88aeef8.

Tony

@p5pRT
Copy link
Author

p5pRT commented Feb 23, 2016

@tonycoz - Status changed from 'open' to 'pending release'

@p5pRT
Copy link
Author

p5pRT commented Feb 23, 2016

From @FGasper

On 22 Feb 2016 10​:37 PM, Tony Cook via RT wrote​:

On Mon Feb 08 15​:22​:16 2016, felipe@​felipegasper.com wrote​:

Perhaps, then, modify the paragraph that begins the section to​:

-If an uncaught exception results in interpreter exit, the exit
-code is determined from the values of $! and $? with this
-pseudocode​:

+If L</die> is called when there is no L</eval> in the call stack,
+Perl sets C<$?> as per the following pseudocode​:

Anyhow, if $? will continue to be set before propagation rather than
after, then this is kind of splitting hairs. I definitely appreciate
Tony’s proposed change.

I thought about that, and tried a few variations on the text, but didn't
come up with any changes I liked.

I've applied my most recent patch with a change from a comma to a semi-colon as 88aeef8.

Thank you! :)

-FG

@p5pRT
Copy link
Author

p5pRT commented May 13, 2016

From @khwilliamson

Thank you for submitting this report. You have helped make Perl better.
 
With the release of Perl 5.24.0 on May 9, 2016, this and 149 other issues have been resolved.

Perl 5.24.0 may be downloaded via https://metacpan.org/release/RJBS/perl-5.24.0

@p5pRT
Copy link
Author

p5pRT commented May 13, 2016

@khwilliamson - Status changed from 'pending release' to 'resolved'

@p5pRT p5pRT closed this as completed May 13, 2016
@p5pRT
Copy link
Author

p5pRT commented Jul 31, 2019

From nicolas@atoomic.org

Note that as an extension this program will die with an exit code of 0...
Not really sure what would be the best way to localize $? and preserve a meaningful $? value
... not localizing it... of course avoid the issue

perl -E '{ local $?; die "Boom!" }'; echo $?
Boom! at -e line 1.
0

On Tue, 23 Feb 2016 00​:03​:08 -0800, felipe@​felipegasper.com wrote​:

On 22 Feb 2016 10​:37 PM, Tony Cook via RT wrote​:

On Mon Feb 08 15​:22​:16 2016, felipe@​felipegasper.com wrote​:

Perhaps, then, modify the paragraph that begins the section to​:

-If an uncaught exception results in interpreter exit, the exit
-code is determined from the values of $! and $? with this
-pseudocode​:

+If L</die> is called when there is no L</eval> in the call stack,
+Perl sets C<$?> as per the following pseudocode​:

Anyhow, if $? will continue to be set before propagation rather than
after, then this is kind of splitting hairs. I definitely appreciate
Tony’s proposed change.

I thought about that, and tried a few variations on the text, but
didn't
come up with any changes I liked.

I've applied my most recent patch with a change from a comma to a
semi-colon as 88aeef8.

Thank you! :)

-FG

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant