Fix pure-perl implementation incorrectly reporting GD during END phase (liz++)
[p5sagit/Devel-GlobalDestruction.git] / lib / Devel / GlobalDestruction.pm
CommitLineData
a91e8a78 1package Devel::GlobalDestruction;
2
3use strict;
4use warnings;
5
1c89491d 6our $VERSION = '0.08';
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}) {
53daa838 16 eval 'sub in_global_destruction () { ${^GLOBAL_PHASE} eq q[DESTRUCT] }';
3790e928 17}
6e3fd33e 18# try to load the xs version if it was compiled
19#
9aaf3646 20elsif (eval {
53daa838 21 require XSLoader;
22 XSLoader::load(__PACKAGE__, $VERSION);
23 1;
9aaf3646 24}) {
53daa838 25 # the eval already installed everything, nothing to do
3790e928 26}
6e3fd33e 27# Not core nor XS
53daa838 28# The whole thing is in an eval to prevent perl from parsing it in the
29# first place under perls where none of this is needed
6e3fd33e 30#
9aaf3646 31else {
53daa838 32 eval <<'PP_IGD' or die $@;
9d5ad143 33
53daa838 34# SpeedyCGI runs END blocks every cycle but somehow keeps object instances
35# hence DIAF
36die("The pure-perl version of @{[__PACKAGE__]} can not function correctly under CGI::SpeedyCGI. "
37 . "Please ensure you have a working compiler, and reinstall @{[__PACKAGE__]} to enable the XS "
38 . "codepath.\n"
39) if $CGI::SpeedyCGI::i_am_speedy;
9d5ad143 40
9aaf3646 41my ($in_global_destruction, $before_is_installed);
a91e8a78 42
41ec1eaf 43sub in_global_destruction () { $in_global_destruction }
9aaf3646 44
140a3884 45# end_av trick suggested by liz++
46require B;
47my $add_endblock = sub {
48 push @{ B::end_av()->object_2svref }, sub { $in_global_destruction = 1 };
49};
50
6e3fd33e 51# This block will fire towards the end of the program execution
140a3884 52# Use it to inject an END block which is guaranteed to run last
53# (as long as something else doesn't inject yet another block in
54# the same manner afterwards, at which point it hardly matters
55# anyway)
6e3fd33e 56#
140a3884 57END { $add_endblock->() }
9aaf3646 58
59# threads do not execute the global ENDs (it would be stupid). However
60# one can register a new END via simple string eval within a thread, and
61# achieve the same result. A logical place to do this would be CLONE, which
62# is claimed to run in the context of the new thread. However this does
63# not really seem to be the case - any END evaled in a CLONE is ignored :(
64# Hence blatantly hooking threads::create
6e3fd33e 65#
9aaf3646 66if ($INC{'threads.pm'}) {
67 my $orig_create = threads->can('create');
68 no warnings 'redefine';
69 *threads::create = sub {
70 { local $@; eval 'END { $in_global_destruction = 1 }' };
71 goto $orig_create;
72 };
73 $before_is_installed = 1;
74}
75
76# just in case threads got loaded after us (silly)
77sub CLONE {
78 unless ($before_is_installed) {
79 require Carp;
80 Carp::croak("You must load the 'threads' module before @{[ __PACKAGE__ ]}");
81 }
82}
a91e8a78 83
9aaf3646 841; # keep eval happy
85
86PP_IGD
87
88}
89
901; # keep require happy
91
92
93__END__
a91e8a78 94
95=head1 NAME
96
761f3ee2 97Devel::GlobalDestruction - Expose the flag which marks global
a91e8a78 98destruction.
99
100=head1 SYNOPSIS
101
f832e240 102 package Foo;
103 use Devel::GlobalDestruction;
a91e8a78 104
f832e240 105 use namespace::clean; # to avoid having an "in_global_destruction" method
a91e8a78 106
f832e240 107 sub DESTROY {
108 return if in_global_destruction;
a91e8a78 109
f832e240 110 do_something_a_little_tricky();
111 }
a91e8a78 112
113=head1 DESCRIPTION
114
115Perl's global destruction is a little tricky to deal with WRT finalizers
116because it's not ordered and objects can sometimes disappear.
117
118Writing defensive destructors is hard and annoying, and usually if global
119destruction is happenning you only need the destructors that free up non
120process local resources to actually execute.
121
122For these constructors you can avoid the mess by simply bailing out if global
123destruction is in effect.
124
125=head1 EXPORTS
126
aaccce0c 127This module uses L<Sub::Exporter::Progressive> so the exports may be renamed,
128aliased, etc. if L<Sub::Exporter> is present.
a91e8a78 129
130=over 4
131
132=item in_global_destruction
133
761f3ee2 134Returns true if the interpreter is in global destruction. In perl 5.14+, this
135returns C<${^GLOBAL_PHASE} eq 'DESTRUCT'>, and on earlier perls, it returns the
136current value of C<PL_dirty>.
a91e8a78 137
138=back
139
ec94b9e1 140=head1 AUTHORS
a91e8a78 141
142Yuval Kogman E<lt>nothingmuch@woobling.orgE<gt>
143
ec94b9e1 144Florian Ragwitz E<lt>rafl@debian.orgE<gt>
145
aaa7f60f 146Jesse Luehrs E<lt>doy@tozt.netE<gt>
147
9aaf3646 148Peter Rabbitson E<lt>ribasushi@cpan.orgE<gt>
149
aaccce0c 150Arthur Axel 'fREW' Schmidt E<lt>frioux@gmail.comE<gt>
151
140a3884 152Elizabeth Mattijsen E<lt>liz@dijkmat.nlE<gt>
153
a91e8a78 154=head1 COPYRIGHT
155
f832e240 156 Copyright (c) 2008 Yuval Kogman. All rights reserved
157 This program is free software; you can redistribute
158 it and/or modify it under the same terms as Perl itself.
a91e8a78 159
160=cut