perl-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Stas Bekman <s...@stason.org>
Subject Re: [mp2] cloning and $r
Date Wed, 20 Apr 2005 06:18:50 GMT
Stas Bekman wrote:
> Using the CLONE_SKIP approach, we now have a new problem. The following 
> sequence gives us a segfault:
> 
> % t/TEST -maxclients 1  t/modperl/io_with_closed_stds.t t/apr/pool.t
> 
> #0  0xb7b79efb in modperl_wbucket_flush (wb=0x120, add_flush_bucket=0)
>     at modperl_filter.c:236
> 236         if (wb->outcnt) {
> (gdb) bt
> #0  0xb7b79efb in modperl_wbucket_flush (wb=0x120, add_flush_bucket=0)
>     at modperl_filter.c:236
> #1  0xb7b79348 in PerlIOApache_flush (my_perl=0x825b228, f=0x92e8158)
>     at modperl_io_apache.c:173
> #2  0xb7b20208 in Perl_PerlIO_flush (my_perl=0x825b228, f=0x92e8158) at 
> perlio.c:1604
> #3  0xb7b20295 in Perl_PerlIO_flush (my_perl=0x825b228, f=0x92e8158) at 
> perlio.c:1628
> #4  0xb7fe7a68 in Perl_ithread_create (my_perl=0x825b228, obj=0x0,
>     classname=0x93aa620 "threads", init_function=0x9232c70, 
> params=0x9232d84)
>     at threads.xs:415
> [...]
> (gdb) p *wb
> Cannot access memory at address 0x120
> (gdb) p wb
> $1 = (modperl_wbucket_t *) 0x120
> (gdb) up
> #1  0xb7b79348 in PerlIOApache_flush (my_perl=0x825b228, f=0x92e8158)
>     at modperl_io_apache.c:173
> 173                    MP_TRACE_STR_TRUNC(rcfg->wbucket->pool,
> (gdb) p *rcfg
> $3 = {pnotes = 0x8950ad8, global_request_obj = 0x8950af0, flags = 16 
> '\020',
>   status = 155424024, wbucket = 0x120, handlers_per_dir = {0x21, 
> 0xb7cc3838,
>     0xb7cc3838, 0x866c9b0, 0x866ca30, 0x866b578, 0x866cb50, 0x866b410, 
> 0x21, 0x10,
>     0x0}, handlers_per_srv = {0x0, 0x0, 0x0}, perl_globals = {end = {av 
> = 0x10,
>       key = 143993632}, env = {gv = 0x121, tmphv = 0x8951058, orighv = 
> 0xc}, inc = {
>       gv = 0x3, tmpav = 0x3, origav = 0x8666fd0}, defout = {gv = 0x100020,
>       flags = -27 'å'}, rs = {sv = 0x8952c7a,
> 
> As you can see rcfg is totally bogus.
> 
> I'm not sure whether it's the previous test that shows that more things 
> need to be cleaned up at the end of request, or may be we have no choice 
> but to clone $r. Using atomatic cloning of $r may work, as long as 
> threads won't try to access any of $r's records, since a race condition 
> is possible. We can't really prevent it.
> 
> Quite a fun game...
> 
> The interesting thing is that the second test that fails is 'SetHandler 
> modperl', so it doesn't override STDOUT. Therefore it's clear that the 
> first test which is 'SetHandler perl-script' and overriding STDOUT, 
> doesn't bring things back to the clean state after its run. I should 
> probably investigate this possibility first.

OK, I've figure it out: it was Test::Builder who was dupping STDOUT which 
was open to use PerlIO::Apache layer, so essentially it was stashing the 
raw pointer to r. On the following request, anything that tries to reopen 
STDOUT will need to flush it first, and it'll try to flush a bogus r, 
which doesn't exist anymore.

So, I've bandaided this with calling $Test->reset early at the server 
startup, where STDOUT is not yet overriden, now we get:

# XXX: May be this should go into Apache::Test, so other mod_perl test
# suites won't suffer from the same problems.
use Test::Builder;
my $Test = Test::Builder->new;
# under mod_perl we don't want Test::Builder to run the END block
$Test->no_ending(1);
# setup T-B's STD handlers early so that it won't mess up with PerlIO
# layers (which is a death under threads)
$Test->reset;

The problem is that it's clear that our code is very fragile, we can't 
verify whether r is valid or not before using it, if we only have a C 
pointer. Many times the invalid r still have most of its fields valid, so 
checking for specific fields is not an option. So any closure will cause a 
crash, any dupped IO layer will cause a crash. Unfortunately I can't quite 
see how can we protect against this. Ideas?

May be instead of passing $r which has a pointer to r, we should create an 
intermediate pointer, _r which will point to r.

request_rec ** = &r;

So Perl will never see r. At the end of each request we can:

   _r = NULL;

so any closure or dupped IO layer who holds a ghost of $r from the 
previous request will show a clear NULL for which we can check and 
gracefully die, rather than segfault?

Does it look like a working solution? Will this introduce a significant 
overhead?

-- 
__________________________________________________________________
Stas Bekman            JAm_pH ------> Just Another mod_perl Hacker
http://stason.org/     mod_perl Guide ---> http://perl.apache.org
mailto:stas@stason.org http://use.perl.org http://apacheweek.com
http://modperlbook.org http://apache.org   http://ticketmaster.com

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@perl.apache.org
For additional commands, e-mail: dev-help@perl.apache.org


Mime
View raw message