You are viewing a plain text version of this content. The canonical link for it is here.
Posted to modperl@perl.apache.org by Ken Williams <ke...@mathforum.org> on 2002/01/01 00:13:33 UTC

Apache::Session getting DESTROYed in wrong order

Hey,

I'm having problems with Apache::Session, the symptom is that none of my 
data is getting written to the database.  It's not the nested-data 
problem, since I'm not using any nested data structures.

After some investigation, I've discovered that the 
Apache::Session::Store::MySQL::DESTROY routine is getting called before 
the Apache::Session::MySQL::DESTROY routine, so when the latter is 
called it doesn't have any way to write to the database.

I think Perl is supposed to guarantee that the outer object's DESTROY is 
called before the inner object's, but I think maybe this guarantee 
doesn't extend to the "global destruction" phase.  So I'm wondering why 
the session is getting cleaned up in global destruction rather than 
refcount destruction.  I've declared %session as a locally-scoped 
variable, so it should evaporate before global destruction, unless it's 
got circular data structures or something.  Anyone know what might be 
going on?

This is Apache::Session version 1.53.

Note: this problem isn't related to mod_perl, but IIRC this list is the 
proper place for discussing Apache::Session.

  -Ken


Re: Apache::Session getting DESTROYed in wrong order

Posted by Perrin Harkins <pe...@elem.com>.
> The circular reference was the only way I could think of to force an
> object to be destroyed during global destruction.

What happens if you use a global?

> Hmm, that may be - Mason does create more closures now than it used to.
> It seems like only 'named' closures would create this problem, though,
> and not 'anonymous' closures (since the refcount of the anonymous
> closure itself should go to zero, freeing its contents).

I was thinking of this situation:

my %session = get_session();

sub transmogrify {
    $session{'foo'}++;
}

I could be wrong, but I think that will make %session stick around, because
transmogrify() now has a private copy of it.

- Perrin


Re: Apache::Session getting DESTROYed in wrong order

Posted by Ken Williams <ke...@mathforum.org>.
On Thursday, January 3, 2002, at 11:57 AM, Perrin Harkins wrote:
>> I don't have a test case involving Apache::Session yet (I've been out 
>> of
>> town for a couple days), but here's a simple one in Perl that
>> demonstrates the DESTROY order problem:
>
> That's sort of a weird example, since it has a circular reference.  
> Does it
> have problems without the circular ref?

The circular reference was the only way I could think of to force an 
object to be destroyed during global destruction.  I don't know whether 
it has a problem without circularity or not.


> At a guess, I'd say you're making an unintentional closure somewhere.
>

Hmm, that may be - Mason does create more closures now than it used to.  
It seems like only 'named' closures would create this problem, though, 
and not 'anonymous' closures (since the refcount of the anonymous 
closure itself should go to zero, freeing its contents).  Mason is 
supposed to be using all anonymous closures.

I'm finding the destruction behavior highly unpredictable with the 
'named' closure actually, so maybe I should bring it up on p5p.  Perhaps 
the order of destruction is just random during global destruction, 
because I've seen it happen from the inside out & from the outside in.

  -Ken


Re: Apache::Session getting DESTROYed in wrong order

Posted by "Jeffrey W. Baker" <jw...@acm.org>.

On Fri, 4 Jan 2002, Ken Williams wrote:

>
> On Friday, January 4, 2002, at 02:48 AM, Gerald Richter wrote:
>
> >>    # Won't get cleaned up properly
> >>    local %foo;
> >>    tie %foo, 'Dummy', name => '%foo';
> >
> > local only make a copy of the original value and restores it at the end
> > of
> > the scope, so %foo will not destroyed, but restored at the end of the
> > scope.
> > I guess this is the reason my it still stays tied.
>
> AMS just posted this small test case to p5p:
>
>      sub X::TIEHASH{bless{}}
>      { local %x; tie %x, "X" } print tied %x ? "a" : "b";
>
> 5.004_03 prints "b", and 5.004_04 (and higher) prints "a".  I think "b"
> is the proper behavior, at least that's my opinion.

Well, you can say I'm cold-hearted, but I think if you use every feature
Perl has in one line, you can expect trouble.  Apache::Session has a
history of this.  With every Perl < 5.6, it cause a segfault when calling
die() from within TIEHASH.

-jwb


Re: Apache::Session getting DESTROYed in wrong order

Posted by Ken Williams <ke...@mathforum.org>.
On Friday, January 4, 2002, at 02:48 AM, Gerald Richter wrote:

>>    # Won't get cleaned up properly
>>    local %foo;
>>    tie %foo, 'Dummy', name => '%foo';
>
> local only make a copy of the original value and restores it at the end 
> of
> the scope, so %foo will not destroyed, but restored at the end of the 
> scope.
> I guess this is the reason my it still stays tied.

