Please take our Survey
logo       

Choosing A Webhost:
A web hosting service is a type of Internet hosting service that allows individuals and organizations to provide their own website accessible via the World Wide Web. Web hosts are companies that provide space on a server they own for use by their clients as well as providing Internet connectivity, typically in a data center. Web hosts can also provide data center space and connectivity to the Internet for servers they do not own to be located in their data center, called colocation. more...

Re: the pools destruction issue solved: msg#00293

apache.mod-perl.devel

Subject: Re: the pools destruction issue solved

Strange, Randy says, the patch won't apply. The patch had no tabs, may be the mailer has messed it up. Here it is as an attachment.

__________________________________________________________________
Stas Bekman JAm_pH ------> Just Another mod_perl Hacker
http://stason.org/ mod_perl Guide ---> http://perl.apache.org
mailto:stas@xxxxxxxxxx http://use.perl.org http://apacheweek.com
http://modperlbook.org http://apache.org http://ticketmaster.com
Index: t/response/TestAPR/pool.pm
===================================================================
RCS file: /home/cvs/modperl-2.0/t/response/TestAPR/pool.pm,v
retrieving revision 1.5
diff -u -r1.5 pool.pm
--- t/response/TestAPR/pool.pm 9 Sep 2003 17:22:39 -0000 1.5
+++ t/response/TestAPR/pool.pm 26 Sep 2003 00:31:12 -0000
@@ -1,9 +1,11 @@
package TestAPR::pool;

use strict;
-use warnings FATAL => 'all';
+use warnings;# FATAL => 'all';

use Apache::Test;
+use Apache::TestUtil;
+use Apache::TestTrace;

use Apache::RequestRec ();
use APR::Pool ();
@@ -11,85 +13,214 @@

use Apache::Const -compile => 'OK';

