You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by st...@apache.org on 2013/09/28 03:34:14 UTC

svn commit: r1527140 - in /subversion/trunk: build.conf subversion/include/private/svn_subr_private.h subversion/libsvn_subr/root_pools.c subversion/tests/libsvn_subr/root-pools-test.c

Author: stefan2
Date: Sat Sep 28 01:34:13 2013
New Revision: 1527140

URL: http://svn.apache.org/r1527140
Log:
Introduce a small new private API that allows for recycling root pools
as piloted by svnserve in r1523465.  Provide some basic testing as well.

* subversion/include/private/svn_subr_private.h
  (svn_root_pools__t): declare new API data types
  (svn_root_pools__create,
   svn_root_pools__acquire_pool,
   svn_root_pools__release_pool): declare new API functions

* subversion/libsvn_subr/root_pools.c
  (): new file
  (svn_root_pools__t): define new API data type
  (acquire_pool_internal): new utility function
  (svn_root_pools__create,
   svn_root_pools__acquire_pool,
   svn_root_pools__release_pool): implement new API functions

* subversion/tests/libsvn_subr/root-pools-test.c
  (): tests for the new API

* build.conf
  (root-pools-test): new build target
  (__ALL_TESTS__): add new test as dependency

Added:
    subversion/trunk/subversion/libsvn_subr/root_pools.c
    subversion/trunk/subversion/tests/libsvn_subr/root-pools-test.c
Modified:
    subversion/trunk/build.conf
    subversion/trunk/subversion/include/private/svn_subr_private.h

Modified: subversion/trunk/build.conf
URL: http://svn.apache.org/viewvc/subversion/trunk/build.conf?rev=1527140&r1=1527139&r2=1527140&view=diff
==============================================================================
--- subversion/trunk/build.conf (original)
+++ subversion/trunk/build.conf Sat Sep 28 01:34:13 2013
@@ -1008,6 +1008,14 @@ sources = revision-test.c
 install = test
 libs = libsvn_test libsvn_subr apr
 
+[root-pools-test]
+description = Test time functions
+type = exe
+path = subversion/tests/libsvn_subr
+sources = root-pools-test.c
+install = test
+libs = libsvn_test libsvn_subr apriconv apr
+
 [skel-test]
 description = Test skels in libsvn_subr
 type = exe
@@ -1390,7 +1398,8 @@ libs = __ALL__
        skel-test strings-reps-test changes-test locks-test repos-test
        checksum-test compat-test config-test hashdump-test mergeinfo-test
        opt-test packed-data-test path-test prefix-string-test
-       priority-queue-test stream-test string-test time-test utf-test
+       priority-queue-test root-pools-test stream-test
+       string-test time-test utf-test
        error-test error-code-test cache-test spillbuf-test crypto-test
        named_atomic-test named_atomic-proc-test revision-test
        subst_translate-test io-test

Modified: subversion/trunk/subversion/include/private/svn_subr_private.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/include/private/svn_subr_private.h?rev=1527140&r1=1527139&r2=1527140&view=diff
==============================================================================
--- subversion/trunk/subversion/include/private/svn_subr_private.h (original)
+++ subversion/trunk/subversion/include/private/svn_subr_private.h Sat Sep 28 01:34:13 2013
@@ -429,6 +429,40 @@ svn__decompress(svn_stringbuf_t *in,
 
 /** @} */
 
+/**
+ * @defgroup svn_root_pools Recycle-able root pools API
+ * @{
+ */
+
+/* Opaque thread-safe container for unused / recylcleable root pools.
+ *
+ * Recyling root pools (actually, their allocators) circumvents a
+ * scalability bottleneck in the OS memory management when multi-threaded
+ * applications frequently create and destroy allocators.
+ */
+typedef struct svn_root_pools__t svn_root_pools__t;
+
+/* Create a new root pools container and return it in *POOLS.
+ */
+svn_error_t *
+svn_root_pools__create(svn_root_pools__t **pools);
+
+/* Return a currently unused pool from POOLS.  If POOLS is empty, create a
+ * new root pool and return that.  The pool returned is not thread-safe.
+ */
+apr_pool_t *
+svn_root_pools__acquire_pool(svn_root_pools__t *pools);
+
+/* Clear and release the given root POOL and put it back into POOLS.
+ * If that fails, destroy POOL.
+ */
+void
+svn_root_pools__release_pool(apr_pool_t *pool,
+                             svn_root_pools__t *pools);
+
+/** @} */
+
+
 
 #ifdef __cplusplus
 }