AMS just posted this small test case to p5p:

     sub X::TIEHASH{bless{}}
     { local %x; tie %x, "X" } print tied %x ? "a" : "b";

5.004_03 prints "b", and 5.004_04 (and higher) prints "a".  I think "b" 
is the proper behavior, at least that's my opinion.


  -Ken


Re: Apache::Session getting DESTROYed in wrong order

Posted by Gerald Richter <ri...@ecos.de>.
>    # Won't get cleaned up properly
>    local %foo;
>    tie %foo, 'Dummy', name => '%foo';

local only make a copy of the original value and restores it at the end of
the scope, so %foo will not destroyed, but restored at the end of the scope.
I guess this is the reason my it still stays tied.

In my experiences there are more weired behaviours with tied hashs and
arrays. (e.g. don't access a tied hash inside of a method of a tied hash,
use FETCH instead, tied hash element doesn't always spring into existence,
like normal hash elements does). You have to use them with some care.


>
> Investigating with Devel::Peek suggests that it's a %foo refcount
> problem, it's somehow getting set to 2 after tie(%foo).
>

2 is ok. one for %foo itself and one because it's tied to another object

Gerald


-------------------------------------------------------------
Gerald Richter    ecos electronic communication services gmbh
Internetconnect * Webserver/-design/-datenbanken * Consulting

Post:       Tulpenstrasse 5         D-55276 Dienheim b. Mainz
E-Mail:     richter@ecos.de         Voice:    +49 6133 925131
WWW:        http://www.ecos.de      Fax:      +49 6133 925152
-------------------------------------------------------------




Re: Apache::Session getting DESTROYed in wrong order

Posted by Perrin Harkins <pe...@elem.com>.
> I register a clean up handler to explicitly untie the session variable.

I have found that it's safer to put things in pnotes than to use globals and
cleanup handlers.  We used a lot of cleanup handlers at eToys to clear
globals holding various request-specific things, and we started getting
unpredictable segfaults.  When I moved them to pnotes instead the segfaults
went away.  I think it may have had something to do with cleanup handlers
running in an unpredictable order and some of them trying to use things that
were already cleaned up, so it was probably my fault, but pnotes just seems
a bit more foolproof.

- Perrin


Re: Apache::Session getting DESTROYed in wrong order

Posted by Jay Lawrence <Ja...@Lawrence.Net>.
I register a clean up handler to explicitly untie the session variable. I am
not
sure how to do this in the setup you have running...so I can't be of much
explicit help.

Jay

----- Original Message ----- 
From: "Ken Williams" <ke...@mathforum.org>
To: "Perrin Harkins" <pe...@elem.com>
Cc: <mo...@apache.org>
Sent: Friday, January 18, 2002 1:53 AM
Subject: Re: Apache::Session getting DESTROYed in wrong order


> 
> On Friday, January 18, 2002, at 12:44 AM, Perrin Harkins wrote:
> 
> >> In a Mason context, which is where I'm using it, I do this in my
> >> top-level autohandler (ignore the main:: subroutines, they're just for
> >> pedagogy):
> >>
> >>
> >> <%init>
> >>   # 'local' so it's available to lower-level components
> >>   local *session;
> >>
> >>   my $dbh = &::get_dbh;
> >>   my $session_id = &::get_cookie('_session_id');
> >>   tie %session, 'Apache::Session::MySQL', $session_id,
> >>                {Handle => $dbh, LockHandle => $dbh};
> >>   ...
> >> </%init>
> >
> > Geez, that's awfully confusing to look at (local and typeglobs is not a
> > newbie-friendly combo).  Isn't there a simpler way?  What about putting
> > it in pnotes?
> 
> I don't think there's a simpler way.  Putting it in pnotes means that 
> all other components will also have to use $r->pnotes('session'), rather 
> than just using %session.
> 
> Perhaps "local(*session)" is better than "local *session"?  It at least 
> looks less like a "pointer to local".  ;-)
> 
>   -Ken
> 
> 


Re: Apache::Session getting DESTROYed in wrong order

Posted by Ken Williams <ke...@mathforum.org>.
On Friday, January 18, 2002, at 12:44 AM, Perrin Harkins wrote:

>> In a Mason context, which is where I'm using it, I do this in my
>> top-level autohandler (ignore the main:: subroutines, they're just for
>> pedagogy):
>>
>>
>> <%init>
>>   # 'local' so it's available to lower-level components
>>   local *session;
>>
>>   my $dbh = &::get_dbh;
>>   my $session_id = &::get_cookie('_session_id');
>>   tie %session, 'Apache::Session::MySQL', $session_id,
>>                {Handle => $dbh, LockHandle => $dbh};
>>   ...
>> </%init>
>
> Geez, that's awfully confusing to look at (local and typeglobs is not a
> newbie-friendly combo).  Isn't there a simpler way?  What about putting
> it in pnotes?

