in reply to Re^5: when is destroy function called
in thread when is destroy function called

Athanasius:

Sorry for the delay in responding, I had to read a lot more than I expected!

In general, a subroutine won't remove arguments from its argument list unless explicitly told to (shift,pop) in the subroutine. Especially as the subroutine might either use the argument again later, or might pass its complete argument list to another subroutine for handling. In C, the caller builds the stack with arguments, calls the function, and when the function returns the caller removes the items from the stack. That's the mental model I have of perl (at this time), for right or wrong.

Regarding the reference counts: the output is pretty tedious to read. Here's a stripped version of the program in which we'll look just at $b. It makes tracing the refcount to the anonymous array easier to track:

#!/usr/bin/perl use Devel::Peek; { package Foo; sub new { my $class = shift; my $val = shift; return bless { thing=>$val }; } sub val { my $self = shift; return "val: " . (++$self->{theVal}); } sub DESTROY { print "destroyed at ", ${shift}->{theVal}, "\n"; } } my $b = [ ]; print "--- before building t ---\n"; Dump($b); my $t = Foo->new($b); print "--- after building t ---\n"; Dump($b); print "--- printing 1 ---\n"; print $t->val(), Dump($b), undef($t), "--- printing 2---\n", Dump($b), ".\n"; print "--- after print ---\n"; Dump($b);

Here you can see that the reference count start at 1 (line x), and increase to 2 when we build t. It's 2 when we start the print, and still 2 after the undef. But immediately after the print, it's back to 1 (and we see the destroy text).

--- before building t --- SV = RV(0x81df0cc) at 0x81df0c0 REFCNT = 1 FLAGS = (PADMY,ROK) RV = 0x81c1818 SV = PVAV(0x81c286c) at 0x81c1818 REFCNT = 1 FLAGS = () ARRAY = 0x0 FILL = -1 MAX = -1 ARYLEN = 0x0 FLAGS = (REAL) --- after building t --- SV = RV(0x81df0cc) at 0x81df0c0 REFCNT = 1 FLAGS = (PADMY,ROK) RV = 0x81c1818 SV = PVAV(0x81c286c) at 0x81c1818 REFCNT = 2 FLAGS = () ARRAY = 0x0 FILL = -1 MAX = -1 ARYLEN = 0x0 FLAGS = (REAL) --- printing 1 --- SV = RV(0x81df0cc) at 0x81df0c0 REFCNT = 1 FLAGS = (PADMY,ROK) RV = 0x81c1818 SV = PVAV(0x81c286c) at 0x81c1818 REFCNT = 2 FLAGS = () ARRAY = 0x0 FILL = -1 MAX = -1 ARYLEN = 0x0 FLAGS = (REAL) SV = RV(0x81df0cc) at 0x81df0c0 REFCNT = 1 FLAGS = (PADMY,ROK) RV = 0x81c1818 SV = PVAV(0x81c286c) at 0x81c1818 REFCNT = 2 FLAGS = () ARRAY = 0x0 FILL = -1 MAX = -1 ARYLEN = 0x0 FLAGS = (REAL) val: 1--- printing 2--- . destroyed at --- after print --- SV = RV(0x81df0cc) at 0x81df0c0 REFCNT = 1 FLAGS = (PADMY,ROK) RV = 0x81c1818 SV = PVAV(0x81c286c) at 0x81c1818 REFCNT = 1 FLAGS = () ARRAY = 0x0 FILL = -1 MAX = -1 ARYLEN = 0x0 FLAGS = (REAL)

After checking your pointer to Reference Counts and Mortality in perlguts, it led me a lot of reading. I think you're correct: the section http://perldoc.perl.org/perlcall.html#Passing-Parameters in perlcall shows an example call to LeftString from C. It shows the caller building the argument list, and destroying them afterwards with FREETMPS. In the notes below the example, note 5 clinches it (for me, anyhow).

Now that I've done all that reading, maybe I should try to dig into the perl source again and try to wrap my brain around some of it....

...roboticus

When your only tool is a hammer, all problems look like your thumb.

Replies are listed 'Best First'.
Re^7: when is destroy function called
by Athanasius (Archbishop) on Mar 06, 2013 at 13:11 UTC

    roboticus:

    Thanks for the explanation. As a test, I augmented the example I gave above with an equivalent function in C. (This was my first experience using Inline::C. To call the Perl sub from within C, I copied the example in the section “Calling Perl from C” of the Inline::C-Cookbook.)

    #! perl use strict; use warnings; $| = 1; use Inline C => <<'END_C'; void baz(SV* foo_ref) { printf("Begin baz()\n"); fflush(stdin); dSP; ENTER; SAVETMPS; XPUSHs(sv_2mortal(newSVpvf("Plus an extra line"))); PUTBACK; call_pv("destruct", G_DISCARD); printf("Before FREETMPS\n"); fflush(stdin); FREETMPS; LEAVE; printf("-End- baz()\n"); fflush(stdin); } END_C { package Foo; sub new { my ($class, $num) = @_; my $self = { id => $num, }; return bless $self, $class; } sub DESTROY { my ($self) = @_; print 'Foo::DESTROY(', $self->{id}, ")\n"; } } my $foo1 = Foo->new('first'); my $foo2 = Foo->new('second'); bar($foo1); print "Back in main (1)\n"; baz($foo2); print "Back in main (2)\n"; sub bar { print "Begin bar()\n"; destruct($_[0]); print "-End- bar()\n"; } sub destruct { undef $_[0]; }

    Output:

    22:55 >perl 552c_SoPW.pl Begin bar() Foo::DESTROY(first) -End- bar() Back in main (1) Begin baz() Foo::DESTROY(second) Before FREETMPS -End- baz() Back in main (2) 22:56 >

    From which it seems that the object is destroyed — meaning its reference count has fallen to zero — before the FREETMPS macro is reached. In which case the addition of the object to the sub’s call stack has not caused its reference count to be incremented? (Or am I simply misunderstanding here?)

    Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

      Athanasius:

      Your code and analysis look alright with me. But I'll have to dig more to go much further. I may have to download your code this evening and give it a try. I'm kind of curious if not marking them as mortal would cause them to die in the FREETMPS area.

      It's pretty darned interesting, though. Hopefully someone with good internals knowledge will chime in. (bulk88, you there?)

      Update: I reworded it a little.

      ...roboticus

      When your only tool is a hammer, all problems look like your thumb.

        Mortals are freeded at the next ";" or next "}" scope end. Not sure about scope opens("{"). DESTROYs will run when the ";" is executed, not at commas and not at sub calls (callee gets a new mortal scope, i am talking about caller scope) in the statement. This can cause headaches when you assign a new obj to a scalar that held a ref to an old obj, depending on the design of the class/api/library, the old obj can change global state set by new obj, so when you do something that involves global state, you wont see the action of new obj, but of old obj.