Make the pure-perl fallback work under -c (RT#78619)
[p5sagit/Devel-GlobalDestruction.git] / lib / Devel / GlobalDestruction.pm
CommitLineData
a91e8a78 1package Devel::GlobalDestruction;
2
3use strict;
4use warnings;
5
23d57d81 6our $VERSION = '0.09';
a91e8a78 7
aaccce0c 8use Sub::Exporter::Progressive -setup => {
53daa838 9 exports => [ qw(in_global_destruction) ],
10 groups => { default => [ -all ] },
a91e8a78 11};
12
6e3fd33e 13# we run 5.14+ - everything is in core
14#
eaac10b5 15if (defined ${^GLOBAL_PHASE}) {
62376bb4 16 eval 'sub in_global_destruction () { ${^GLOBAL_PHASE} eq q[DESTRUCT] }; 1'
17 or die $@;
3790e928 18}
6e3fd33e 19# try to load the xs version if it was compiled
20#
9aaf3646 21elsif (eval {
53daa838 22 require XSLoader;
23 XSLoader::load(__PACKAGE__, $VERSION);
24 1;
9aaf3646 25}) {
53daa838 26 # the eval already installed everything, nothing to do
3790e928 27}
09b12813 28# We need pure-perl and we are running under -c
29# None of the END-block trickery will work, use a global scope guard instead,
30# as it is more than adequate in this situation
31# The whole thing is in an eval to prevent perl from parsing it in the
32# first place where none of this is needed
33#
34elsif ($^C) {
35 eval <<'PP_IGD' or die $@;
36
37 my $in_global_destruction;
38
39 sub in_global_destruction () { $in_global_destruction }
40
41 {
42 package Devel::GlobalDestgruction::_MinusC::ScopeGuard;
43 sub DESTROY { shift->[0]->() };
44 }
45
46 no warnings 'once';
47 $Devel::GlobalDestgruction::_MinusC::guard = bless [sub {
48 $in_global_destruction = 1;
49 }], 'Devel::GlobalDestgruction::_MinusC::ScopeGuard';
50
51 1; # keep eval happy
52
53PP_IGD
54}
6e3fd33e 55# Not core nor XS
53daa838 56# The whole thing is in an eval to prevent perl from parsing it in the
57# first place under perls where none of this is needed
6e3fd33e 58#
9aaf3646 59else {
53daa838 60 eval <<'PP_IGD' or die $@;
9d5ad143 61
53daa838 62# SpeedyCGI runs END blocks every cycle but somehow keeps object instances
63# hence DIAF
64die("The pure-perl version of @{[__PACKAGE__]} can not function correctly under CGI::SpeedyCGI. "
65 . "Please ensure you have a working compiler, and reinstall @{[__PACKAGE__]} to enable the XS "
66 . "codepath.\n"
67) if $CGI::SpeedyCGI::i_am_speedy;
9d5ad143 68
9aaf3646 69my ($in_global_destruction, $before_is_installed);
a91e8a78 70
41ec1eaf 71sub in_global_destruction () { $in_global_destruction }
9aaf3646 72
140a3884 73# end_av trick suggested by liz++
74require B;
75my $add_endblock = sub {
76 push @{ B::end_av()->object_2svref }, sub { $in_global_destruction = 1 };
77};
78
6e3fd33e 79# This block will fire towards the end of the program execution
140a3884 80# Use it to inject an END block which is guaranteed to run last
81# (as long as something else doesn't inject yet another block in
82# the same manner afterwards, at which point it hardly matters
83# anyway)
6e3fd33e 84#
140a3884 85END { $add_endblock->() }
9aaf3646 86
87# threads do not execute the global ENDs (it would be stupid). However
b0a03550 88# one can register a new thread-local END from within a thread, and
9aaf3646 89# achieve the same result. A logical place to do this would be CLONE, which
90# is claimed to run in the context of the new thread. However this does
b0a03550 91# not really seem to be the case - any END inserted in a CLONE is ignored :(
9aaf3646 92# Hence blatantly hooking threads::create
6e3fd33e 93#
9aaf3646 94if ($INC{'threads.pm'}) {
b0a03550 95 require Scalar::Util;
96
9aaf3646 97 my $orig_create = threads->can('create');
98 no warnings 'redefine';
b0a03550 99
9aaf3646 100 *threads::create = sub {
b0a03550 101 my $class = shift;
102 my $target = shift;
103
104 unless ( (Scalar::Util::reftype($target)||'') eq 'CODE' ) {
105 no strict 'refs';
106 $target = \&{ caller() . "::$target" };
107 }
108
109 @_ = (
110 $class,
df69e815 111 sub {
112 # Perls compiled with THREADS_HAVE_PIDS do not copy end_av properly
113 # between threads, so B::end_av ends up returning a B::SPECIAL and it
114 # goes downhill from there
115 # Install a noop END just to be on the safe side
116 { local $@; eval 'END {}' }
117 $add_endblock->();
118 goto $target
119 },
b0a03550 120 @_,
121 );
122
9aaf3646 123 goto $orig_create;
124 };
b0a03550 125
9aaf3646 126 $before_is_installed = 1;
127}
128
129# just in case threads got loaded after us (silly)
130sub CLONE {
131 unless ($before_is_installed) {
132 require Carp;
133 Carp::croak("You must load the 'threads' module before @{[ __PACKAGE__ ]}");
134 }
135}
a91e8a78 136
9aaf3646 1371; # keep eval happy
138
139PP_IGD
140
141}
142
1431; # keep require happy
144
145
146__END__
a91e8a78 147
148=head1 NAME
149
761f3ee2 150Devel::GlobalDestruction - Expose the flag which marks global
a91e8a78 151destruction.
152
153=head1 SYNOPSIS
154
f832e240 155 package Foo;
156 use Devel::GlobalDestruction;
a91e8a78 157
f832e240 158 use namespace::clean; # to avoid having an "in_global_destruction" method
a91e8a78 159
f832e240 160 sub DESTROY {
161 return if in_global_destruction;
a91e8a78 162
f832e240 163 do_something_a_little_tricky();
164 }
a91e8a78 165
166=head1 DESCRIPTION
167
168Perl's global destruction is a little tricky to deal with WRT finalizers
169because it's not ordered and objects can sometimes disappear.
170
171Writing defensive destructors is hard and annoying, and usually if global
172destruction is happenning you only need the destructors that free up non
173process local resources to actually execute.
174
175For these constructors you can avoid the mess by simply bailing out if global
176destruction is in effect.
177
178=head1 EXPORTS
179
aaccce0c 180This module uses L<Sub::Exporter::Progressive> so the exports may be renamed,
181aliased, etc. if L<Sub::Exporter> is present.
a91e8a78 182
183=over 4
184
185=item in_global_destruction
186
761f3ee2 187Returns true if the interpreter is in global destruction. In perl 5.14+, this
188returns C<${^GLOBAL_PHASE} eq 'DESTRUCT'>, and on earlier perls, it returns the
189current value of C<PL_dirty>.
a91e8a78 190
191=back
192
ec94b9e1 193=head1 AUTHORS
a91e8a78 194
195Yuval Kogman E<lt>nothingmuch@woobling.orgE<gt>
196
ec94b9e1 197Florian Ragwitz E<lt>rafl@debian.orgE<gt>
198
aaa7f60f 199Jesse Luehrs E<lt>doy@tozt.netE<gt>
200
9aaf3646 201Peter Rabbitson E<lt>ribasushi@cpan.orgE<gt>
202
aaccce0c 203Arthur Axel 'fREW' Schmidt E<lt>frioux@gmail.comE<gt>
204
140a3884 205Elizabeth Mattijsen E<lt>liz@dijkmat.nlE<gt>
206
a91e8a78 207=head1 COPYRIGHT
208
f832e240 209 Copyright (c) 2008 Yuval Kogman. All rights reserved
210 This program is free software; you can redistribute
211 it and/or modify it under the same terms as Perl itself.
a91e8a78 212
213=cut