Added: subversion/trunk/subversion/libsvn_subr/root_pools.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_subr/root_pools.c?rev=1527140&view=auto
==============================================================================
--- subversion/trunk/subversion/libsvn_subr/root_pools.c (added)
+++ subversion/trunk/subversion/libsvn_subr/root_pools.c Sat Sep 28 01:34:13 2013
@@ -0,0 +1,110 @@
+/*
+ * root_pools.c :  Implement svn_root_pools__* API
+ *
+ * ====================================================================
+ *    Licensed to the Apache Software Foundation (ASF) under one
+ *    or more contributor license agreements.  See the NOTICE file
+ *    distributed with this work for additional information
+ *    regarding copyright ownership.  The ASF licenses this file
+ *    to you under the Apache License, Version 2.0 (the
+ *    "License"); you may not use this file except in compliance
+ *    with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing,
+ *    software distributed under the License is distributed on an
+ *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *    KIND, either express or implied.  See the License for the
+ *    specific language governing permissions and limitations
+ *    under the License.
+ * ====================================================================
+ */
+
+#include "svn_pools.h"
+
+#include "private/svn_subr_private.h"
+#include "private/svn_mutex.h"
+
+typedef struct svn_root_pools__t
+{
+  /* unused pools.
+   * Use MUTEX to serialize access to this collection.
+   */
+  apr_array_header_t *unused_pools;
+
+  /* Mutex to serialize access to UNUSED_POOLS */
+  svn_mutex__t *mutex;
+
+} svn_root_pools__t;
+
+svn_error_t *
+svn_root_pools__create(svn_root_pools__t **pools)
+{
+  /* the collection of root pools must be managed independently from
+     any other pool */
+  apr_pool_t *pool
+    = apr_allocator_owner_get(svn_pool_create_allocator(FALSE));
+
+  /* construct result object */
+  svn_root_pools__t *result = apr_pcalloc(pool, sizeof(*result));
+  SVN_ERR(svn_mutex__init(&result->mutex, TRUE, pool));
+  result->unused_pools = apr_array_make(pool, 16, sizeof(apr_pool_t *));
+
+  /* done */
+  *pools = result;
+
+  return SVN_NO_ERROR;
+}
+
+/* Return a currently unused connection pool in *POOL. If no such pool
+ * exists, create a new root pool and return that in *POOL.
+ */
+static svn_error_t *
+acquire_pool_internal(apr_pool_t **pool,
+                      svn_root_pools__t *pools)
+{
+  SVN_ERR(svn_mutex__lock(pools->mutex));
+  *pool = pools->unused_pools->nelts
+        ? *(apr_pool_t **)apr_array_pop(pools->unused_pools)
+        : apr_allocator_owner_get(svn_pool_create_allocator(FALSE));
+  SVN_ERR(svn_mutex__unlock(pools->mutex, SVN_NO_ERROR));
+
+  return SVN_NO_ERROR;
+}
+
+apr_pool_t *
+svn_root_pools__acquire_pool(svn_root_pools__t *pools)
+{
+  apr_pool_t *pool;
+  svn_error_t *err = acquire_pool_internal(&pool, pools);
+  if (err)
+    {
+      /* Mutex failure?!  Well, try to continue with unrecycled data. */
+      svn_error_clear(err);
+      pool = apr_allocator_owner_get(svn_pool_create_allocator(FALSE));
+    }
+
+  return pool;
+}
+
+void
+svn_root_pools__release_pool(apr_pool_t *pool,
+                             svn_root_pools__t *pools)
+{
+  svn_error_t *err;
+
+  svn_pool_clear(pool);
+
+  err = svn_mutex__lock(pools->mutex);
+  if (err)
+    {
+      svn_error_clear(err);
+      svn_pool_destroy(pool);
+    }
+  else
+    {
+      APR_ARRAY_PUSH(pools->unused_pools, apr_pool_t *) = pool;
+      svn_error_clear(svn_mutex__unlock(pools->mutex, SVN_NO_ERROR));
+    }
+}