I don't think there's a simpler way.  Putting it in pnotes means that 
all other components will also have to use $r->pnotes('session'), rather 
than just using %session.

Perhaps "local(*session)" is better than "local *session"?  It at least 
looks less like a "pointer to local".  ;-)

  -Ken


Re: Apache::Session getting DESTROYed in wrong order

Posted by Perrin Harkins <pe...@elem.com>.
> In a Mason context, which is where I'm using it, I do this in my
> top-level autohandler (ignore the main:: subroutines, they're just for
> pedagogy):
>
>
> <%init>
>   # 'local' so it's available to lower-level components
>   local *session;
>
>   my $dbh = &::get_dbh;
>   my $session_id = &::get_cookie('_session_id');
>   tie %session, 'Apache::Session::MySQL', $session_id,
>                {Handle => $dbh, LockHandle => $dbh};
>   ...
> </%init>

Geez, that's awfully confusing to look at (local and typeglobs is not a
newbie-friendly combo).  Isn't there a simpler way?  What about putting
it in pnotes?
- Perrin


Re: Apache::Session getting DESTROYed in wrong order

Posted by Ken Williams <ke...@mathforum.org>.
On Friday, January 4, 2002, at 02:22 AM, Ken Williams wrote:
> For the sake of thread completion, here's a script which demonstrates 
> the bug.  It turns out to be a Perl bug (5.6.1, at least), not an 
> Apache::Session bug.  I'll post to p5p after I post here.

I was surprised to find the "it's not a bug, it's a feature" defense on 
p5p.  So here's an update.  The following is either a workaround, or the 
proper fix, depending on what you think Perl's proper behavior should 
be. ;-)

{
   local *session;
   tie %session, 'Apache::Session::MySQL', ...;
    ...
}


The "local *session;" is the important bit.  It doesn't work to do
"local %session;", because %session will still be tied even after it 
goes out of scope, and thus the hash data will never get written to 
storage.

In a Mason context, which is where I'm using it, I do this in my 
top-level autohandler (ignore the main:: subroutines, they're just for 
pedagogy):


<%init>
  # 'local' so it's available to lower-level components
  local *session;

  my $dbh = &::get_dbh;
  my $session_id = &::get_cookie('_session_id');
  tie %session, 'Apache::Session::MySQL', $session_id,
               {Handle => $dbh, LockHandle => $dbh};
  ...
</%init>


  -Ken


Re: Apache::Session getting DESTROYed in wrong order

Posted by Ken Williams <ke...@mathforum.org>.
Hey,

For the sake of thread completion, here's a script which demonstrates 
the bug.  It turns out to be a Perl bug (5.6.1, at least), not an 
Apache::Session bug.  I'll post to p5p after I post here.

Note that $foo and %bar are cleaned up by refcount, but %foo isn't 
cleaned up until global destruction.  This means there must be some bad 
interaction between tie(), closures, and global variables, I guess.

-------------------------------------------------------------
#!/usr/bin/perl

use strict;

{
   package Dummy;
   sub new { bless {@_[1,2]} }
   sub TIEHASH { bless {@_[1,2]} }
   sub DESTROY { warn "Destroying $_[0]->{name}: $_[0]" }
}

use vars qw(%foo $foo);

{
   # Will get cleaned up properly
   local $foo = new Dummy(name => '$foo');

   # Will get cleaned up properly
   my %bar;
   tie %bar, 'Dummy', name => '%bar';

   # Won't get cleaned up properly
   local %foo;
   tie %foo, 'Dummy', name => '%foo';
}
------------------------------------------------------------
Destroying %bar: Dummy=HASH(0x632c) at destroy.pl line 9.
Destroying $foo: Dummy=HASH(0x641c) at destroy.pl line 9.
Destroying %foo: Dummy=HASH(0x22ccc) at destroy.pl line 9 during global 
destruction.
------------------------------------------------------------

Investigating with Devel::Peek suggests that it's a %foo refcount 
problem, it's somehow getting set to 2 after tie(%foo).

  -Ken


Re: Apache::Session getting DESTROYed in wrong order

Posted by Perrin Harkins <pe...@elem.com>.
> I don't have a test case involving Apache::Session yet (I've been out of
> town for a couple days), but here's a simple one in Perl that
> demonstrates the DESTROY order problem:

That's sort of a weird example, since it has a circular reference.  Does it
have problems without the circular ref?

> So I think I need to find out why the Apache::Session objects aren't
> being destroyed until global destruction time, i.e. why their refcounts
> aren't going to zero.

At a guess, I'd say you're making an unintentional closure somewhere.

- Perrin


Re: Apache::Session getting DESTROYed in wrong order

Posted by Ken Williams <ke...@mathforum.org>.
Hi Aaron,

