Rewrite completely broken pure-perl GD detection under threads
[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
b0a03550 60# one can register a new thread-local END from within a thread, and
9aaf3646 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
b0a03550 63# not really seem to be the case - any END inserted in a CLONE is ignored :(
9aaf3646 64# Hence blatantly hooking threads::create
6e3fd33e 65#
9aaf3646 66if ($INC{'threads.pm'}) {
b0a03550 67 require Scalar::Util;
68
9aaf3646 69 my $orig_create = threads->can('create');
70 no warnings 'redefine';
b0a03550 71
9aaf3646 72 *threads::create = sub {
b0a03550 73 my $class = shift;
74 my $target = shift;
75
76 unless ( (Scalar::Util::reftype($target)||'') eq 'CODE' ) {
77 no strict 'refs';
78 $target = \&{ caller() . "::$target" };
79 }
80
81 @_ = (
82 $class,
83 sub { $add_endblock->(); goto $target },
84 @_,
85 );
86
9aaf3646 87 goto $orig_create;
88 };
b0a03550 89
9aaf3646 90 $before_is_installed = 1;
91}
92
93# just in case threads got loaded after us (silly)
94sub CLONE {
95 unless ($before_is_installed) {
96 require Carp;
97 Carp::croak("You must load the 'threads' module before @{[ __PACKAGE__ ]}");
98 }
99}
a91e8a78 100
9aaf3646 1011; # keep eval happy
102
103PP_IGD
104
105}
106
1071; # keep require happy
108
109
110__END__
a91e8a78 111
112=head1 NAME
113
761f3ee2 114Devel::GlobalDestruction - Expose the flag which marks global
a91e8a78 115destruction.
116
117=head1 SYNOPSIS
118
f832e240 119 package Foo;
120 use Devel::GlobalDestruction;
a91e8a78 121
f832e240 122 use namespace::clean; # to avoid having an "in_global_destruction" method
a91e8a78 123
f832e240 124 sub DESTROY {
125 return if in_global_destruction;
a91e8a78 126
f832e240 127 do_something_a_little_tricky();
128 }
a91e8a78 129
130=head1 DESCRIPTION
131
132Perl's global destruction is a little tricky to deal with WRT finalizers
133because it's not ordered and objects can sometimes disappear.
134
135Writing defensive destructors is hard and annoying, and usually if global
136destruction is happenning you only need the destructors that free up non
137process local resources to actually execute.
138
139For these constructors you can avoid the mess by simply bailing out if global
140destruction is in effect.
141
142=head1 EXPORTS
143
aaccce0c 144This module uses L<Sub::Exporter::Progressive> so the exports may be renamed,
145aliased, etc. if L<Sub::Exporter> is present.
a91e8a78 146
147=over 4
148
149=item in_global_destruction
150
761f3ee2 151Returns true if the interpreter is in global destruction. In perl 5.14+, this
152returns C<${^GLOBAL_PHASE} eq 'DESTRUCT'>, and on earlier perls, it returns the
153current value of C<PL_dirty>.
a91e8a78 154
155=back
156
ec94b9e1 157=head1 AUTHORS
a91e8a78 158
159Yuval Kogman E<lt>nothingmuch@woobling.orgE<gt>
160
ec94b9e1 161Florian Ragwitz E<lt>rafl@debian.orgE<gt>
162
aaa7f60f 163Jesse Luehrs E<lt>doy@tozt.netE<gt>
164
9aaf3646 165Peter Rabbitson E<lt>ribasushi@cpan.orgE<gt>
166
aaccce0c 167Arthur Axel 'fREW' Schmidt E<lt>frioux@gmail.comE<gt>
168
140a3884 169Elizabeth Mattijsen E<lt>liz@dijkmat.nlE<gt>
170
a91e8a78 171=head1 COPYRIGHT
172
f832e240 173 Copyright (c) 2008 Yuval Kogman. All rights reserved
174 This program is free software; you can redistribute
175 it and/or modify it under the same terms as Perl itself.
a91e8a78 176
177=cut