develooper Front page | perl.perl5.porters | Postings from September 2012

Re: slab alocator, CvSTART of PL_main_cv and Devel::Size (was Re:[perl.git] branch blead, updated. v5.17.1-240-gc5fb998)

Thread Previous
Nicholas Clark
September 6, 2012 07:06
Re: slab alocator, CvSTART of PL_main_cv and Devel::Size (was Re:[perl.git] branch blead, updated. v5.17.1-240-gc5fb998)
Message ID:
Right. After a long time down the rabbit hole, and digressed by various
other things I found there, and "events", I return with an answer.

On Sun, Jul 29, 2012 at 12:57:39PM -0700, Father Chrysostomos wrote:
> On Jul 18, 2012, at 2:38 PM, Nicholas Clark wrote:

> > On Fri, Jun 29, 2012 at 09:25:02AM +0200, Father Chrysostomos wrote:

> > It is visible in at least one way outside the core - CvSTART() of PL_main_cv
> > ends up being left with a value long after compiling is finished.
> > 
> > Devel::Size::total_size when passed a subroutine reference ends up chasing
> > CvOUTSIDE pointers to PL_main_cv [whether it *should* is a different
> > question], and then follows CvSTART() and gets very upset because it's not
> > actually ops.

No, it shouldn't.
a) It shouldn't be sizing up CvSTART(). Just CvROOT()
b) It also shouldn't even be thinking about sizing up PL_main_cv - that's
   a fixed cost of the interpreter, not something "owned" by any user

> > This seems to fix things so that PL_main_cv doesn't retain a pointer to
> > something it no longer owns:

> > Breakpoint 1, perl_run (my_perl=0x9cc010) at perl.c:2306
> > 2306       int ret = 0;
> > (gdb) call Perl_sv_dump(PL_main_cv)
> > SV = PVCV(0x9dafa0) at 0x9cefc8
> >  REFCNT = 1
> >  COMP_STASH = 0x0
> >  ROOT = 0x0
> >  GVGV::GV = 0x0
> >  FILE = "(null)"
> >  DEPTH = 0
> >  FLAGS = 0x100
> >  OUTSIDE_SEQ = 0
> >  PADLIST = 0x9cefe0
> >  OUTSIDE = 0x0 (null)
> > 
> > and CvSTART() isn't pointing to bogus things.
> I was under the impression that no code attributed a meaning to CvSTART without CvROOT also set.  I think Devel::Size should follow that.  The above fix will work, unless Devel::Size is invoked from within a BEGIN block.

Agree, and agree. I also suspect that it breaks in too many other places,
not just BEGIN blocks.

Specifically, the above change makes PL_main_cv seem "normal" for the case
of perl -e1

However the call to Perl_cv_forget_slab() is in this bit of Perl_newPROG():

	if (o->op_type == OP_STUB) {
            /* This block is entered if nothing is compiled for the main
               program. This will be the case for an genuinely empty main
               program, or one which only has BEGIN blocks etc, so already
               run and freed.

               Historically (5.000) the guard above was !o. However, commit
               f8a08f7b8bd67b28 (Jun 2001), integrated to blead as
               c71fccf11fde0068, changed perly.y so that newPROG() is now
               called with the output of block_end(), which returns a new
               OP_STUB for the case of an empty optree. ByteLoader (and
               maybe other things) also take this path, because they set up
               PL_main_start and PL_main_root directly, without generating an

	    PL_comppad_name = 0;
	    PL_compcv = 0;
	    S_op_destroy(aTHX_ o);
	PL_main_root = op_scope(sawparens(scalarvoid(o)));
	PL_curcop = &PL_compiling;
	PL_main_start = LINKLIST(PL_main_root);
	PL_main_root->op_private |= OPpREFCOUNTED;
	OpREFCNT_set(PL_main_root, 1);
	PL_main_root->op_next = 0;
	PL_compcv = 0;

I've added the comment as part of the chasing things down.
Specifically because Perl_cv_forget_slab() isn't called for the case of

perl -e 'BEGIN {1}'

(or more realistically, anything that is perl -Msomething -e0)

Whack *that* mole, and then you find that Perl_newPROG() isn't even
*called* for the case of

perl -e 'BEGIN {exit 1}'

and more amusingly, perl_run() *is* still called for the case of

perl -e 'BEGIN {exit 0}'

(but it exits promptly, because PL_main_start is NULL).

This is because the value returned by perl_parse() is the exit code if
my_exit() is called, and a return of 0 from perl_parse() is "successfully

And I suspect that as well as your case of being called in a BEGIN block,
CvSTART(PL_main_cv) will be non-NULL within END blocks for a whole variety
of reasons.

> When a CV has a reference count on its slab (CvSLABBED), it is responsible for making sure it is freed.  (Hence, no two CVs should ever have a reference count on the same slab.)  The CV only needs to reference the slab during compilation.  Once it is compiled and CvROOT attached, it has finished its job, so it can forget the slab.  (I'll add that to perlintern.)
> So it is not when the CV is about to be freed, but, rather, when it is *not* going to be freed yet, that cv_forget_slab is called.  That's why setting CvROOT to NULL is not going to work.

Aha. Thanks for the explanation.

Nicholas Clark

Thread Previous Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at | Group listing | About