Added: subversion/trunk/subversion/tests/libsvn_subr/root-pools-test.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/libsvn_subr/root-pools-test.c?rev=1527140&view=auto
==============================================================================
--- subversion/trunk/subversion/tests/libsvn_subr/root-pools-test.c (added)
+++ subversion/trunk/subversion/tests/libsvn_subr/root-pools-test.c Sat Sep 28 01:34:13 2013
@@ -0,0 +1,130 @@
+/*
+ * root-pools-test.c -- test the svn_root_pools__* API
+ *
+ * ====================================================================
+ *    Licensed to the Apache Software Foundation (ASF) under one
+ *    or more contributor license agreements.  See the NOTICE file
+ *    distributed with this work for additional information
+ *    regarding copyright ownership.  The ASF licenses this file
+ *    to you under the Apache License, Version 2.0 (the
+ *    "License"); you may not use this file except in compliance
+ *    with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing,
+ *    software distributed under the License is distributed on an
+ *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *    KIND, either express or implied.  See the License for the
+ *    specific language governing permissions and limitations
+ *    under the License.
+ * ====================================================================
+ */
+
+#include <apr_pools.h>
+#include <apr_thread_proc.h>
+#include <apr_thread_cond.h>
+
+#include "private/svn_atomic.h"
+#include "private/svn_subr_private.h"
+
+#include "../svn_test.h"
+
+/* do a few allocations of various sizes from POOL */
+static void
+do_some_allocations(apr_pool_t *pool)
+{
+  int i;
+  apr_size_t fib = 1, fib1 = 0, fib2 = 0;
+  for (i = 0; i < 25; ++i)      /* fib(25) = 75025 */
+    {
+      apr_pcalloc(pool, fib1);
+      fib2 = fib1;
+      fib1 = fib;
+      fib += fib2;
+    }
+}
+
+/* allocate, use and recycle a pool from POOLs a few times */
+static void
+use_root_pool(svn_root_pools__t *pools)
+{
+  int i;
+  for (i = 0; i < 1000; ++i)
+    {
+      apr_pool_t *pool = svn_root_pools__acquire_pool(pools);
+      do_some_allocations(pool);
+      svn_root_pools__release_pool(pool, pools);
+    }
+}
+
+static void *
+APR_THREAD_FUNC thread_func(apr_thread_t *tid, void *data)
+{
+  /* give all threads a good chance to get started by the scheduler */
+  apr_thread_yield();
+
+  use_root_pool(data);
+
+  return NULL;
+}
+
+static svn_error_t *
+test_root_pool(apr_pool_t *pool)
+{
+  svn_root_pools__t *pools;
+  SVN_ERR(svn_root_pools__create(&pools));
+  use_root_pool(pools);
+
+  return SVN_NO_ERROR;
+}
+
+#define APR_ERR(expr)                           \
+  do {                                          \
+    apr_status_t status = (expr);               \
+    if (status)                                 \
+      return svn_error_wrap_apr(status, NULL);  \
+  } while (0)
+
+static svn_error_t *
+test_root_pool_concurrency(apr_pool_t *pool)
+{
+#if APR_HAS_THREADS
+  /* The svn_root_pools__t container is supposed to be thread-safe.
+     Do some multi-threaded access and hope that there are no segfaults.
+   */
+  enum { THREAD_COUNT = 10 };
+  svn_root_pools__t *pools;
+  apr_thread_t *threads[THREAD_COUNT];
+  int i;
+
+  SVN_ERR(svn_root_pools__create(&pools));
+
+  for (i = 0; i < THREAD_COUNT; ++i)
+    APR_ERR(apr_thread_create(&threads[i], NULL, thread_func, pools, pool));
+
+  /* wait for the threads to finish */
+  for (i = 0; i < THREAD_COUNT; ++i)
+    {
+      apr_status_t retval;
+      APR_ERR(apr_thread_join(&retval, threads[i]));
+      APR_ERR(retval);
+    }
+#endif
+
+  return SVN_NO_ERROR;
+}
+
+
+/* The test table.  */
+
+struct svn_test_descriptor_t test_funcs[] =
+  {
+    SVN_TEST_NULL,
+    SVN_TEST_PASS2(test_root_pool,
+                   "test root pool recycling"),
+    SVN_TEST_SKIP2(test_root_pool_concurrency,
+                   ! APR_HAS_THREADS,
+                   "test concurrent root pool recycling"),
+    SVN_TEST_NULL
+  };