modperl-apache_session_weirdness_maybe_mason_circular_ref_problem_huh

This is part of The Pile, a partial archive of some open source mailing lists and newsgroups.




To: modperl@apache.org
From: "Ken Williams" <ken@mathforum.org>
Subject: Apache::Session getting DESTROYed in wrong order
Date: Mon, 31 Dec 2001 17:13:33 -0600

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.

===

To: "Aaron E. Ross" <ross@coreference.com>
From: "Ken Williams" <ken@mathforum.org>
Subject: Re: Apache::Session getting DESTROYed in wrong
order
Date: Thu, 3 Jan 2002 02:42:50 -0600

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.


===

To: "Ken Williams" <ken@mathforum.org>, "Aaron E. Ross"
<ross@coreference.com>
From: "Perrin Harkins" <perrin@elem.com>
Subject: Re: Apache::Session getting DESTROYed in wrong
order
Date: Thu, 3 Jan 2002 12:57:29 -0500

> 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

===

To: "Perrin Harkins" <perrin@elem.com>
From: "Ken Williams" <ken@mathforum.org>
Subject: Re: Apache::Session getting DESTROYed in wrong
order
Date: Thu, 3 Jan 2002 12:26:26 -0600

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.

===

To: "Ken Williams" <ken@mathforum.org>
From: "Perrin Harkins" <perrin@elem.com>
Subject: Re: Apache::Session getting DESTROYed in wrong
order
Date: Thu, 3 Jan 2002 13:36:01 -0500

> 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.

===

To: Ken Williams <ken@mathforum.org>
From: "Jeffrey W. Baker" <jwbaker@acm.org>
Subject: Re: Apache::Session getting DESTROYed in wrong
order
Date: Thu, 3 Jan 2002 12:02:08 -0800 (PST)

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.

===

To: "Jeffrey W. Baker" <jwbaker@acm.org>
From: "Ken Williams" <ken@mathforum.org>
Subject: Re: Apache::Session getting DESTROYed in wrong
order
Date: Thu, 3 Jan 2002 14:23:14 -0600

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

===
To: "Perrin Harkins" <perrin@elem.com>
From: "Ken Williams" <ken@mathforum.org>
Subject: Re: Apache::Session getting DESTROYed in wrong
order
Date: Fri, 4 Jan 2002 02:22:07 -0600

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).

===

To: "Ken Williams" <ken@mathforum.org>, "Perrin Harkins"
<perrin@elem.com>
From: "Gerald Richter" <richter@ecos.de>
Subject: Re: Apache::Session getting DESTROYed in wrong
order
Date: Fri, 4 Jan 2002 09:48:57 +0100

>    # 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

===

To: "Gerald Richter" <richter@ecos.de>
From: "Ken Williams" <ken@mathforum.org>
Subject: Re: Apache::Session getting DESTROYed in wrong
order
Date: Fri, 4 Jan 2002 17:36:30 -0600

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

===

To: Ken Williams <ken@mathforum.org>
From: "Jeffrey W. Baker" <jwbaker@acm.org>
Subject: Re: Apache::Session getting DESTROYed in wrong
order
Date: Tue, 8 Jan 2002 12:41:08 -0800 (PST)

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

===

To: "Ken Williams" <ken@mathforum.org>
From: "Ken Williams" <ken@mathforum.org>
Subject: Re: Apache::Session getting DESTROYed in wrong
order
Date: Thu, 17 Jan 2002 23:54:13 -0600

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>


===

To: "Ken Williams" <ken@mathforum.org>
From: "Perrin Harkins" <perrin@elem.com>
Subject: Re: Apache::Session getting DESTROYed in wrong
order
Date: Fri, 18 Jan 2002 01:44:06 -0500

> 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?

===

To: "Perrin Harkins" <perrin@elem.com>
From: "Ken Williams" <ken@mathforum.org>
Subject: Re: Apache::Session getting DESTROYed in wrong
order
Date: Fri, 18 Jan 2002 00:53:34 -0600

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".  ;-)

===

To: "Perrin Harkins" <perrin@elem.com>
From: "Jay Lawrence" <Jay@Lawrence.Net>
Subject: Re: Apache::Session getting DESTROYed in wrong
order
Date: Fri, 18 Jan 2002 09:11:47 -0500


"Ken Williams" <ken@mathforum.org> wrote: 

> 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".  ;-)

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.

===

To: "Jay Lawrence" <Jay@Lawrence.Net>
From: "Perrin Harkins" <perrin@elem.com>
Subject: Re: Apache::Session getting DESTROYed in wrong
order
Date: Fri, 18 Jan 2002 11:00:38 -0500

> 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.

===

To: modperl@apache.org
From: William White <whitew@library.ucsf.edu>
Subject: locking bug in Apache::Session::File
Date: Fri, 18 Jan 2002 13:17:05 -0800

I've been told this is the place to send questions related to apache perl 
modules.

I believe I have discovered a locking bug in Apache::Session::File.

The following code should retrieve an existing session from the file system 
and place an exclusive lock on the session file:

my $locking_args = { 'Directory' => '/tmp/sessions_dir',
                                 'LockDirectory' => '/tmp/sessions_lock_dir',
                                 'Transaction' => '1' };

tie(%session, 'Apache::Session::File', $session_id, $locking_args);

The 'locking_args' hash is used to pass parameters to the locking object 
contained by the session object.  According to the Apache::Session 
documentation any true value of "Transaction" should force the object to 
exclusively lock the session file.  Unfortunately this does not appear to 
work (at least not all the time).

Looking in the TIEHASH I think I've discovered the reason.  The session 
uses a locking object.  In this case the locking object is 
Apache::Session::Lock::File.  This object has two methods which acquire 
locks, aptly named acquire_read_lock and acquire_write_lock.  The first 
method uses flock to acquire a non-exclusive lock.  The second method uses 
flock to acquire an exclusive lock.  TIEHASH checks the value of 
'Transaction' and calls acquire_write_lock if the value is true.  It then 
calls a method named restore.  It does this regardless of the value of 
'Transaction'.  The restore method calls acquire_read_lock.  Again it does 
this without examining the value of 'Transaction'.

Now according to the flock man page if a process requests a lock on a file 
it already has locked, then the new lock will replace the old one.  Thus 
requesting a non-exclusive lock on file which the process already has an 
exclusive lock for will cause the non-exclusive lock to replace the 
exclusive one.

The call to acquire_read_lock in the restore method wipes out the exclusive 
lock on the session file.  This makes it impossible to maintain 
transactional consistency with Apache::Session::File.

I was wondering if anyone else out there has run into this problem.  Is 
there a fix available?  My version of Apache::Session is 1.54 which is the 
newest version that I see on CPAN.  Is there another version out there that 
fixes this problem or should I bring this up with the author?

===


the rest of The Pile (a partial mailing list archive)

doom@kzsu.stanford.edu