This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Rework mod loading for %- and %!; fix mem leak
authorFather Chrysostomos <sprout@cpan.org>
Thu, 4 Aug 2016 20:00:18 +0000 (13:00 -0700)
committerFather Chrysostomos <sprout@cpan.org>
Thu, 4 Aug 2016 20:12:19 +0000 (13:12 -0700)
commite94ea821c9b12318e87433760bdeb91530114733
treec7bc07f96b4f1a9abb0846e5a520e68149883b26
parent036bbc13ecf0ff7517db4871ba606c5a3affcb60
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.
embed.fnc
embed.h
ext/Errno/Errno_pm.PL
ext/Tie-Hash-NamedCapture/NamedCapture.xs
ext/arybase/arybase.xs
gv.c
proto.h
t/op/magic.t
t/op/svleak.t