-sub add_cleanup {
- my $arg = shift;
- $arg->[0]->notes->add(cleanup => $arg->[1]);
- 1;
-}
-
-sub set_cleanup {
- my $arg = shift;
- $arg->[0]->notes->set(cleanup => $arg->[1]);
- 1;
-}
-
sub handler {
my $r = shift;

- plan $r, tests => 13;
+ plan $r, tests => 38;

- my $p = APR::Pool->new;
+ ### native pools ###

- ok $p->isa('APR::Pool');
+ # explicit and implicit DESTROY shouldn't destroy native pools
+ {
+ my $p = $r->pool;

- my $subp = $p->new;
+ ok t_cmp(5, ancestry_count($p), "\$r->pool has 5 ancestors");

- ok $subp->isa('APR::Pool');
+ $p->cleanup_register(\&set_cleanup, [$r, 'native DESTROY']);

-#only available with -DAPR_POOL_DEBUG
-# my $num_bytes = $p->num_bytes;
-# ok $num_bytes;
+ $p->DESTROY;

- $p->cleanup_register(\&add_cleanup, [$r, 'parent']);
- $subp->cleanup_register(\&set_cleanup, [$r, 'child']);
+ my @notes = $r->notes->get('cleanup');

- # should destroy the subpool too
- $p->destroy;
+ ok t_cmp(0, scalar(@notes), "should be 0 notes");

- my @notes = $r->notes->get('cleanup');
- ok $notes[0] eq 'child';
- ok $notes[1] eq 'parent';
- ok @notes == 2;
+ $r->notes->clear;
+ }
+
+ # implicit DESTROY shouldn't destroy native pools
+ {
+ {
+ my $p = $r->pool;

- # explicity DESTROY the objects
- my $p2 = APR::Pool->new;
- $p2->cleanup_register(\&set_cleanup, [$r, 'new DESTROY']);
- $p2->DESTROY;
+ ok t_cmp(5, ancestry_count($p), "\$r->pool has 5 ancestors");

- @notes = $r->notes->get('cleanup');
- ok $notes[0] eq 'new DESTROY';
- ok @notes == 1;
+ $p->cleanup_register(\&set_cleanup, [$r, 'native scoped']);
+ }

- # DESTROY should be a no-op on native pools
- my $p3 = $r->pool;
- $p3->cleanup_register(\&set_cleanup, [$r, 'native DESTROY']);
- $p3->DESTROY;
+ my @notes = $r->notes->get('cleanup');

- @notes = $r->notes->get('cleanup');
- ok $notes[0] eq 'new DESTROY'; # same as before - no change
- ok @notes == 1;
+ ok t_cmp(0, scalar(@notes), "should be 0 notes");

- # make sure lexical scoping destroys the pool
- {
- my $p4 = APR::Pool->new;
- $p4->cleanup_register(\&set_cleanup, [$r, 'new scoped']);
+ $r->notes->clear;
}

- @notes = $r->notes->get('cleanup');
- ok $notes[0] eq 'new scoped';
- ok @notes == 1;

- # but doesn't affect native pools
+ ### custom pools ###
+
+
+ # test: explicit pool object DESTROY destroys the custom pool
{
- my $p5 = $r->pool;
- $p5->cleanup_register(\&set_cleanup, [$r, 'native scoped']);
+ my $p = APR::Pool->new;
+
+ $p->cleanup_register(\&set_cleanup, [$r, 'new DESTROY']);
+
+ ok t_cmp(1, ancestry_count($p),
+ "a new pool has one ancestor: the global pool");
+
+ # explicity DESTROY the object
+ $p->DESTROY;
+
+ my @notes = $r->notes->get('cleanup');
+
+ ok t_cmp(1, scalar(@notes), "should be 1 note");
+
+ ok t_cmp('new DESTROY', $notes[0]);
+
+ $r->notes->clear;
+ }
+
+
+ # test: lexical scoping DESTROYs the custom pool
+ {
+ {
+ my $p = APR::Pool->new;
+
+ ok t_cmp(1, ancestry_count($p),
+ "a new pool has one ancestor: the global pool");
+
+ $p->cleanup_register(\&set_cleanup, [$r, 'new scoped']);
+ }
+
+ my @notes = $r->notes->get('cleanup');
+
+ ok t_cmp(1, scalar(@notes), "should be 1 note");
+
+ ok t_cmp('new scoped', $notes[0]);
+
+ $r->notes->clear;
+ }
+
+ ### custom pools + sub-pools ###
+
+ # test: basic pool and sub-pool tests + implicit destroy of pool objects
+ {
+ {
+ my ($pp, $sp) = both_pools_create_ok($r);
+ }
+
+ both_pools_destroy_ok($r);
+
+ $r->notes->clear;
+ }
+
+
+ # test: explicitly destroying a parent pool should destroy its
+ # sub-pool
+ {
+ my ($pp, $sp) = both_pools_create_ok($r);
+
+ # destroying $pp should destroy the subpool $sp too
+ $pp->DESTROY;
+
+ both_pools_destroy_ok($r);
+
+ $r->notes->clear;
+ }
+
+
+ # test: destroying a sub-pool before the parent pool
+ {
+ my ($pp, $sp) = both_pools_create_ok($r);
+
+ $sp->DESTROY;
+ $pp->DESTROY;
+
+ both_pools_destroy_ok($r);
+
+ $r->notes->clear;
+ }
+
+
+
+ # test: destroying a sub-pool explicitly after the parent pool
+ {
+ my ($pp, $sp) = both_pools_create_ok($r);
+
+ $pp->DESTROY;
+ $sp->DESTROY;
+
+ both_pools_destroy_ok($r);
+
+ $r->notes->clear;
}

- @notes = $r->notes->get('cleanup');
- ok $notes[0] eq 'new scoped'; # same as before - no change
- ok @notes == 1;

Apache::OK;
+}
+
+# returns how many ancestor generations the pool has (parent,
+# grandparent, etc.)
+sub ancestry_count {
+ my $child = shift;
+ my $gen = 0;
+ while (my $parent = $child->parent_get) {
+ # prevent possible endless loops
+ die "child pool reports to be its own parent, corruption!"
+ if $parent == $child;
+ $gen++;
+ die "child knows its parent, but the parent denies having that child"
+ unless $parent->is_ancestor($child);
+ $child = $parent;
+ }
+ return $gen;
+}
+
+
+sub add_cleanup {
+ my $arg = shift;
+ $arg->[0]->notes->add(cleanup => $arg->[1]);
+ 1;
+}
+
+sub set_cleanup {
+ my $arg = shift;
+ $arg->[0]->notes->set(cleanup => $arg->[1]);
+ 1;
+}
+
+# +4 tests
+sub both_pools_create_ok {
+ my $r = shift;
+
+ my $pp = APR::Pool->new;
+
+ ok t_cmp(1, $pp->isa('APR::Pool'), "isa('APR::Pool')");
+
+ ok t_cmp(1, ancestry_count($pp),
+ "a new pool has one ancestor: the global pool");
+
+ my $sp = $pp->new;
+
+ ok t_cmp(1, $sp->isa('APR::Pool'), "isa('APR::Pool')");
+
+ ok t_cmp(2, ancestry_count($sp),
+ "a subpool has 2 ancestors: the parent and global pools");
+
+ $pp->cleanup_register(\&add_cleanup, [$r, 'parent']);
+ $sp->cleanup_register(\&set_cleanup, [$r, 'child']);
+
+ return ($pp, $sp);
+
+}
+
+# +3 tests
+sub both_pools_destroy_ok {
+ my $r = shift;
+ my @notes = $r->notes->get('cleanup');
+
+ ok t_cmp(2, scalar(@notes), "should be 2 notes");
+ ok t_cmp('child', $notes[0]);
+ ok t_cmp('parent', $notes[1]);
}

1;
Index: xs/APR/Pool/APR__Pool.h
===================================================================
RCS file: /home/cvs/modperl-2.0/xs/APR/Pool/APR__Pool.h,v
retrieving revision 1.6
diff -u -r1.6 APR__Pool.h
--- xs/APR/Pool/APR__Pool.h 9 Sep 2003 17:22:39 -0000 1.6
+++ xs/APR/Pool/APR__Pool.h 26 Sep 2003 00:31:12 -0000
@@ -1,22 +1,160 @@
#define MP_APR_POOL_NEW "APR::Pool::new"

+typedef struct {
+ int destroyable;
+ int ref_count;
+} mpxs_pool_account_t;
+
+static MP_INLINE void mpxs_apr_pool_destroyable_set(apr_pool_t *p)
+{
+ mpxs_pool_account_t *data;
+
+ apr_pool_userdata_get((void **)&data, MP_APR_POOL_NEW, p);
+ if (!data) {
+ data = (mpxs_pool_account_t *)apr_pcalloc(p, sizeof(*data));
+ }
+
+ data->destroyable++;
+
+ apr_pool_userdata_set(data, MP_APR_POOL_NEW, NULL, p);
+}
+
+static MP_INLINE void mpxs_apr_pool_destroyable_unset(apr_pool_t *p)
+{
+ mpxs_pool_account_t *data;
+
+ apr_pool_userdata_get((void **)&data, MP_APR_POOL_NEW, p);
+ if (!data) {
+ data = (mpxs_pool_account_t *)apr_pcalloc(p, sizeof(*data));
+ }
+
+ data->destroyable = 0;
+
+ apr_pool_userdata_set(data, MP_APR_POOL_NEW, NULL, p);
+}
+
+static MP_INLINE int mpxs_apr_pool_ref_count_inc(apr_pool_t *p)
+{
+ mpxs_pool_account_t *data;
+
+ apr_pool_userdata_get((void **)&data, MP_APR_POOL_NEW, p);
+ if (!data) {
+ data = (mpxs_pool_account_t *)apr_pcalloc(p, sizeof(*data));
+ }
+
+ data->ref_count++;
+
+ apr_pool_userdata_set(data, MP_APR_POOL_NEW, NULL, p);
+
+ return data->ref_count;
+}
+
+static MP_INLINE int mpxs_apr_pool_ref_count_dec(apr_pool_t *p)
+{
+ mpxs_pool_account_t *data;
+
+ apr_pool_userdata_get((void **)&data, MP_APR_POOL_NEW, p);
+ if (!data) {
+ data = (mpxs_pool_account_t *)apr_pcalloc(p, sizeof(*data));
+ }
+
+ if (data->ref_count > 0) {
+ data->ref_count--;
+ }
+
+ apr_pool_userdata_set(data, MP_APR_POOL_NEW, NULL, p);
+
+ return data->ref_count;
+}
+
+static MP_INLINE int mpxs_apr_pool_is_pool_destroyable(apr_pool_t *p)
+{
+ mpxs_pool_account_t *data;
+
+ apr_pool_userdata_get((void **)&data, MP_APR_POOL_NEW, p);
+ if (!data) {
+ data = (mpxs_pool_account_t *)apr_pcalloc(p, sizeof(*data));
+ }
+
+ return data->destroyable && !data->ref_count;
+}
+
+static MP_INLINE apr_status_t mpxs_apr_pool_unflag(void *data)
+{
+ /* unset the flag for the key MP_APR_POOL_NEW to prevent from
+ * apr_pool_destroy being called twice */
+ mpxs_apr_pool_destroyable_unset((apr_pool_t *)data);
+
+ return APR_SUCCESS;
+}
+
+
/**
- * create a new pool or subpool
- * @param obj an APR::Pool object or NULL
- * @return a new pool or subpool
+ * Create a new pool or subpool. Pass APR::Pool as an object if it's
+ * not a subpool.
+ * @param parent_pool_obj an APR::Pool object or NULL
+ * @return a new pool or subpool
*/
-static MP_INLINE apr_pool_t *mpxs_apr_pool_create(pTHX_ SV *obj)
+static MP_INLINE apr_pool_t *mpxs_apr_pool_create(pTHX_ SV *parent_pool_obj)
{
- apr_pool_t *parent = mpxs_sv_object_deref(obj, apr_pool_t);
- apr_pool_t *newpool = NULL;
- (void)apr_pool_create(&newpool, parent);
-
- /* mark the pool as being created via APR::Pool->new()
- * see mpxs_apr_pool_DESTROY */
- apr_pool_userdata_set((const void *)1, MP_APR_POOL_NEW,
- apr_pool_cleanup_null, newpool);
+ apr_pool_t *parent_pool = mpxs_sv_object_deref(parent_pool_obj,
apr_pool_t);
+ apr_pool_t *child_pool = NULL;
+
+ (void)apr_pool_create(&child_pool, parent_pool);
+ Perl_warn(aTHX_ "==> MP_DEBUG: new pool 0x%lx\n", child_pool);
+
+#if APR_POOL_DEBUG
+ apr_pool_tag(child_pool, MP_APR_POOL_NEW);
+#endif
+
+ /* corruption validation */
+ if (child_pool == parent_pool) {
+ Perl_croak(aTHX_ "a newly allocated sub-pool 0x%lx "
+ "is the same as its parent 0x%lx, aborting",
+ (unsigned long)child_pool, (unsigned long)parent_pool);
+ }

- return newpool;
+ /* mark the pool eligible for destruction. We aren't suppose to
+ * destroy pools not created by APR::Pool::new().
+ * see mpxs_apr_pool_DESTROY
+ */
+ mpxs_apr_pool_destroyable_set(child_pool);
+
+ /* Each newly created pool must be destroyed only once. Calling
+ * apr_pool_destroy will destroy the pool and its children pools,
+ * however a perl object for a sub-pool will still keep a pointer
+ * to the pool which was already destroyed. When this object is
+ * DESTROYed, apr_pool_destroy will be called again. In the best
+ * case it'll try to destroy a non-existing pool, but in the worst
+ * case it'll destroy a different valid pool which has been given
+ * the same memory allocation wrecking havoc. Therefore we must
+ * ensure that when sub-pools are destroyed via the parent pool,
+ * their cleanup callbacks will destroy their perl objects
+ */
+ apr_pool_cleanup_register(child_pool, (void *)child_pool,
+ mpxs_apr_pool_unflag,
+ apr_pool_cleanup_null);
+#if APR_POOL_DEBUG
+ /* child <-> parent <-> ... <-> top ancestry traversal */
+ {
+ apr_pool_t *p = child_pool;
+ apr_pool_t *pp;
+
+ while ((pp = apr_pool_parent_get(p))) {
+ Perl_warn(aTHX_ "==> MP_DEBUG: parent 0x%lx, child 0x%lx\n",
+ (unsigned long)pp, (unsigned long)p);
+
+ if (apr_pool_is_ancestor(pp, p)) {
+ Perl_warn(aTHX_ "==> MP_DEBUG: 0x%lx is a subpool of 0x%lx\n",
+ (unsigned long)p, (unsigned long)pp);
+ }
+ p = pp;
+ }
+ }
+#endif
+
+ mpxs_apr_pool_ref_count_inc(child_pool);
+ return child_pool;
}

typedef struct {
@@ -111,26 +249,54 @@
apr_pool_cleanup_null);
}

