Author: brane
Date: Tue Jan  8 14:29:31 2019
New Revision: 1850753

URL: http://svn.apache.org/viewvc?rev=1850753&view=rev
Log:
Keep the SVN++ global pool alive as long as there are any users.

[in subversion/bindings/cxx]
* include/svnxx/init.hpp
  (detail::global_state): Renamed from detail::context.
  (init): Derive directly from the global_state shared pointer.
  (init::~init): Add explicit destructor (for debugging).

* src/private/init_private.hpp: Do not include svn_private_config.h.
  (detail::global_state): Renamed from detail::context.
  (detail::global_state::get): Don't try to translate the exception
   message, because neither APR nor the localisation library are
   likely to have been initialized.

* src/aprwrap/pool.hpp: Include init_private.hpp.
  (apr::pool::pool): Add a constructor overload that uses the global
   pool from a known global state reference.
* src/aprwrap/impl.cpp
  (apr::pool::get_root_pool): Rename detail::context to detail::global_state.

* src/debug.cpp,
  src/private/debug_private.hpp: New files.

* src/private/client_context_private.hpp: Include init_private.hpp.
  (client::context::state): New member, keeps the global state alive as
   long as the client context is viable.
  (client::context::get_state): New accessor.
  (client::context::get_ctx): Renamed from 'get'.
  (impl::unwrap): Make the context_wrapper a final class.

* src/init.cpp:
   Include debug_private.hpp and apr_pools.h instead of svn_pools.h.
  (init::init): Use global_state and add pool debugging logs.
  (init::~init): Implement, with pool debugging logs.
  (notify_root_pool_cleanup): New; tracks the global pool's destruction.
  (create_root_pool): Add pool debugging logs. Avoid leaking the root pool.
  (detail::global_state): Add pool debugging logs.

* src/client_status.cpp
  (client::status): use the client context's renamed get_ctx() accessor.

* tests/test_init.cpp: Rename detail::context to detail::global_state.

Added:
    subversion/trunk/subversion/bindings/cxx/src/debug.cpp   (with props)
    subversion/trunk/subversion/bindings/cxx/src/private/debug_private.hpp   
(with props)
Modified:
    subversion/trunk/subversion/bindings/cxx/include/svnxx/init.hpp
    subversion/trunk/subversion/bindings/cxx/src/aprwrap/impl.cpp
    subversion/trunk/subversion/bindings/cxx/src/aprwrap/pool.hpp
    subversion/trunk/subversion/bindings/cxx/src/client_status.cpp
    subversion/trunk/subversion/bindings/cxx/src/init.cpp
    
subversion/trunk/subversion/bindings/cxx/src/private/client_context_private.hpp
    subversion/trunk/subversion/bindings/cxx/src/private/init_private.hpp
    subversion/trunk/subversion/bindings/cxx/tests/test_init.cpp

Modified: subversion/trunk/subversion/bindings/cxx/include/svnxx/init.hpp
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/cxx/include/svnxx/init.hpp?rev=1850753&r1=1850752&r2=1850753&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/cxx/include/svnxx/init.hpp (original)
+++ subversion/trunk/subversion/bindings/cxx/include/svnxx/init.hpp Tue Jan  8 
14:29:31 2019
@@ -33,7 +33,7 @@ namespace svnxx {
 
 namespace detail {
 // Forward declaration of the private API context.
-class context;
+class global_state;
 } // namespace detail
 
 /**
@@ -44,13 +44,13 @@ class context;
  * create an @c init object before you can use the SVN++ API. It is
  * safe to create create any number of these objects.
  */
-class init
+class init : private std::shared_ptr<detail::global_state>
 {
+  using state = std::shared_ptr<detail::global_state>;
+
 public:
   init();
-
-private:
-  std::shared_ptr<detail::context> context;
+  ~init() noexcept;
 };
 
 } // namespace svnxx

