Work around a rather bizzare effect of compile flag THREADS_HAVE_PIDS
[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,
df69e815 84 sub {
85 # Perls compiled with THREADS_HAVE_PIDS do not copy end_av properly
86 # between threads, so B::end_av ends up returning a B::SPECIAL and it
87 # goes downhill from there
88 # Install a noop END just to be on the safe side
89 { local $@; eval 'END {}' }
90 $add_endblock->();
91 goto $target
92 },
b0a03550 93 @_,
94 );
95
9aaf3646 96 goto $orig_create;
97 };
b0a03550 98
9aaf3646 99 $before_is_installed = 1;
100}
101
102# just in case threads got loaded after us (silly)
103sub CLONE {
104 unless ($before_is_installed) {
105 require Carp;
106 Carp::croak("You must load the 'threads' module before @{[ __PACKAGE__ ]}");
107 }
108}
a91e8a78 109
9aaf3646 1101; # keep eval happy
111
112PP_IGD
113
114}
115
1161; # keep require happy
117
118
119__END__
a91e8a78 120
121=head1 NAME
122
761f3ee2 123Devel::GlobalDestruction - Expose the flag which marks global
a91e8a78 124destruction.
125
126=head1 SYNOPSIS
127
f832e240 128 package Foo;
129 use Devel::GlobalDestruction;
a91e8a78 130
f832e240 131 use namespace::clean; # to avoid having an "in_global_destruction" method
a91e8a78 132
f832e240 133 sub DESTROY {
134 return if in_global_destruction;
a91e8a78 135
f832e240 136 do_something_a_little_tricky();
137 }
a91e8a78 138
139=head1 DESCRIPTION
140
141Perl's global destruction is a little tricky to deal with WRT finalizers
142because it's not ordered and objects can sometimes disappear.
143
144Writing defensive destructors is hard and annoying, and usually if global
145destruction is happenning you only need the destructors that free up non
146process local resources to actually execute.
147
148For these constructors you can avoid the mess by simply bailing out if global
149destruction is in effect.
150
151=head1 EXPORTS
152
aaccce0c 153This module uses L<Sub::Exporter::Progressive> so the exports may be renamed,
154aliased, etc. if L<Sub::Exporter> is present.
a91e8a78 155
156=over 4
157
158=item in_global_destruction
159
761f3ee2 160Returns true if the interpreter is in global destruction. In perl 5.14+, this
161returns C<${^GLOBAL_PHASE} eq 'DESTRUCT'>, and on earlier perls, it returns the
162current value of C<PL_dirty>.
a91e8a78 163
164=back
165
ec94b9e1 166=head1 AUTHORS
a91e8a78 167
168Yuval Kogman E<lt>nothingmuch@woobling.orgE<gt>
169
ec94b9e1 170Florian Ragwitz E<lt>rafl@debian.orgE<gt>
171
aaa7f60f 172Jesse Luehrs E<lt>doy@tozt.netE<gt>
173
9aaf3646 174Peter Rabbitson E<lt>ribasushi@cpan.orgE<gt>
175
aaccce0c 176Arthur Axel 'fREW' Schmidt E<lt>frioux@gmail.comE<gt>
177
140a3884 178Elizabeth Mattijsen E<lt>liz@dijkmat.nlE<gt>
179
a91e8a78 180=head1 COPYRIGHT
181
f832e240 182 Copyright (c) 2008 Yuval Kogman. All rights reserved
183 This program is free software; you can redistribute
184 it and/or modify it under the same terms as Perl itself.
a91e8a78 185
186=cut