Even more simplifications of the PP callback
Peter Rabbitson [Thu, 22 Dec 2011 22:54:09 +0000 (23:54 +0100)]
Changes
lib/namespace/clean/_PP_OSE.pm
lib/namespace/clean/_PP_OSE_5_8.pm
lib/namespace/clean/_PP_SG.pm [deleted file]

diff --git a/Changes b/Changes
index 7c821b3..9ed6044 100644 (file)
--- 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)
index 26fc091..25f44af 100644 (file)
@@ -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;
index 15cc00b..302463c 100644 (file)
@@ -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 (file)
index 768ce41..0000000
+++ /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;