Modified: subversion/trunk/subversion/bindings/cxx/src/aprwrap/impl.cpp
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/cxx/src/aprwrap/impl.cpp?rev=1850753&r1=1850752&r2=1850753&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/cxx/src/aprwrap/impl.cpp (original)
+++ subversion/trunk/subversion/bindings/cxx/src/aprwrap/impl.cpp Tue Jan  8 
14:29:31 2019
@@ -37,8 +37,8 @@ namespace apr {
 
 apr_pool_t* pool::get_root_pool()
 {
-  auto ctx = detail::context::get();
-  return ctx->get_root_pool();
+  auto state = detail::global_state::get();
+  return state->get_root_pool();
 }
 
 //

Modified: subversion/trunk/subversion/bindings/cxx/src/aprwrap/pool.hpp
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/cxx/src/aprwrap/pool.hpp?rev=1850753&r1=1850752&r2=1850753&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/cxx/src/aprwrap/pool.hpp (original)
+++ subversion/trunk/subversion/bindings/cxx/src/aprwrap/pool.hpp Tue Jan  8 
14:29:31 2019
@@ -31,6 +31,8 @@
 #include "svnxx/exception.hpp"
 #include "svnxx/detail/noncopyable.hpp"
 
+#include "../private/init_private.hpp"
+
 #include "svn_pools.h"
 
 namespace apache {
@@ -101,6 +103,13 @@ public:
     {}
 
   /**
+   * Create a pool as a child of the global pool in @a state.
+   */
+  explicit pool(const detail::global_state::ptr& state)
+    : pool_ptr(svn_pool_create(state->get_root_pool()))
+    {}
+
+  /**
    * Clear the pool.
    */
   void clear()

Modified: subversion/trunk/subversion/bindings/cxx/src/client_status.cpp
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/cxx/src/client_status.cpp?rev=1850753&r1=1850752&r2=1850753&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/cxx/src/client_status.cpp (original)
+++ subversion/trunk/subversion/bindings/cxx/src/client_status.cpp Tue Jan  8 
14:29:31 2019
@@ -68,7 +68,7 @@ status(context& ctx_, const char* path,
   svn_revnum_t result;
 
   impl::checked_call(
-      svn_client_status6(&result, ctx.get(), path, &rev,
+      svn_client_status6(&result, ctx.get_ctx(), path, &rev,
                          impl::convert(depth),
                          bool(flags & status_flags::get_all),
                          bool(flags & status_flags::check_out_of_date),

Added: subversion/trunk/subversion/bindings/cxx/src/debug.cpp
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/cxx/src/debug.cpp?rev=1850753&view=auto
==============================================================================
--- subversion/trunk/subversion/bindings/cxx/src/debug.cpp (added)
+++ subversion/trunk/subversion/bindings/cxx/src/debug.cpp Tue Jan  8 14:29:31 
2019
@@ -0,0 +1,37 @@
+/**
+ * @copyright
+ * ====================================================================
+ *    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.
+ * ====================================================================
+ * @endcopyright
+ */
+
+#include "private/debug_private.hpp"
+
+namespace apache {
+namespace subversion {
+namespace svnxx {
+namespace impl {
+
+const char root_pool_tag[] = "svn++ root pool";
+const char root_pool_key[] = "SVN++ 30F4B67A-CAB4-1337-F00D-DEADBEEFA78F";
+
+} // namespace impl
+} // namespace svnxx
+} // namespace subversion
+} // namespace apache

Propchange: subversion/trunk/subversion/bindings/cxx/src/debug.cpp
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: subversion/trunk/subversion/bindings/cxx/src/init.cpp
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/cxx/src/init.cpp?rev=1850753&r1=1850752&r2=1850753&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/cxx/src/init.cpp (original)
+++ subversion/trunk/subversion/bindings/cxx/src/init.cpp Tue Jan  8 14:29:31 
2019
@@ -24,18 +24,30 @@
 #include <sstream>
 
 #include "svnxx/exception.hpp"
+#include "private/debug_private.hpp"
 #include "private/init_private.hpp"
 
 #include <apr_general.h>
-#include "svn_pools.h"
+#include <apr_pools.h>
 
 namespace apache {
 namespace subversion {
 namespace svnxx {
 
 init::init()
-  : context(detail::context::create())
-{}
+  : state(detail::global_state::create())
+{
+#ifdef SVNXX_POOL_DEBUG
+  SVN_DBG(("svn++ created init object   %pp", static_cast<void*>(this)));
+#endif
+}
+
+init::~init() noexcept
+{
+#ifdef SVNXX_POOL_DEBUG
+  SVN_DBG(("svn++ destroyed init object %pp", static_cast<void*>(this)));
+#endif
+}
 
 namespace detail {
 
@@ -52,6 +64,15 @@ int handle_failed_allocation(int)
   throw allocation_failed_builder("svn::allocation_failed");
 }
 
+#ifdef SVNXX_POOL_DEBUG
+apr_status_t notify_root_pool_cleanup(void* key)
+{
+  if (key == impl::root_pool_key)
+    SVN_DBG(("svn++ destroyed root pool"));
+  return APR_SUCCESS;
+}
+#endif
+
 apr_pool_t* create_root_pool()
 {
   // Create the root pool's allocator.
@@ -68,42 +89,56 @@ apr_pool_t* create_root_pool()
     throw allocation_failed_builder("svn++ creating root pool");
 
 #if APR_POOL_DEBUG
-  apr_pool_tag(root_pool, "svn++ root pool");
+  apr_pool_tag(root_pool, impl::root_pool_tag);
 #endif
 
 #if APR_HAS_THREADS
   // SVN++ pools are always as thread safe as APR can make them.
   apr_thread_mutex_t *mutex = nullptr;
   status = apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_DEFAULT, 
root_pool);
-  if (status || !mutex)
-    throw allocation_failed_builder("svn++ creating allocator mutex");
-  apr_allocator_mutex_set(allocator, mutex);
+  if (mutex && !status)
+    apr_allocator_mutex_set(allocator, mutex);
+  else
+    {
+#ifdef SVNXX_POOL_DEBUG
+      SVN_DBG(("svn++ could not create allocator mutex, apr_err=%d", status));
+#endif
+      apr_pool_destroy(root_pool); // Don't leak the global pool.
+      throw allocation_failed_builder("svn++ creating allocator mutex");
+    }
+#endif
+
+#ifdef SVNXX_POOL_DEBUG
+  apr_pool_cleanup_register(root_pool, impl::root_pool_key,
+                            notify_root_pool_cleanup,
+                            apr_pool_cleanup_null);
+  SVN_DBG(("svn++ created root pool"));
 #endif
 
   return root_pool;
 }
 } // anonymous namespace
 
-std::mutex context::guard;
-context::weak_ptr context::self;
+std::mutex global_state::guard;
+global_state::weak_ptr global_state::self;
 
-context::ptr context::create()
+global_state::ptr global_state::create()
 {
   std::unique_lock<std::mutex> lock(guard);
-  auto ctx = self.lock();
-  if (!ctx)
+  auto state = self.lock();
+  if (!state)
     {
       // Work around the private constructor: since this struct is
       // defined within a class member, the the private constructor
       // is accessible and std::make_shared won't complain about it.
-      struct make_shared_hack : public context {};
-      ctx = std::make_shared<make_shared_hack>();
-      self = ctx;
+      struct global_state_builder final : public global_state {};
+      state = std::make_shared<global_state_builder>();
+      self = state;
     }
-  return ctx;
+  return state;
 }
 
-context::context()
+global_state::global_state()
 {
   const auto status = apr_initialize();
   if (status)
@@ -114,11 +149,18 @@ context::context()
               << apr_strerror(status, errbuf, sizeof(errbuf) - 1);
       throw std::runtime_error(message.str());
     }
+
   root_pool = create_root_pool();
+#ifdef SVNXX_POOL_DEBUG
+  SVN_DBG(("svn++ created global state"));
+#endif
 }
 
-context::~context()
+global_state::~global_state()
 {
+#ifdef SVNXX_POOL_DEBUG
+  SVN_DBG(("svn++ destroyed global state"));
+#endif
   std::unique_lock<std::mutex> lock(guard);
   apr_terminate();
   root_pool = nullptr;

Modified: 
subversion/trunk/subversion/bindings/cxx/src/private/client_context_private.hpp
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/cxx/src/private/client_context_private.hpp?rev=1850753&r1=1850752&r2=1850753&view=diff
==============================================================================
--- 
subversion/trunk/subversion/bindings/cxx/src/private/client_context_private.hpp 
(original)
+++ 
subversion/trunk/subversion/bindings/cxx/src/private/client_context_private.hpp 
Tue Jan  8 14:29:31 2019
@@ -26,6 +26,7 @@
 
 #include "svnxx/client/context.hpp"
 
+#include "../private/init_private.hpp"
 #include "../aprwrap.hpp"
 
 #include "svn_client.h"
@@ -39,15 +40,21 @@ namespace detail {
 // TODO: document this
 class context
 {
+  using global_state = svnxx::detail::global_state;
+
 public:
   context()
-    : ctx(create_ctx(ctx_pool))
+    : state(global_state::get()),
+      ctx_pool(state),
+      ctx(create_ctx(ctx_pool))
     {}
 
-  svn_client_ctx_t* get() const noexcept { return ctx; };
+  const global_state::ptr& get_state() const noexcept { return state; }
   const apr::pool& get_pool() const noexcept { return ctx_pool; }
+  svn_client_ctx_t* get_ctx() const noexcept { return ctx; };
 
 private:
+  const global_state::ptr state;
   apr::pool ctx_pool;
   svn_client_ctx_t* const ctx;
 
@@ -61,7 +68,7 @@ namespace impl {
 // TODO: document this
 inline client::detail::context& unwrap(client::context& ctx)
 {
-  struct context_wrapper : public client::context
+  struct context_wrapper final : public client::context
   {
     using inherited::get;
   };

Added: subversion/trunk/subversion/bindings/cxx/src/private/debug_private.hpp
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/cxx/src/private/debug_private.hpp?rev=1850753&view=auto
==============================================================================
--- subversion/trunk/subversion/bindings/cxx/src/private/debug_private.hpp 
(added)
+++ subversion/trunk/subversion/bindings/cxx/src/private/debug_private.hpp Tue 
Jan  8 14:29:31 2019
@@ -0,0 +1,49 @@
+/**
+ * @copyright
+ * ====================================================================
+ *    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.
+ * ====================================================================
+ * @endcopyright
+ */
+
+#ifndef SVNXX_PRIVATE_DEBUG_HPP
+#define SVNXX_PRIVATE_DEBUG_HPP
+
+// We can only write pool debug logs with SVN_DEBUG enabled.
+#if defined(SVNXX_POOL_DEBUG) && !defined(SVN_DEBUG)
+#  undef SVNXX_POOL_DEBUG
+#endif
+
+#ifdef SVNXX_POOL_DEBUG
+#include "private/svn_debug.h"
+#endif
+
+namespace apache {
+namespace subversion {
+namespace svnxx {
+namespace impl {
+
+extern const char root_pool_tag[];
+extern const char root_pool_key[];
+
+} // namespace impl
+} // namespace svnxx
+} // namespace subversion
+} // namespace apache
+
+#endif // SVNXX_PRIVATE_DEBUG_HPP

Propchange: 
subversion/trunk/subversion/bindings/cxx/src/private/debug_private.hpp
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: subversion/trunk/subversion/bindings/cxx/src/private/init_private.hpp
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/cxx/src/private/init_private.hpp?rev=1850753&r1=1850752&r2=1850753&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/cxx/src/private/init_private.hpp 
(original)
+++ subversion/trunk/subversion/bindings/cxx/src/private/init_private.hpp Tue 
Jan  8 14:29:31 2019
@@ -33,33 +33,31 @@
 #include "svnxx/init.hpp"
 #include "svnxx/detail/noncopyable.hpp"
 
-#include "svn_private_config.h"
-
 namespace apache {
 namespace subversion {
 namespace svnxx {
 namespace detail {
 
-class context : noncopyable
+class global_state : noncopyable
 {
 public:
-  using ptr = std::shared_ptr<context>;
-  using weak_ptr = std::weak_ptr<context>;
+  using ptr = std::shared_ptr<global_state>;
+  using weak_ptr = std::weak_ptr<global_state>;
 
-  ~context();
+  ~global_state();
 
   static ptr create();
   static ptr get()
     {
-      auto ctx = self.lock();
-      if (!ctx)
+      auto state = self.lock();
+      if (!state)
         {
           throw std::logic_error(
-              _("The SVN++ library is not initialized."
-                " Did you forget to create an instance of "
-                " the apache::subversion::svnxx::init class?"));
+              "The SVN++ library is not initialized."
+              " Did you forget to create an instance of "
+              " the apache::subversion::svnxx::init class?");
         }
-      return ctx;
+      return state;
     }
 
   apr_pool_t* get_root_pool() const noexcept
@@ -68,8 +66,8 @@ public:
     }
 
 private:
-  // Thou shalt not create contexts other than through the factory.
-  context();
+  // Thou shalt not create global_states other than through the factory.
+  global_state();
 
   apr_pool_t* root_pool{nullptr};
 

Modified: subversion/trunk/subversion/bindings/cxx/tests/test_init.cpp
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/cxx/tests/test_init.cpp?rev=1850753&r1=1850752&r2=1850753&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/cxx/tests/test_init.cpp (original)
+++ subversion/trunk/subversion/bindings/cxx/tests/test_init.cpp Tue Jan  8 
14:29:31 2019
@@ -27,38 +27,38 @@ namespace detail = ::apache::subversion:
 
 BOOST_AUTO_TEST_SUITE(init);
 
-BOOST_AUTO_TEST_CASE(context_with_init)
+BOOST_AUTO_TEST_CASE(state_with_init)
 {
   svn::init svnxx_initialized;
-  BOOST_TEST(detail::context::get());
+  BOOST_TEST(detail::global_state::get());
 }
 
-BOOST_AUTO_TEST_CASE(context_without_init)
+BOOST_AUTO_TEST_CASE(state_without_init)
 {
-  BOOST_CHECK_THROW(detail::context::get(), std::logic_error);
+  BOOST_CHECK_THROW(detail::global_state::get(), std::logic_error);
 }
 
 BOOST_AUTO_TEST_CASE(init_scope)
 {
   {
     svn::init svnxx_initialized;
-    BOOST_TEST(detail::context::get());
+    BOOST_TEST(detail::global_state::get());
   }
-  BOOST_CHECK_THROW(detail::context::get(), std::logic_error);
+  BOOST_CHECK_THROW(detail::global_state::get(), std::logic_error);
 }
 
-BOOST_AUTO_TEST_CASE(multi_init_same_context)
+BOOST_AUTO_TEST_CASE(multi_init_same_state)
 {
   svn::init svnxx_initialized_first;
-  const auto ctx = detail::context::get();
-  BOOST_REQUIRE(ctx);
+  const auto state = detail::global_state::get();
+  BOOST_REQUIRE(state);
 
   {
     svn::init svnxx_initialized_second;
-    BOOST_TEST(ctx == detail::context::get());
+    BOOST_TEST(state == detail::global_state::get());
   }
 
-  BOOST_TEST(ctx == detail::context::get());
+  BOOST_TEST(state == detail::global_state::get());
 }
 
 BOOST_AUTO_TEST_SUITE_END();


Reply via email to