Even more simplifications of the PP callback
[p5sagit/namespace-clean.git] / lib / namespace / clean / _PP_OSE.pm
CommitLineData
b2e54862 1package # hide from the pauses
2 namespace::clean::_PP_OSE;
3
4use warnings;
5use strict;
6
b2e54862 7use Tie::Hash;
8use Hash::Util::FieldHash 'fieldhash';
9
aabe8f1c 10# Here we rely on a combination of several behaviors:
11#
12# * %^H is deallocated on scope exit, so any references to it disappear
13# * A lost weakref in a fieldhash causes the corresponding key to be deleted
14# * Deletion of a key on a tied hash triggers DELETE
15#
16# Therefore the DELETE of a tied fieldhash containing a %^H reference will
17# be the hook to fire all our callbacks.
18#
19# The SUPER:: gimmick is there to ensure the fieldhash is cleaned up in a
20# timely manner. When you call delete() in non-void context, you get a mortal
21# scalar whose reference count decreases at the end of the current statement.
22# During scope exit, ‘statement’ is not clearly defined, so more scope
23# unwinding could happen before the mortal gets freed. Forcing the DELETE
24# in void context localizes the life of the mortal scalar.
b2e54862 25
26fieldhash my %hh;
b2e54862 27{
28 package namespace::clean::_TieHintHashFieldHash;
29 use base 'Tie::StdHash';
30 sub DELETE {
aabe8f1c 31 $_->() for @{ $_[0]->{$_[1]} };
b2e54862 32 shift->SUPER::DELETE(@_);
aabe8f1c 33 1; # put the preceding statement in void context so the free is immediate
b2e54862 34 }
35}
36
b2e54862 37sub on_scope_end (&) {
38 $^H |= 0x020000;
39
40 tie(%hh, 'namespace::clean::_TieHintHashFieldHash')
41 unless tied %hh;
42
aabe8f1c 43 push @{ $hh{\%^H} ||= [] }, shift;
b2e54862 44}
45
461;