Pureperlize
[p5sagit/Devel-GlobalDestruction.git] / lib / Devel / GlobalDestruction.pm
1 package Devel::GlobalDestruction;
2
3 use strict;
4 use warnings;
5
6 our $VERSION = '0.04';
7
8 use Sub::Exporter -setup => {
9     exports => [ qw(in_global_destruction) ],
10     groups  => { default => [ -all ] },
11 };
12
13 if (defined ${^GLOBAL_PHASE}) {
14     eval 'sub in_global_destruction () { ${^GLOBAL_PHASE} eq q[DESTRUCT] }';
15 }
16 elsif (eval {
17     require XSLoader;
18     XSLoader::load(__PACKAGE__, $VERSION);
19     1;
20 }) {
21     # the eval already installed everything, nothing to do
22 }
23 else {
24   eval <<'PP_IGD' or die $@;
25
26 my ($in_global_destruction, $before_is_installed);
27
28 sub in_global_destruction { $in_global_destruction }
29
30 END {
31   # SpeedyCGI runs END blocks every cycle but somehow keeps object instances
32   # hence lying about it seems reasonable...ish
33   $in_global_destruction = 1 unless $CGI::SpeedyCGI::i_am_speedy;
34 }
35
36 # threads do not execute the global ENDs (it would be stupid). However
37 # one can register a new END via simple string eval within a thread, and
38 # achieve the same result. A logical place to do this would be CLONE, which
39 # is claimed to run in the context of the new thread. However this does
40 # not really seem to be the case - any END evaled in a CLONE is ignored :(
41 # Hence blatantly hooking threads::create
42
43 if ($INC{'threads.pm'}) {
44   my $orig_create = threads->can('create');
45   no warnings 'redefine';
46   *threads::create = sub {
47     { local $@; eval 'END { $in_global_destruction = 1 }' };
48     goto $orig_create;
49   };
50   $before_is_installed = 1;
51 }
52
53 # just in case threads got loaded after us (silly)
54 sub CLONE {
55   unless ($before_is_installed) {
56     require Carp;
57     Carp::croak("You must load the 'threads' module before @{[ __PACKAGE__ ]}");
58   }
59 }
60
61 1;  # keep eval happy
62
63 PP_IGD
64
65 }
66
67 1;  # keep require happy
68
69
70 __END__
71
72 =head1 NAME
73
74 Devel::GlobalDestruction - Expose the flag which marks global
75 destruction.
76
77 =head1 SYNOPSIS
78
79     package Foo;
80     use Devel::GlobalDestruction;
81
82     use namespace::clean; # to avoid having an "in_global_destruction" method
83
84     sub DESTROY {
85         return if in_global_destruction;
86
87         do_something_a_little_tricky();
88     }
89
90 =head1 DESCRIPTION
91
92 Perl's global destruction is a little tricky to deal with WRT finalizers
93 because it's not ordered and objects can sometimes disappear.
94
95 Writing defensive destructors is hard and annoying, and usually if global
96 destruction is happenning you only need the destructors that free up non
97 process local resources to actually execute.
98
99 For these constructors you can avoid the mess by simply bailing out if global
100 destruction is in effect.
101
102 =head1 EXPORTS
103
104 This module uses L<Sub::Exporter> so the exports may be renamed, aliased, etc.
105
106 =over 4
107
108 =item in_global_destruction
109
110 Returns true if the interpreter is in global destruction. In perl 5.14+, this
111 returns C<${^GLOBAL_PHASE} eq 'DESTRUCT'>, and on earlier perls, it returns the
112 current value of C<PL_dirty>.
113
114 =back
115
116 =head1 VERSION CONTROL
117
118 This module is maintained using Darcs. You can get the latest version from
119 L<http://nothingmuch.woobling.org/code>, and use C<darcs send> to commit
120 changes.
121
122 =head1 AUTHORS
123
124 Yuval Kogman E<lt>nothingmuch@woobling.orgE<gt>
125
126 Florian Ragwitz E<lt>rafl@debian.orgE<gt>
127
128 Jesse Luehrs E<lt>doy@tozt.netE<gt>
129
130 Peter Rabbitson E<lt>ribasushi@cpan.orgE<gt>
131
132 =head1 COPYRIGHT
133
134     Copyright (c) 2008 Yuval Kogman. All rights reserved
135     This program is free software; you can redistribute
136     it and/or modify it under the same terms as Perl itself.
137
138 =cut