From: Peter Rabbitson Date: Thu, 22 Dec 2011 22:54:09 +0000 (+0100) Subject: Even more simplifications of the PP callback X-Git-Tag: 0.22~3 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=p5sagit%2Fnamespace-clean.git;a=commitdiff_plain;h=aabe8f1c2065fd20f03a08c81cbe1e90e816220d Even more simplifications of the PP callback --- diff --git a/Changes b/Changes index 7c821b3..9ed6044 100644 --- a/Changes +++ b/Changes @@ -1,3 +1,7 @@ + - Simplify the >= 5.10 PP variant even more - move the hook from + DESTROY into DELETE + - Force explicit callback invocation order on 5.8 PP + [0.21_02] - Replace the %^H tie approach with fieldhashes, fixes all known corner cases and caveats on supported perls >= 5.8.1 (FC) diff --git a/lib/namespace/clean/_PP_OSE.pm b/lib/namespace/clean/_PP_OSE.pm index 26fc091..25f44af 100644 --- a/lib/namespace/clean/_PP_OSE.pm +++ b/lib/namespace/clean/_PP_OSE.pm @@ -4,39 +4,43 @@ package # hide from the pauses use warnings; use strict; -use namespace::clean::_PP_SG; use Tie::Hash; use Hash::Util::FieldHash 'fieldhash'; -# Hash::Util::FieldHash is not deleting elements in void context. When -# you call delete() in non-void context, a mortal scalar is returned. A -# mortal scalar is one whose reference count decreases at the end of the -# current statement. During scope exit, ‘statement’ is not clearly -# defined, so more scope unwinding could happen before the mortal gets -# freed. -# By tying it and overriding DELETE, we can force the deletion into -# void context. +# Here we rely on a combination of several behaviors: +# +# * %^H is deallocated on scope exit, so any references to it disappear +# * A lost weakref in a fieldhash causes the corresponding key to be deleted +# * Deletion of a key on a tied hash triggers DELETE +# +# Therefore the DELETE of a tied fieldhash containing a %^H reference will +# be the hook to fire all our callbacks. +# +# The SUPER:: gimmick is there to ensure the fieldhash is cleaned up in a +# timely manner. When you call delete() in non-void context, you get a mortal +# scalar whose reference count decreases at the end of the current statement. +# During scope exit, ‘statement’ is not clearly defined, so more scope +# unwinding could happen before the mortal gets freed. Forcing the DELETE +# in void context localizes the life of the mortal scalar. fieldhash my %hh; - { package namespace::clean::_TieHintHashFieldHash; use base 'Tie::StdHash'; sub DELETE { + $_->() for @{ $_[0]->{$_[1]} }; shift->SUPER::DELETE(@_); - 1; # put the preceding statement in void context + 1; # put the preceding statement in void context so the free is immediate } } - sub on_scope_end (&) { $^H |= 0x020000; tie(%hh, 'namespace::clean::_TieHintHashFieldHash') unless tied %hh; - push @{$hh{\%^H} ||= []}, - namespace::clean::_PP_SG->arm(shift); + push @{ $hh{\%^H} ||= [] }, shift; } 1; diff --git a/lib/namespace/clean/_PP_OSE_5_8.pm b/lib/namespace/clean/_PP_OSE_5_8.pm index 15cc00b..302463c 100644 --- a/lib/namespace/clean/_PP_OSE_5_8.pm +++ b/lib/namespace/clean/_PP_OSE_5_8.pm @@ -4,15 +4,23 @@ package # hide from the pauses use warnings; use strict; -use namespace::clean::_PP_SG; - # This is the original implementation, which sadly is broken # on perl 5.10+ withing string evals sub on_scope_end (&) { $^H |= 0x020000; - push @{$^H{'__namespace::clean__guardstack__'} ||= [] }, - namespace::clean::_PP_SG->arm(shift); + push @{ + $^H{'__namespace::clean__guardstack__'} + ||= bless ([], 'namespace::clean::_PP_SG_STACK') + }, shift; } +package # hide from the pauses + namespace::clean::_PP_SG_STACK; + +use warnings; +use strict; + +sub DESTROY { $_->() for @{$_[0]} } + 1; diff --git a/lib/namespace/clean/_PP_SG.pm b/lib/namespace/clean/_PP_SG.pm deleted file mode 100644 index 768ce41..0000000 --- a/lib/namespace/clean/_PP_SG.pm +++ /dev/null @@ -1,10 +0,0 @@ -package # hide from the pauses - namespace::clean::_PP_SG; - -use warnings; -use strict; - -sub arm { bless [ $_[1] ] } -sub DESTROY { $_[0]->[0]->() } - -1;