Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things
 
PerlMonks  

orderly global destruction? blessed references gone? XS ok

by patcat88 (Deacon)
on Jul 26, 2011 at 20:51 UTC ( [id://916846]=perlquestion: print w/replies, xml ) Need Help??

patcat88 has asked for the wisdom of the Perl Monks concerning the following question:

From what Ive read and from looking at Perl_sv_clean_objs http://perl5.git.perl.org/perl.git/blob/92f022bbf8c129c6f2379a382f1eaaa5c7bd9f3f:/sv.c#l548 in Perl 5.12.2, for global declared objects, during global destruction (die(), undefined sub called, exit(), etc), first perl deletes all references, in linear pointer order (effectively random) to blessed objects, if the object's refcount goes to 0 when the reference is deleted, DESTROY will be called.

My problem is, that if class A uses class B internally, when class A's DESTROYs called, the hash slice that contains a class B instance is undefined. Same problem with tied scalars. Same problem with weak ref that class B has to parent class A. I get a "Can't call method "FETCH" on an undefined value at XXX during global destruction."

One fix I see is to use ogd. I can borrow ogd's concept and have a weakref array stored as a package global. Put in an END block in the package. In the end block manually call DESTROY on each entry in the array. The parent object has a destroyed flag in it which prevents DESTROY from trying to clear out a parent object that was already cleared. Is the risk of colliding with other END blocks that want to see the objects still alive real (and use a method below instead), or does the order of END blocks running somehow prevent that? Is this a way? the correct way? etc

Another way around it I saw in some perl module is, "return undef if ! ref($self->{'chldobj'})" in the DESTROY. I interpret that as dont clean up, who cares? your exiting perl anyways, aslong as you free your XS side mem allocs who cares? perl will delete every last SV*s anyways with the arena system. Is this a way? the correct way? etc

Another thing I think I saw was, in XS, increase refcount of the RV's target SV/HV (the blessed object). Save the HV* as a plain integer in a hash slice of the parent object. Then in DESTROY, if the reference is undef, recreate it from the plain integer with XS. Remember to decrease refcount on the HV* in XS at the end of the parent's DESTROY. Basically, keep the blessed object alive in XS land, and recreate it in Perl land from XS land if its gone in Perl land. This can be further simplified as, just keep the child object always in XS land and refetch it through an XSUB every time you need to use it in Perl. Again, is this a way? the correct way? etc

Any fixes must be 5.12 and 5.10 compatible for my purpose. ${^GLOBAL_PHASE} is too new.

edit: Here is a object destruction test script, it has a commented out myObj END block that destroys weak refs of all instances created, you can comment it back in to see one way of fixing unordered object destruction during global destruction problem. Package main has 2 different main END blocks, the location of the main END block determines whether the main END runs before the package ENDs or after. The main END before non-main ENDs is a hypothetical situation addressed below. The goal is for myObj's DESTROY to have all 3 inner objects available when and whereever myObj's DESTROY runs, no exceptions. Why a inner object has to be available to the parent object when the parent object is DESTROYed is explained in paragraph after code. There has to be some fix to allow a parent object to have all its private child objects available during the parent's object DESTORY call with out it being the responsibility of the caller of parent object's responsibility. Should it be official that Perl the language allows dangling pointers objects or that Perl doesn't have OOP?

Is it unconventional/not supported in general in Perl, to put an END block before the package definition/PM "use"s (and therefore the non-main package END blocks), so there is no point in a non-main package defensively coding against a main package's END block running and assuming the non-main objects are still usable, before the non-main package's END block?

#!/usr/bin/perl -w use strict; package main; #Undefined subroutine &main::uhdsfsd called at objdesmonks.pl line 96. #myobj end #fakeptr end #cObj2 end #cObj end #main end #$o is myObj=HASH(0x182a50c) my $o; #is an END before package definitions a programmer error? END { print "main end\n"; print "\$o is $o\n"; print Dumper($o); $o->printObj(); } package cObj; sub new { print "in cObj new\n"; bless({'data' => 'value'}); } sub DESTROY { print "cObj destroyed\n"; #system("pause"); } END { print "cObj end\n"; } package cObj2; sub new { print "in cObj2 new\n"; bless({'data2' => 'value2'}); } sub DESTROY { print "cObj2 destroyed\n"; #system("pause"); } END { print "cObj2 end\n"; } package fakeptr; sub new { print "in fakeptr new\n"; bless( do{\(my $o = 999000999)}), } sub DESTROY { print "fakeptr destroyed\n"; } END { print "fakeptr end\n"; } package myObj; use Data::Dumper; use Scalar::Util qw( weaken ); my %instances = (); sub new { my $ret; print "in myObj new\n"; $ret = bless({cobj => cObj::new(), cobj2 => "not created yet" ,fa +keptr => fakeptr->new()}); $instances{($ret+0)} = $ret; weaken($instances{($ret+0)}); return $ret; } sub addChild { $_[0]->{cobj2} = cObj2::new(); } sub printObj { print "printing object\n ".Dumper($_[0]); } sub DESTROY { print "myObj destroyed\n"; print Dumper($_[0]); } END { print "myobj end\n"; #comment this out to disable instance clearing #print "myobj clearing instances\n"; #for(keys %instances) { # %{$instances{$_}} = (); #} } package main; use Data::Dumper; use Devel::Peek; #if main END is here, main END fires before non main ENDs #Undefined subroutine &main::uhdsfsd called at objdesmonks.pl line 83. #main end #$o is myObj=HASH(0x182a50c) #........................ #myobj end #fakeptr end #cObj2 end #cObj end #END { # print "main end\n"; # print "\$o is $o\n"; # print Dumper($o); # $o->printObj(); #} $o = myObj::new(); my $b = myObj::new(); sub mysub { print Dumper($o); $o->addChild(); uhdsfsd(); #system("pause"); } print $b; $b = 0; mysub(); 0;

C:\Documents and Settings\Owner\Desktop\>perl objdesmonks.pl in myObj new in cObj new in fakeptr new in myObj new in cObj new in fakeptr new myObj=HASH(0x18f7974)myObj destroyed $VAR1 = bless( { 'cobj' => bless( { 'data' => 'value' }, 'cObj' ), 'fakeptr' => bless( do{\(my $o = 999000999)}, 'fakept +r' ), 'cobj2' => 'not created yet' }, 'myObj' ); fakeptr destroyed cObj destroyed $VAR1 = bless( { 'cobj' => bless( { 'data' => 'value' }, 'cObj' ), 'fakeptr' => bless( do{\(my $o = 999000999)}, 'fakept +r' ), 'cobj2' => 'not created yet' }, 'myObj' ); in cObj2 new Undefined subroutine &main::uhdsfsd called at objdesmonks.pl line 124. myobj end fakeptr end cObj2 end cObj end main end $o is myObj=HASH(0x182a50c) $VAR1 = bless( { 'cobj' => bless( { 'data' => 'value' }, 'cObj' ), 'fakeptr' => bless( do{\(my $o = 999000999)}, 'fakept +r' ), 'cobj2' => bless( { 'data2' => 'value2' }, 'cObj2' ) }, 'myObj' ); printing object $VAR1 = bless( { 'cobj' => bless( { 'data' => 'value' }, 'cObj' ), 'fakeptr' => bless( do{\(my $o = 999000999)}, 'fakept +r' ), 'cobj2' => bless( { 'data2' => 'value2' }, 'cObj2' ) }, 'myObj' ); cObj2 destroyed fakeptr destroyed cObj destroyed myObj destroyed $VAR1 = bless( { 'cobj' => undef, 'fakeptr' => undef, 'cobj2' => undef }, 'myObj' ); C:\Documents and Settings\Owner\Desktop\>


This paragraph is fluff and hypothetical. Reading it not necessary.... Why would a inner object HAVE to be available during the parent's DESTROY? 2 reasons. Lets assume a myObj instance called $self->{'fakeptr'}->lock() when myObj instance was created. myObj also caches/buffers data that needs to be passed to fakeptr before fakeptr's DESTROY runs. fakeptr is written in XS and wraps a closed source but linkable C++ library supplied by a VAR that manipulates a slow I/O resource off campus maintained by a VAR, so caching writes/transactions/records/requests to fakeptr is a must. myObj MUST write/pass the buffered data inside myObj to fakeptr and call $self->{'fakeptr'}->unlock() during DESTROY, otherwise the remote resource is corrupted. You can say, why not have fakeptr automatically unlock() on fakeptr's DESTROY? What if reads/gets of records from the C++ lib that fakeptr wraps are destructive? why not write them back right after reading them in fakeptr? 2 possible reasons, lock() and unlock() of fakeptr is from the C++ API and represents a human customer's contact history to the vendor on the remote resource. The customer's call history log now says he placed and terminated 200 phone calls to the vendor from 15:23:44 to 15:23:45. Other possible reason is the remote resources's throughput dropped by 99% from the (lock(), getRec(), writeRec(), unlock())*50 sequence, rather than lock(), getRec(), pause, writeRec(), unlock() sequence. The C++ API is not changeable, the protocol the C++ API uses is binary and secret. REing is out of the question. Getting rid of the VAR is out of the question.

Replies are listed 'Best First'.
Re: orderly global destruction? blessed references gone? XS ok (sub Main)
by tye (Sage) on Jul 27, 2011 at 03:27 UTC

    This is one of the reasons I (still) use my long-standing (and slowly evolving) practice for laying out Perl scripts, outlined in (tye)Re: Stupid question.

    In my one module where I have the potential for inner objects, I was able to use the "just check for inner object destroyed early" trick that you mentioned. That is what I would do if possible. If you are just free()ing allocated memory, then that should be enough.

    In general, I find it is an excellent idea to avoid XS as much as possible. Doing complex tricks in XS like you discuss above seems to me a pretty bad idea. I see tons of problems (including "core dumps") that appear to be from bugs in even fairly simple XS code in quite main-stream modules that are well maintained.

    - tye        

Re: orderly global destruction? blessed references gone? XS ok
by ikegami (Patriarch) on Jul 26, 2011 at 21:26 UTC

    How about

    END { $global = undef; }

    Is that feasible for you?

      END { $global = undef; }

      That END block is in main/caller's package, not the $global's package. A PM/Class can't control where its instances stored (lex or global). The programmer of the PM doesn't write where the PM is used. Are you suggesting a package weakref list/hash and an END block in the same package in the that destroys all live instances of that package?

        That END block is in main/caller's package, not the $global's package

        Yes, assuming you mean "ref($global)" by "$global's package".

        Are you suggesting a package weakref list/hash and an END block in the same package in the that destroys all live instances of that package?

        I was suggesting that if you create a global that's creating a problem, that you cleanup the global you created before it creates the problem.

        What you mention could work, but it could also create new instances of the problem you are trying to avoid.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://916846]
Approved by Corion
Front-paged by ikegami
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others browsing the Monastery: (6)
As of 2024-03-28 21:46 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found