Beefy Boxes and Bandwidth Generously Provided by pair Networks
No such thing as a small change

Re^3: perlembed: mortalize an AV, get "Attempt to free unreferenced scalar" - don't mortalize, leaks memory

by demerphq (Chancellor)
on Jul 10, 2006 at 13:53 UTC ( #560143=note: print w/replies, xml ) Need Help??

in reply to Re^2: perlembed: mortalize an AV, get "Attempt to free unreferenced scalar" - don't mortalize, leaks memory
in thread perlembed: mortalize an AV, get "Attempt to free unreferenced scalar" - don't mortalize, leaks memory

Update: Looks like this isnt right... Sigh

I thought my mortalized SVs were only supposed to be blown away when the following code gets executed:

Yes, the logic i explained is basically what happens when FREETMPS/LEAVE occurs. Dont take my description too literally, im far from being authority on the internals, just trying to add some insight based on the little understanding i do have.

So how could the values be freed too soon?

Im assuming the reason the vars could be blown away too soon is that when you mortalize a var it goes on a temps stack, which is then processed by the FREETMPS/LEAVE macros. If you mortalize a member element of a composite structure which is itself mortalized then presumably there will be two attempts to refcount-dec/free the same thing.

That leaks, but if I mortalize the SV as in the comment, everything is great, and there are no warnings. So what gives? What's the difference between the av_push and hv_store examples?

Im basically guessing, but id think the problem is that when you do newSVpv() the created SVPV has a refcount of 1. You then increment the refcount, bringing it to 2, (note that in the array example you do not do the additional SvREFCOUNT_inc()) then you hv_push into a mortalized hash, which is then referenced by a mortal SVRV. So the mortal SVPV gets its refcount decremented once by its own mortalization, and then decremented again when the hash is freed. If you dont mortalize the SVPV then its refcount is 1 and it leaks. However it seems to me that the proper thing to do here is to do something like:

HV *hash = newHV(); SV *type = newSVpv("string", 0); if ( NULL == hv_store( hash, "type", strlen("type"), type, 0) ) { SvREFCNT_dec( type ); /* free the SV */ printf("hv_store failed\n"); exit(1); /*me wonders about this...*/ } XPUSHs(sv_2mortal(newRV((SV *)hash)));

The idea being that the only thing that needs to be mortal is the newRV(), which when its freed will cascade through the things it references, refcount decrementing them to 0 and thus causing them to be freed.

Anyway, its very possible ive gotten something horribly wrong, but this is more or less as i understand it.


  • Comment on Re^3: perlembed: mortalize an AV, get "Attempt to free unreferenced scalar" - don't mortalize, leaks memory
  • Download Code

Replies are listed 'Best First'.
Re^4: perlembed: mortalize an AV, get "Attempt to free unreferenced scalar" - don't mortalize, leaks memory
by edan (Curate) on Jul 10, 2006 at 18:03 UTC
    Tried that. Leaks like a madman. Nope, I think the HV and the SVpv need to be mortal. I'll stick with my current incantation, since empirically it's correct, even though I still don't know why, and am still plagued by the thought that maybe it only seems right, but isn't.

    Any more help is most welcome.


      Possible "mistakes":

      1. Ref count set too high: Leaks memory
      2. Ref count set too low: Throws up, hopefully not too cryptically
      3. Ref count set exactly 1 "too high" and mortalized when it doesn't need to be: Nothing goes wrong

      So you've probably got a case of (3) now. You usually don't have to make mortal something that you aren't putting directly onto the Perl stack, but sometimes it is convient to do so.

      For example, you could use newRV_noinc() since you are making a reference to an AV that you just created (and thus that already has a reference count of 1). Then you wouldn't need to make the AV mortal. Or, you could decide that you like the slightly less efficient method of immediately making it mortal such that it will get freed even if something goes wrong with (or before) your call to newRV().

      I'd try to avoid calling SvREFCNT_inc() / SvREFCNT_dec(), there's usually a clearer way (that is less likely to be done incorrectly). You should call croak() not printf() and exit().

      Why not use call_pv() instead of call_sv() and making yet another thing that you have to make mortal?

      So your original problem was two mistakes. First, av_push() doesn't increment the refcount so creating a new SV and av_push()ing it onto one array is fine (refcount starts out as and stays 1). But av_push()ing it onto two arrays would be bad (refcount still 1 when needs to be 2). Mortalizing the SV means the refcount drops to 0 later so it tries to get free()d twice.

      Second, you use newRV() [which is newRV_inc()] so the AV's ref count was too high. So you had one ref-count too high and a different ref count too low. So your two choices were leaking an AV such that the AV didn't try to free up the SV a second time or properly destroy the AV such that it noticed that the SV had too low of a ref count.

      - tye        

        Thanks tye - this is extremely helpful. So I just want to make sure that I understood properly what you recommend to do. Does the following code give a decent example of how to push an array ref and a hash ref onto the stack?

        dSP; ENTER; SAVETMPS; PUSHMARK(SP); AV *arr = newAV(); av_push( arr, newSVpv("string", 0)); XPUSHs(sv_2mortal(newRV_noinc((SV *)arr))); HV *hash = newHV(); SV *type = newSVpv("string", 0); hv_store( hash, "type", strlen("type"), type, 0); XPUSHs(sv_2mortal(newRV_noinc((SV *)hash))); PUTBACK; call_sv(sv_2mortal(newSVpv("TestPM::test_leak", 0)), G_SCALAR); SPAGAIN; SV *retval = POPs; PUTBACK; FREETMPS; LEAVE;

        I'd really appreciate it if you could validate this approach, which is my understanding of what you explained above.

        Regarding your other questions:

        1. The printf/exit was just an example - my real code is actually throwing an exception there. Actually, it's also doing a FREETMPS/LEAVE - is that the right thing to do if I'm expecting the process to stay alive, and I want everything cleaned up? Will that work okay with the non-mortal approach you're advocating?
        2. I was calling SvREFCNT_inc() since it seemed to me from the hv_store() docs that I needed to. But you're saying that I don't, right? I have to say that I'm still left feeling like the above code might work, but isn't really handling refcounts correctly. But I trust you more than I trust my gut feelings here...
        3. call_sv() was also just an example - my real code is calling call_method().

        So wouldn't it be better if I do mortalize everything, and just SvREFCNT_inc whenever I either av_push() or hv_store() the SV, as well as using newRV_inc()? Will that not work and be correct?


      Tried that. Leaks like a madman.

      Well, thanks for letting me know. I guess i need to book up on this stuff a little better :-)

      I think at this point if some internals guru hasnt popped up to explain things (i havent checked as of writing this) that you should ask on p5p.

      Sorry I wasnt more helpful. :-(


      Hmm, i did a little research into this, and i think i know what the problem was with my code. newRV is an alias for newRV_inc(), which means the item being referenced has an implicit SvREFCNT_inc() called on it, thus in the snippet i showed *hash ends up with a refcount of 2 instead of a refcount of 1. Changing it to newRV_noinc() should fix it.

      HV *hash = newHV(); SV *type = newSVpv("string", 0); if ( NULL == hv_store( hash, "type", strlen("type"), type, 0) ) { SvREFCNT_dec( type ); /* free the SV */ printf("hv_store failed\n"); exit(1); /*me wonders about this...*/ } XPUSHs(sv_2mortal(newRV_noinc((SV *)hash)));

      I really have to get myself set up to check this, but I thought id mention it. BTW, if you havent already read it you should review "Reference Counts and Mortality" in perlguts. Which on a quick reading basically agrees with what i said before, with the minor caveat about newRV_noinc(), which it specifically documents as being a common mistake.

      For example, imagine you want to return a reference from an XSUB function. Inside the XSUB routine, you create an SV which initially has a reference count of one. Then you call newRV_inc, passing it the just-created SV. This returns the reference as a new SV, but the reference count of the SV you passed to newRV_inc has been incremented to two. Now you return the reference from the XSUB routine and forget about the SV. But Perl hasn't! Whenever the returned reference is destroyed, the reference count of the original SV is decreased to one and nothing happens. The SV will hang around without any way to access it until Perl itself terminates. This is a memory leak.

      Which basically explains why my original snippet "leaks like mad". It leaks like mad because the root of the structure that you want to disappear on its own never goes to 0, so the whole lot stays in memory. Change the newRV() to newRV_noinc() and it should be freed as and when expected and no leaks.


        Hmm.... thanks for pressing this, demerphq. You appear to be right about not needing so many mortals. I think the trick is to avoid incrementing any refcounts (as I was doing after hv_store()), and only mortalize the RV I am pushing on the stack. This is basically what you said, but the code comes out a bit different from what you said:

        AV *arr = newAV(); av_push( arr, newSVpv("test", 0)); XPUSHs(sv_2mortal(newRV_noinc((SV *)arr))); HV *hash = newHV(); hv_store( hash, "key", strlen("key"), newSVpv("test", 0), 0); XPUSHs(sv_2mortal(newRV_noinc((SV *)hash)));

        So I'm just creating all "intermediate" variables non-mortals, not incrementing any refcounts after av_push or hv_store(), using newRV_noinc(), and mortalizing just the RV that's pushed on the stack.

        Does that seem right? Sure looks cleaner...


Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: note [id://560143]
[marto]: fortunately my bad jokes and puns don't fall into those categories :P

How do I use this? | Other CB clients
Other Users?
Others chilling in the Monastery: (7)
As of 2018-04-26 10:39 GMT
Find Nodes?
    Voting Booth?