+
+static MP_INLINE apr_pool_t *
+mpxs_apr_pool_parent_get(pTHX_ apr_pool_t *child_pool)
+{
+ apr_pool_t *parent_pool = apr_pool_parent_get(child_pool);
+ if (parent_pool) {
+ mpxs_apr_pool_ref_count_inc(parent_pool);
+ }
+
+ return parent_pool;
+
+}
+
+
+
+
/**
* destroy a pool
* @param obj an APR::Pool object
*/
static MP_INLINE void mpxs_apr_pool_DESTROY(pTHX_ SV *obj) {

- void *flag;
apr_pool_t *p;

+ p = mpxs_sv_object_deref(obj, apr_pool_t);
+
+ mpxs_apr_pool_ref_count_dec(p);
+
/* APR::Pool::DESTROY
* we only want to call DESTROY on objects created by
* APR::Pool->new(), not objects representing native pools
* like r->pool. native pools can be destroyed using
- * apr_pool_destroy ($p->destroy) */
-
- p = mpxs_sv_object_deref(obj, apr_pool_t);
-
- apr_pool_userdata_get(&flag, MP_APR_POOL_NEW, p);
-
- if (flag) {
- apr_pool_destroy(p);
+ * apr_pool_destroy ($p->destroy)
+ */
+ if (mpxs_apr_pool_is_pool_destroyable(p)) {
+ Perl_warn(aTHX_ "==> MP_DEBUG: DESTROY pool 0x%lx\n", (unsigned
long)p);
+ apr_pool_destroy(p);
+ /* mpxs_apr_pool_unflag called by apr_pool_destroy takes care
+ * of marking this pool as undestroyable, so we do it only once */
+ }
+ else {
+ /* either because we didn't create this pool (e.g., r->pool),
+ * or because this pool has already been destroyed via the
+ * destruction of the parent pool
+ */
+ Perl_warn(aTHX_ "==> MP_DEBUG: skipping DESTROY, "
+ "this object is not eligible to destroy pool 0x%lx\n",
+ (unsigned long)p);
+
}
}
Index: xs/maps/apr_functions.map
===================================================================
RCS file: /home/cvs/modperl-2.0/xs/maps/apr_functions.map,v
retrieving revision 1.58
diff -u -r1.58 apr_functions.map
--- xs/maps/apr_functions.map 9 Sep 2003 17:22:39 -0000 1.58
+++ xs/maps/apr_functions.map 26 Sep 2003 00:31:12 -0000
@@ -157,7 +157,7 @@
apr_pool_destroy
DEFINE_DESTROY | mpxs_apr_pool_DESTROY | SV *:obj
>apr_pool_destroy_debug
- apr_pool_t *:DEFINE_new | mpxs_apr_pool_create | SV *:obj
+ apr_pool_t *:DEFINE_new | mpxs_apr_pool_create | SV *:parent_pool_obj
-apr_pool_create_ex
>apr_pool_create_ex_debug
!apr_pool_userdata_get
@@ -175,7 +175,7 @@
-apr_pmemdup
!apr_pool_child_cleanup_set
!apr_pool_abort_get
- apr_pool_parent_get
+ apr_pool_parent_get | mpxs_
apr_pool_is_ancestor
-apr_pool_abort_set
>apr_pool_initialize
Index: xs/tables/current/ModPerl/FunctionTable.pm
===================================================================
RCS file: /home/cvs/modperl-2.0/xs/tables/current/ModPerl/FunctionTable.pm,v
retrieving revision 1.122
diff -u -r1.122 FunctionTable.pm
--- xs/tables/current/ModPerl/FunctionTable.pm 9 Sep 2003 17:22:39 -0000
1.122
+++ xs/tables/current/ModPerl/FunctionTable.pm 26 Sep 2003 00:31:12 -0000
@@ -6429,6 +6429,24 @@
]
},
{
+ 'return_type' => 'apr_pool_t *',
+ 'name' => 'mpxs_apr_pool_parent_get',
+ 'attr' => [
+ 'static',
+ '__inline__'
+ ],
+ 'args' => [
+ {
+ 'type' => 'PerlInterpreter *',
+ 'name' => 'my_perl'
+ },
+ {
+ 'type' => 'apr_pool_t *',
+ 'name' => 'child_pool'
+ },
+ ]
+ },
+ {
'return_type' => 'void',
'name' => 'mpxs_apr_pool_DESTROY',
'attr' => [

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@xxxxxxxxxxxxxxx
For additional commands, e-mail: dev-help@xxxxxxxxxxxxxxx
<Prev in Thread] Current Thread [Next in Thread>
Google Custom Search

Recently Viewed:
drivers.mtd/200...    security.firewa...    java.openamf.cv...    rpm.yum/2003-08...    telephony.sipp....    file-systems.oc...    qnx.openqnx.dev...    voip.linphone.u...    hardware.sony/2...    network.simulat...    boot-loaders.gr...    ietf.usenet.for...    culture.languag...    emacs.latex.pre...    music.jamiroqua...    xfree86.neomagi...    user-groups.lin...    ltp/2006-08/msg...    kde.kst/2005-08...    programming.too...    os.freebsd.deve...    window-managers...    audio.cd-record...    gnu.fiasco.bugs...   
Home | advertise | OSDir is an inevitable website. super tiny logo

Free Magazines

Cisco News
Receive a free quarterly e-newsletter with exclusive articles on how Cisco IT uses its own products and solutions to enable the business.
subscribe

Systems Management News, the newspaper for IT systems administration and data center managers! Each issue of Systems Management News is chock-full of news and analysis to help you understand what's happening in your field.
subscribe

The Enterprise Newsweekly eWeek is the essential technology information source for builders of e-business.
subscribe

Oracle Magazine Oracle Magazine contains technology strategy articles, sample code, tips, Oracle and partner news, how to articles for developers and DBAs, and more. Oracle (NASDAQ: ORCL) is the world's largest enterprise software company.
subscribe

Total Telecom Total Telecom is "The Economist of the communications industry".
subscribe

Navigation