summaryrefslogtreecommitdiff
path: root/ext/Errno
diff options
context:
space:
mode:
authorFather Chrysostomos <sprout@cpan.org>2016-08-04 13:00:18 -0700
committerFather Chrysostomos <sprout@cpan.org>2016-08-04 13:12:19 -0700
commite94ea821c9b12318e87433760bdeb91530114733 (patch)
treec7bc07f96b4f1a9abb0846e5a520e68149883b26 /ext/Errno
parent036bbc13ecf0ff7517db4871ba606c5a3affcb60 (diff)
downloadperl-e94ea821c9b12318e87433760bdeb91530114733.tar.gz
Rework mod loading for %- and %!; fix mem leak
There are many built-in variables that perl creates on demand for efficiency’s sake. gv_fetchpvn_flags (which is responsible for sym- bol lookup) will fill in those variables automatically when add- ing a symbol. The special GV_ADDMG flag passed to this function by a few code paths (such as defined *{"..."}) tells gv_fetchpvn_flags to add the symbol, but only if it is one of the ‘magical’ built-in variables that we pre- tend already exist. To accomplish this, when the GV_ADDMG flag is passed, gv_fetchpvn_flags, if the symbol does not already exist, creates a new GV that is not attached to the stash. It then runs it through its magicalization code and checks afterward to see whether the GV changed. If it did, then it gets added to the stash. Otherwise, it is discarded. Three of the variables, %-, %!, and $], are problematic, in that they are implemented by external modules. gv_fetchpvn_flags loads those modules, which tie the variable in question, and then control is returned to gv_fetchpvn_flags. If it has a GV that has not been installed in the symbol table yet, then the module will vivify that GV on its own by a recursive call to gv_fetchpvn_flags (with the GV_ADD flag, which does none of this temporary-dangling-GV stuff), and gv_fetchpvn_flags will have a separate one which, when installed, would clobber the one with the tied variable. We solved that by having the GV installed right before calling the module, for those three variables (in perl 5.16). The implementation changed in commit v5.19.3-437-g930867a, which was supposed to clean up the code and make it easier to follow. Unfortun- ately there was a bug in the implementation. It tries to install the GV for those cases *before* the magicalization code, but the logic is wrong. It checks to see whether we are adding only magical symbols (addmg) and whether the GV has anything in it, but before anything has been added to the GV. So the symbol never gets installed. Instead, it just leaks, and the one that the implementing module vivifies gets used. This leak can be observed with XS::APItest::sv_count: $ ./perl -Ilib -MXS::APItest -e 'for (1..10){ defined *{"!"}; delete $::{"!"}; warn sv_count }' 3833 at -e line 1. 4496 at -e line 1. 4500 at -e line 1. 4504 at -e line 1. 4508 at -e line 1. 4512 at -e line 1. 4516 at -e line 1. 4520 at -e line 1. 4524 at -e line 1. 4528 at -e line 1. Perl 5.18 does not exhibit the leak. So in this commit I am finally implementing something that was dis- cussed about the time that v5.19.3-437-g930867a was introduced. To avoid the whole problem of recursive calls to gv_fetchpvn_flags vying over whose GV counts, I have stopped the implementing modules from tying the variables themselves. Instead, whichever gv_fetchpvn_flags call is trying to create the glob is now responsible for seeing that the variable is tied after the module is loaded. Each module now pro- vides a _tie_it function that gv_fetchpvn_flags can call. One remaining infelicity is that Errno mentions $! in its source, so *! will be vivified when it is loading, only to be clobbered by the GV subsequently installed by gv_fetch_pvn_flags. But at least it will not leak. One test that failed as a result of this (in t/op/magic.t) was try- ing to undo the loading of Errno.pm in order to test it afresh with *{"!"}. But it did not remove *! before the test. The new logic in the code happens to work in such a way that the tiedness of the vari- able determines whether the module needs to be loaded (which is neces- sary, now that the module does not tie the variable). Since the test is by no means normal code, it seems reasonable to change it.
Diffstat (limited to 'ext/Errno')
-rw-r--r--ext/Errno/Errno_pm.PL5
1 files changed, 4 insertions, 1 deletions
diff --git a/ext/Errno/Errno_pm.PL b/ext/Errno/Errno_pm.PL
index 6251a3cf33..37806eb1ae 100644
--- a/ext/Errno/Errno_pm.PL
+++ b/ext/Errno/Errno_pm.PL
@@ -391,6 +391,7 @@ sub STORE {
Carp::confess("ERRNO hash is read only!");
}
+# This is the true return value
*CLEAR = *DELETE = \*STORE; # Typeglob aliasing uses less space
sub NEXTKEY {
@@ -407,7 +408,9 @@ sub EXISTS {
exists $err{$errname};
}
-tie %!, __PACKAGE__; # Returns an object, objects are true.
+sub _tie_it {
+ tie %{$_[0]}, __PACKAGE__;
+}
__END__