You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@perl.apache.org by Stas Bekman <st...@stason.org> on 2004/01/24 12:35:09 UTC

[mp2] APR::Pool re-implementation plus tests

Finally I had the time to finish the reimplementation of the APR::Pool 
(again!). The problem with the current implementation is that a dead perl pool 
object can hijack a newly create pool, which doesn't belong to that object, 
but which happen to be allocated at the same memory pointer. The problem is 
that apr_pool_user_data_set/get has no mechanism to check whether the pool has 
changed since it was last assigned to. It really needs some signature 
mechanism which can be verified that the pool is still the same pool. Since 
apr_pool doesn't sport this feature, I've reimplemented the reference counting 
using a plain sv reference. I've added a whole bunch of new (mainly hijacking) 
tests which badly fail with the current impelementation.

Also at the moment we don't quite handle ithreads, as the cloned perl objects 
will point to the same apr pool and each will try to destroy it. or one will 
try to use it after the other object has freed it already. so we need to use 
the CLONE class method with not quite working in pre 5.8.3 perls 
Util::Scalar::weaken. So it's a mess. Ideally people should not use custom 
pools allocated before new perl interpreters are started, inside these 
interpreters.

Index: lib/ModPerl/WrapXS.pm
===================================================================
RCS file: /home/cvs/modperl-2.0/lib/ModPerl/WrapXS.pm,v
retrieving revision 1.63
diff -u -r1.63 WrapXS.pm
--- lib/ModPerl/WrapXS.pm	17 Dec 2003 21:21:28 -0000	1.63
+++ lib/ModPerl/WrapXS.pm	24 Jan 2004 11:21:52 -0000
@@ -524,9 +524,10 @@

  my %typemap = (
      'Apache::RequestRec' => 'T_APACHEOBJ',
-    'apr_time_t' => 'T_APR_TIME',
-    'APR::Table' => 'T_HASHOBJ',
-    'APR::OS::Thread' => 'T_UVOBJ',
+    'apr_time_t'         => 'T_APR_TIME',
+    'APR::Table'         => 'T_HASHOBJ',
+    'APR::Pool'          => 'T_POOLOBJ',
+    'APR::OS::Thread'    => 'T_UVOBJ',
  );

  sub write_typemap {
Index: t/response/TestAPR/pool.pm
===================================================================
RCS file: /home/cvs/modperl-2.0/t/response/TestAPR/pool.pm,v
retrieving revision 1.7
diff -u -r1.7 pool.pm
--- t/response/TestAPR/pool.pm	26 Sep 2003 08:56:11 -0000	1.7
+++ t/response/TestAPR/pool.pm	24 Jan 2004 11:21:54 -0000
@@ -16,7 +16,7 @@
  sub handler {
      my $r = shift;

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

      ### native pools ###

@@ -39,6 +39,7 @@
          $r->notes->clear;
      }

+
      # implicit DESTROY shouldn't destroy native pools
      {
          {
@@ -58,7 +59,6 @@
          $r->notes->clear;
      }

-
      ### custom pools ###


@@ -84,6 +84,9 @@
      }


+
+
+
      # test: lexical scoping DESTROYs the custom pool
      {
          {
@@ -132,6 +135,7 @@
      }


+
      # test: destroying a sub-pool before the parent pool
      {
          my ($pp, $sp) = both_pools_create_ok($r);
@@ -145,8 +149,10 @@
      }


+    # test: destroying a sub-pool explicitly after the parent pool destroy

-    # test: destroying a sub-pool explicitly after the parent pool
+    # the parent pool should have already destroyed the child pool, so
+    # the object is invalid
      {
          my ($pp, $sp) = both_pools_create_ok($r);

@@ -156,6 +162,173 @@
          both_pools_destroy_ok($r);

          $r->notes->clear;
+    }
+
+
+    # test: destroying a sub-pool before the parent pool and trying to
+    # call APR::Pool methods on the a subpool object which points to a
+    # destroyed pool
+    {
+        my ($pp, $sp) = both_pools_create_ok($r);
+
+        # parent pool destroys child pool
+        $pp->DESTROY;
+
+        # this should "gracefully" fail, since $sp's guts were
+        # destroyed when the parent pool was destroyed
+        eval { $pp = $sp->parent_get };
+        ok t_cmp(qr/invalid pool object/,
+                 $@,
+                 "parent pool destroys child pool");
+
+        # since pool $sp now contains 0 pointer, if we try to make a
+        # new pool out of it, it's the same as APR->new (i.e. it'll
+        # use the global top level pool for it), so the resulting pool
+        # should have an ancestry length of exactly 1
+        my $ssp = $sp->new;
+        ok t_cmp(1, ancestry_count($ssp),
+                 "a new pool has one ancestor: the global pool");
+
+
+        both_pools_destroy_ok($r);
+
+        $r->notes->clear;
+    }
+
+    # test: make sure that one pool won't destroy/affect another pool,
+    # which happened to be allocated at the same memory address after
+    # the pointer to the first pool was destroyed
+    {
+        my $pp2;
+        {
+            my $pp = APR::Pool->new;
+            $pp->DESTROY;
+            # $pp2 ideally should take the exact place of apr_pool
+            # previously pointed to by $pp
+            $pp2 = APR::Pool->new;
+            # $pp object didn't go away yet (it'll when exiting this
+            # scope). in the previous implementation, $pp will be
+            # DESTROY'ed second time on the exit of the scope and it
+            # could happen to work, because $pp2 pointer has allocated
+            # exactly the same address. and if so it would have killed
+            # the pool that $pp2 points to
+
+            # this should "gracefully" fail, since $pp's guts were
+            # destroyed when the parent pool was destroyed
+            # must make sure that it won't try to hijack the new pool
+            # $pp2 that (hopefully) took over $pp's place
+            eval { $pp->parent_get };
+            ok t_cmp(qr/invalid pool object/,
+                     $@,
+                     "a dead pool is a dead pool");
+        }
+
+        # next make sure that $pp2's pool is still alive
+        $pp2->cleanup_register(\&set_cleanup, [$r, 'overtake']);
+        $pp2->DESTROY;
+
+        my @notes = $r->notes->get('cleanup');
+
+        ok t_cmp(1, scalar(@notes), "should be 1 note");
+        ok t_cmp('overtake', $notes[0]);
+
+        $r->notes->clear;
+
+    }
+
+    # test: similar to the previous test, but this time, the parent
+    # pool destroys the child pool. a second allocation of a new pair
+    # of the parent and child pools take over exactly the same
+    # allocations. so if there are any ghost objects, they must not
+    # find the other pools and use them as they own. for example they
+    # could destroy the pools, and the perl objects of the pair would
+    # have no idea that someone has destroyed the pools without their
+    # knowledge. the previous implementation suffered from this
+    # problem. the new implementation uses an SV which is stored in
+    # the object and in the pool. when the pool is destroyed the SV
+    # gets its IVX pointer set to 0, which affects any perl object
+    # that is a ref to that SV. so once an apr pool is destroyed all
+    # perl objects pointing to it get automatically invalidated and
+    # there is no risk of hijacking newly created pools that happen to
+    # be at the same memory address.
+
+    {
+        my ($pp2, $sp2);
+        {
+            my $pp = APR::Pool->new;
+            my $sp = $pp->new;
+            # parent destroys $sp
+            $pp->DESTROY;
+
+            # hopefully these pool will take over the $pp and $sp
+            # allocations
+            ($pp2, $sp2) = both_pools_create_ok($r);
+        }
+
+        # $pp and $sp shouldn't have triggered any cleanups
+        my @notes = $r->notes->get('cleanup');
+        ok t_cmp(0, scalar(@notes), "should be 0 notes");
+        $r->notes->clear;
+
+        # parent pool destroys child pool
+        $pp2->DESTROY;
+
+        both_pools_destroy_ok($r);
+
+        $r->notes->clear;
+    }
+
+    ## $sp is bogus, this shoudl fail!
+    #    my $table = APR::Table::make($sp, 2);
+
+
+    # test: only when the last references to the pool object is gone
+    # it should get destroyed
+    {
+
+        my $cp;
+
+        {
+            my $sp = $r->pool->new;
+
+            $sp->cleanup_register(\&set_cleanup, [$r, 'several references']);
+
+            $cp = $sp;
+            # destroy of $sp shouldn't call apr_pool_destroy, because
+            # $cp still references to it
+        }
+
+        my @notes = $r->notes->get('cleanup');
+        ok t_cmp(0, scalar(@notes), "should be 0 notes");
+        $r->notes->clear;
+
+        # now the last copy is gone and the cleanup hooks will be called
+        $cp->DESTROY;
+
+        @notes = $r->notes->get('cleanup');
+        ok t_cmp(1, scalar(@notes), "should be 1 note");
+        ok t_cmp('several references', $notes[0]);
+    }
+
+    {
+        # and another variation
+        my $pp = $r->pool->new;
+        my $sp = $pp->new;
+
+        my $gp  = $pp->parent_get;
+        my $pp2 = $sp->parent_get;
+
+        # parent destroys children
+        $pp->DESTROY;
+
+        # grand parent ($r->pool) is undestroyable (core pool)
+        $gp->DESTROY;
+
+        # now all custom pools are destroyed - $sp and $pp2 point nowhere
+        $pp2->DESTROY;
+        $sp->DESTROY;
+
+        ok 1;
      }

      # other stuff
Index: xs/typemap
===================================================================
RCS file: /home/cvs/modperl-2.0/xs/typemap,v
retrieving revision 1.9
diff -u -r1.9 typemap
--- xs/typemap	11 Jul 2002 06:14:10 -0000	1.9
+++ xs/typemap	24 Jan 2004 11:21:54 -0000
@@ -5,6 +5,9 @@

  ######################################################################
  OUTPUT
+T_POOLOBJ
+        sv_setref_pv($arg, \"${ntype}\", (void*)$var);
+
  T_APACHEOBJ
  	sv_setref_pv($arg, \"${ntype}\", (void*)$var);

@@ -33,7 +36,20 @@
                         \"$var is not a blessed reference\");
          }

-INPUT
+T_POOLOBJ
+	if (SvROK($arg) && sv_derived_from($arg, \"${ntype}\")) {
+	    IV tmp = SvIV((SV*)SvRV($arg));
+            if (tmp == 0) {
+                Perl_croak(aTHX_ \"invalid pool object (already destroyed?)\");
+            }
+	    $var = INT2PTR($type,tmp);
+	}
+	else {
+	    Perl_croak(aTHX_ SvROK($arg) ?
+                       \"$var is not of type ${ntype}\" :
+                       \"$var is not a blessed reference\");
+        }
+
  T_UVOBJ
  	if (SvROK($arg) && sv_derived_from($arg, \"${ntype}\")) {
  	    UV tmp = SvUV((SV*)SvRV($arg));
Index: xs/APR/Pool/APR__Pool.h
===================================================================
RCS file: /home/cvs/modperl-2.0/xs/APR/Pool/APR__Pool.h,v
retrieving revision 1.8
diff -u -r1.8 APR__Pool.h
--- xs/APR/Pool/APR__Pool.h	30 Sep 2003 21:18:39 -0000	1.8
+++ xs/APR/Pool/APR__Pool.h	24 Jan 2004 11:21:54 -0000
@@ -1,13 +1,27 @@
  #define MP_APR_POOL_NEW "APR::Pool::new"

  typedef struct {
-    int destroyable;
-    int ref_count;
+    SV *sv;
+    PerlInterpreter *perl;
  } mpxs_pool_account_t;

+/* XXX: this implementation has a problem with perl ithreads. if a
+ * custom pool is allocated, and then a thread is spawned we now have
+ * two copies of the pool object, each living in a different perl
+ * interpreter, both pointing to the same memory address of the apr
+ * pool.
+ *
+ * need to write a CLONE class method could properly clone the
+ * thread's copied object, but it's tricky:
+ * - it needs to call parent_get() on the copied object and allocate a
+ *   new pool from that parent's pool
+ * - it needs to reinstall any registered cleanup callbacks (can we do
+ *   that?) may be we can skip those?
+ */
+
  /* XXX: should we make it a new global tracing category
   * MOD_PERL_TRACE=p for tracing pool management? */
-#define MP_POOL_TRACE_DO 0
+#define MP_POOL_TRACE_DO 1

  #if MP_POOL_TRACE_DO && defined(MP_TRACE)
  #define MP_POOL_TRACE modperl_trace
@@ -15,92 +29,34 @@
  #define MP_POOL_TRACE if (0) modperl_trace
  #endif

-
-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) {
-        /* if there is no data, there is nothing to decrement */
-        return 0;
-    }
-
-    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 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)
+/* invalidate all Perl objects referencing the data sv stored in the
+ * pool and the sv itself. this is needed when a parent pool triggers
+ * apr_pool_destroy on its child pools
+ */
+static MP_INLINE apr_status_t
+mpxs_apr_pool_cleanup(void *cleanup_data)
  {
      mpxs_pool_account_t *data;
-
-    apr_pool_userdata_get((void **)&data, MP_APR_POOL_NEW, p);
-    if (!data) {
+    apr_pool_userdata_get((void **)&data, MP_APR_POOL_NEW,
+                          (apr_pool_t *)cleanup_data);
+    if (!(data && data->sv)) {
          /* if there is no data, there is nothing to unset */
-        return;
+        MP_POOL_TRACE(MP_FUNC, "this pool seems to be destroyed already");
      }
-
-    data->destroyable = 0;
-
-    apr_pool_userdata_set(data, MP_APR_POOL_NEW, NULL, p);
-}
-
-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) {
-        /* pools with no special data weren't created by us and
-         * therefore shouldn't be destroyed */
-        return 0;
+    else {
+        dTHXa(data->perl);
+        MP_POOL_TRACE(MP_FUNC,
+                      "pool 0x%lx contains a valid sv 0x%lx, invalidating it",
+                      (unsigned long)data->sv, (unsigned long)cleanup_data);
+
+        /* invalidate all Perl objects referencing this sv */
+        SvIVX(data->sv) = 0;
+
+        /* invalidate the reference stored in the pool */
+        data->sv = NULL;
+        /* data->sv will go away by itself when all objects will go away */
      }

-    return data->destroyable && !data->ref_count;
-}
-
-static MP_INLINE apr_status_t
-mpxs_apr_pool_cleanup_destroyable_unset(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;
  }

@@ -109,13 +65,13 @@
   * @param  parent_pool_obj   an APR::Pool object or an "APR::Pool" class
   * @return                   a new pool or subpool
   */
-static MP_INLINE apr_pool_t *mpxs_apr_pool_create(pTHX_ SV *parent_pool_obj)
+static MP_INLINE SV *mpxs_apr_pool_create(pTHX_ SV *parent_pool_obj)
  {
      apr_pool_t *parent_pool = mpxs_sv_object_deref(parent_pool_obj, apr_pool_t);
      apr_pool_t *child_pool  = NULL;
-
+
+    MP_POOL_TRACE(MP_FUNC, "parent pool 0x%lx\n", (unsigned long)parent_pool);
      (void)apr_pool_create(&child_pool, parent_pool);
-    MP_POOL_TRACE(MP_FUNC, "new pool 0x%lx\n", child_pool);

  #if APR_POOL_DEBUG
      /* useful for pools debugging, can grep for APR::Pool::new */
@@ -131,12 +87,6 @@
                     (unsigned long)child_pool, (unsigned long)parent_pool);
      }

-    /* 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
@@ -146,10 +96,15 @@
       * 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
+     * their cleanup callbacks will destroy the guts of their perl
+     * objects, so when those perl objects, pointing to memory
+     * previously allocated by destroyed sub-pools or re-used already
+     * by new pools, will get their time to DESTROY, they won't make a
+     * mess, trying to destroy an already destroyed pool or even worse
+     * a pool allocate in the place of the old one.
       */
      apr_pool_cleanup_register(child_pool, (void *)child_pool,
-                              mpxs_apr_pool_cleanup_destroyable_unset,
+                              mpxs_apr_pool_cleanup,
                                apr_pool_cleanup_null);
  #if APR_POOL_DEBUG
      /* child <-> parent <-> ... <-> top ancestry traversal */
@@ -170,8 +125,23 @@
      }
  #endif

-    mpxs_apr_pool_ref_count_inc(child_pool);
-    return child_pool;
+    {
+        mpxs_pool_account_t *data =
+            (mpxs_pool_account_t *)apr_pcalloc(child_pool, sizeof(*data));
+
+        SV *rv = sv_setref_pv(NEWSV(0, 0), "APR::Pool", (void*)child_pool);
+
+        data->sv = SvRV(rv);
+#ifdef USE_ITHREADS
+        data->perl = aTHX;
+#endif
+        MP_POOL_TRACE(MP_FUNC, "sub-pool p: 0x%lx, sv: 0x%lx, rv: 0x%lx",
+                      (unsigned long)child_pool, data->sv, rv);
+
+        apr_pool_userdata_set(data, MP_APR_POOL_NEW, NULL, child_pool);
+
+        return rv;
+    }
  }

  typedef struct {
@@ -267,10 +237,11 @@
  }


-static MP_INLINE apr_pool_t *
+static MP_INLINE SV *
  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) {
          /* ideally this should be done by mp_xs_APR__Pool_2obj. Though
           * since most of the time we don't use custom pools, we don't
@@ -281,45 +252,69 @@
           * reference to a custom pool, they must do the ref-counting
           * as well.
           */
-        mpxs_apr_pool_ref_count_inc(parent_pool);
+        mpxs_pool_account_t *data;
+        apr_pool_userdata_get((void **)&data, MP_APR_POOL_NEW, parent_pool);
+        if (data && data->sv) {
+            MP_POOL_TRACE(MP_FUNC,
+                          "parent pool (0x%lx) is a custom pool, sv 0x%lx",
+                          (unsigned long)parent_pool,
+                          (unsigned long)data->sv);
+
+            return newRV_inc(data->sv);
+        }
+        else {
+            MP_POOL_TRACE(MP_FUNC, "parent pool (0x%lx) is a core pool",
+                          (unsigned long)parent_pool);
+            return SvREFCNT_inc(mp_xs_APR__Pool_2obj(parent_pool));
+        }
+    }
+    else {
+        MP_POOL_TRACE(MP_FUNC, "pool (0x%lx) has no parents",
+                      (unsigned long)child_pool);
+                      return SvREFCNT_inc(mp_xs_APR__Pool_2obj(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) {
-
+static MP_INLINE void mpxs_apr_pool_DESTROY(pTHX_ SV *obj)
+{
      apr_pool_t *p;
+    SV *sv = SvRV(obj);

-    p = mpxs_sv_object_deref(obj, apr_pool_t);
+    /* MP_POOL_TRACE(MP_FUNC, "DESTROY 0x%lx-0x%lx",       */
+    /*              (unsigned long)obj,(unsigned long)sv); */
+    /* do_sv_dump(0, Perl_debug_log, obj, 0, 4, FALSE, 0); */

-    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)
-     */
-    if (mpxs_apr_pool_is_pool_destroyable(p)) {
-        MP_POOL_TRACE(MP_FUNC, "DESTROY pool 0x%lx\n", (unsigned long)p);
-        apr_pool_destroy(p);
-        /* mpxs_apr_pool_cleanup_destroyable_unset called by
-         * apr_pool_destroy takes care of marking this pool as
-         * undestroyable, so we do it only once */
+    p = mpxs_sv_object_deref(obj, apr_pool_t);
+    if (!p) {
+        /* non-custom pool */
+        MP_POOL_TRACE(MP_FUNC, "skip apr_pool_destroy: not a custom pool");
+        return;
      }
-    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
-         */
-        MP_POOL_TRACE(MP_FUNC, "skipping DESTROY, "
-                  "this object is not eligible to destroy pool 0x%lx\n",
-                  (unsigned long)p);
-
+
+    if (sv && SvOK(sv)) {
+        mpxs_pool_account_t *data;
+
+        apr_pool_userdata_get((void **)&data, MP_APR_POOL_NEW, p);
+        if (!(data && data->sv)) {
+            MP_POOL_TRACE(MP_FUNC, "skip apr_pool_destroy: no sv found");
+            return;
+        }
+
+        if (SvREFCNT(sv) == 1) {
+            MP_POOL_TRACE(MP_FUNC, "call apr_pool_destroy: last reference");
+            apr_pool_destroy(p);
+        }
+        else {
+            /* when the pool object dies, sv's ref count decrements
+             * itself automatically */
+            MP_POOL_TRACE(MP_FUNC,
+                          "skip apr_pool_destroy: refcount > 1 (%d)",
+                          SvREFCNT(sv));
+        }
      }
  }
+
Index: xs/maps/apr_functions.map
===================================================================
RCS file: /home/cvs/modperl-2.0/xs/maps/apr_functions.map,v
retrieving revision 1.65
diff -u -r1.65 apr_functions.map
--- xs/maps/apr_functions.map	18 Dec 2003 18:53:50 -0000	1.65
+++ xs/maps/apr_functions.map	24 Jan 2004 11:21:55 -0000
@@ -154,7 +154,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 *:parent_pool_obj
+ SV *:DEFINE_new | mpxs_apr_pool_create | SV *:parent_pool_obj
  -apr_pool_create_ex
  >apr_pool_create_ex_debug
  !apr_pool_userdata_get
@@ -172,7 +172,7 @@
  -apr_pmemdup
  !apr_pool_child_cleanup_set
  !apr_pool_abort_get
- apr_pool_parent_get | mpxs_
+ SV *: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.136
diff -u -r1.136 FunctionTable.pm
--- xs/tables/current/ModPerl/FunctionTable.pm	14 Jan 2004 21:27:41 -0000	1.136
+++ xs/tables/current/ModPerl/FunctionTable.pm	24 Jan 2004 11:21:55 -0000
@@ -6528,7 +6528,7 @@
      ]
    },
    {
-    'return_type' => 'apr_pool_t *',
+    'return_type' => 'SV *',
      'name' => 'mpxs_apr_pool_parent_get',
      'attr' => [
        'static',
@@ -6560,7 +6560,7 @@
      ]
    },
    {
-    'return_type' => 'apr_pool_t *',
+    'return_type' => 'SV *',
      'name' => 'mpxs_apr_pool_create',
      'attr' => [
        'static',


__________________________________________________________________
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


Re: [mp2] APR::Pool re-implementation plus tests

Posted by Stas Bekman <st...@stason.org>.
As I had no feedback for a week it's now committed.

__________________________________________________________________
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