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