I don't have a test case involving Apache::Session yet (I've been out of 
town for a couple days), but here's a simple one in Perl that 
demonstrates the DESTROY order problem:

--------------------------------------------------------------
#!/usr/bin/perl

{
   package Outer;
   sub new { bless {inner => 'Inner'->new} }
   sub DESTROY { warn "Destroying $_[0]" }
}

{
   package Inner;
   sub new { bless {} }
   sub DESTROY { warn "Destroying $_[0]" }
}


{
   warn "refcount destruction:\n";
   my $foo = 'Outer'->new;
}

warn "\nglobal destruction:\n";
my $bar = 'Outer'->new;
$bar->{self} = $bar;
--------------------------------------------------------------

So I think I need to find out why the Apache::Session objects aren't 
being destroyed until global destruction time, i.e. why their refcounts 
aren't going to zero.

This is in the context of testing the new HTML::Mason 1.10, so something 
complicated might be happening with that too.

  -Ken


On Wednesday, January 2, 2002, at 04:15 AM, Aaron E. Ross wrote:
>> refcount destruction.  I've declared %session as a locally-scoped
>> variable, so it should evaporate before global destruction, unless it's
>> got circular data structures or something.  Anyone know what might be
>> going on?
>
>  Do you have a simple case we can test yet?


Re: Apache::Session getting DESTROYed in wrong order

Posted by "Aaron E. Ross" <ro...@coreference.com>.
 
 Hi Ken,

> refcount destruction.  I've declared %session as a locally-scoped 
> variable, so it should evaporate before global destruction, unless it's 
> got circular data structures or something.  Anyone know what might be 
> going on?

 Do you have a simple case we can test yet?

 Aaron


Re: Apache::Session getting DESTROYed in wrong order

Posted by Ken Williams <ke...@mathforum.org>.
On Thursday, January 3, 2002, at 02:02 PM, Jeffrey W. Baker wrote:
> This seems like a really weird problem.  The Store module is destroyed
> while another module still has a reference to it.  Unfortunately for you
> and I, the only conclusion I have been able to draw is that Perl's 
> DESTROY
> magic is unreliable.  We have modules in my company where things are
> randomly undefined in DESTROY subroutines, because the DESTROY of the
> referenced thing has already been called.  So, somewhere in Perl there 
> is
> a bug, possibly an off-by-one in the reference counting.

It's probably not the reference counting, since the global destruction 
phase uses a mark-and-sweep system rather than refcounts (and that's 
where my knowledge ends).

I think that the order of global object destruction is totally random, 
whereas refcount destruction is predictable.  After searching p5p, this 
seems to be known and accepted behavior.

I suppose there could be a refcount bug that's causing %session not to 
be cleaned up until global destruction.


> Anyway you can work around it.  Explicitly call tied(%session)->save()
> when the time is right.

True, I guess I'll do that, but I'd like to figure out a little more 
about it too.


  -Ken


Re: Apache::Session getting DESTROYed in wrong order

Posted by "Jeffrey W. Baker" <jw...@acm.org>.

On Mon, 31 Dec 2001, Ken Williams wrote:

> Hey,
>
> I'm having problems with Apache::Session, the symptom is that none of my
> data is getting written to the database.  It's not the nested-data
> problem, since I'm not using any nested data structures.
>
> After some investigation, I've discovered that the
> Apache::Session::Store::MySQL::DESTROY routine is getting called before
> the Apache::Session::MySQL::DESTROY routine, so when the latter is
> called it doesn't have any way to write to the database.
>
> I think Perl is supposed to guarantee that the outer object's DESTROY is
> called before the inner object's, but I think maybe this guarantee
> doesn't extend to the "global destruction" phase.  So I'm wondering why
> the session is getting cleaned up in global destruction rather than
> refcount destruction.  I've declared %session as a locally-scoped
> variable, so it should evaporate before global destruction, unless it's
> got circular data structures or something.  Anyone know what might be
> going on?
>
> This is Apache::Session version 1.53.
>
> Note: this problem isn't related to mod_perl, but IIRC this list is the
> proper place for discussing Apache::Session.

Ken,

Yeah this is the right list for Apache::Session discussion, and Perrin is
the unofficial guy who answers all the questios, since I don't pay that
much attention.

This seems like a really weird problem.  The Store module is destroyed
while another module still has a reference to it.  Unfortunately for you
and I, the only conclusion I have been able to draw is that Perl's DESTROY
magic is unreliable.  We have modules in my company where things are
randomly undefined in DESTROY subroutines, because the DESTROY of the
referenced thing has already been called.  So, somewhere in Perl there is
a bug, possibly an off-by-one in the reference counting.

Anyway you can work around it.  Explicitly call tied(%session)->save()
when the time is right.

-jwb