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

svn commit: r1519803 - in /subversion/trunk/subversion/bindings/javahl: native/JNIUtil.cpp native/org_apache_subversion_javahl_NativeResources.cpp src/org/apache/subversion/javahl/NativeResources.java

Author: brane
Date: Tue Sep  3 19:26:02 2013
New Revision: 1519803

URL: http://svn.apache.org/r1519803
Log:
Serialize intialization of the JavaHL native library, otherwise there's
too much room for race conditions, especially in multithreaded clients.

* subversion/bindings/javahl/native/JNIUtil.cpp
  (GlobalInitGuard): New class, effectively a gate that controls acces
   to the initialization code.
  (JNIUtil::JNIGlobalInit): Use GlobalInitGuard.

* subversion/bindings/javahl/native/org_apache_subversion_javahl_NativeResources.cpp
  (Java_org_apache_subversion_javahl_NativeResources_initNativeLibrary): Return
   the result of JNIUtil::JNIGlobalInit.

* subversion/bindings/javahl/src/org/apache/subversion/javahl/NativeResources.java
  (NativeResources.initNativeLibrary): Change signature to return a boolean
   success flag.
  (NativeResources.init): Raise an exception if initializing the native library
   does not succeed.

Modified:
    subversion/trunk/subversion/bindings/javahl/native/JNIUtil.cpp
    subversion/trunk/subversion/bindings/javahl/native/org_apache_subversion_javahl_NativeResources.cpp
    subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/NativeResources.java

Modified: subversion/trunk/subversion/bindings/javahl/native/JNIUtil.cpp
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/native/JNIUtil.cpp?rev=1519803&r1=1519802&r2=1519803&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/native/JNIUtil.cpp (original)
+++ subversion/trunk/subversion/bindings/javahl/native/JNIUtil.cpp Tue Sep  3 19:26:02 2013
@@ -106,6 +106,93 @@ bool JNIUtil::JNIInit(JNIEnv *env)
   return true;
 }
 
+namespace
+{
+struct GlobalInitGuard
+{
+  enum InitState
+    {
+      state_null,
+      state_init,
+      state_done,
+      state_error
+    };
+
+  GlobalInitGuard()
+    : m_finished(false),
+      m_state(InitState(svn_atomic_cas(&m_global_state,
+                                       state_init, state_null)))
+    {
+      switch (m_state)
+        {
+        case state_null:
+          // This thread won the initialization contest.
+          break;
+
+        case state_done:
+          // The library is already initialized.
+          break;
+
+        case state_init:
+          // Another thread is currently initializing the
+          // library. Spin and wait for it to finish, with exponential
+          // backoff, but no longer than half a second.
+          for (unsigned shift = 0;
+               m_state == state_init && shift < 8;
+               ++shift)
+            {
+              apr_sleep((APR_USEC_PER_SEC / 1000) << shift);
+              m_state = InitState(svn_atomic_cas(&m_global_state,
+                                                 state_null, state_null));
+            }
+          if (m_state == state_init)
+            // The initialization didn't complete in half a second,
+            // which probably implies a thread crash or a deadlock.
+            m_state = state_error;
+          break;
+
+        default:
+          // Error state, or unknown state. In any case, do not continue.
+          m_state = state_error;
+        }
+    }
+
+  ~GlobalInitGuard()
+    {
+      // Signal the end of the library intialization if we're the
+      // initializing thread.
+      if (m_finished && m_state == state_null)
+        {
+          SVN_ERR_ASSERT_NO_RETURN(
+              state_init == svn_atomic_cas(&m_global_state,
+                                           state_done, state_init));
+        }
+    }
+
+  bool done() const
+    {
+      return (m_state == state_done);
+    }
+
+  bool error() const
+    {
+      return (m_state == state_error);
+    }
+
+  void finish()
+    {
+      m_finished = true;
+    }
+
+private:
+  bool m_finished;
+  InitState m_state;
+  static volatile svn_atomic_t m_global_state;
+};
+volatile svn_atomic_t
+GlobalInitGuard::m_global_state = GlobalInitGuard::state_null;
+} // anonymous namespace
+
 /**
  * Initialize the environment for all requests.
  * @param env   the JNI environment for this request
@@ -113,16 +200,10 @@ bool JNIUtil::JNIInit(JNIEnv *env)
 bool JNIUtil::JNIGlobalInit(JNIEnv *env)
 {
   // This method has to be run only once during the run a program.
-  static volatile svn_atomic_t once = 0;
-  svn_atomic_t done = svn_atomic_cas(&once, 1, 0);
-  if (done)
+  GlobalInitGuard guard;
+  if (guard.done())
     return true;
-
-  // Do not run this part more than one time.  This leaves a small
-  // time window when two threads create their first SVNClient and
-  // SVNAdmin at the same time, but I do not see a better option
-  // without APR already initialized
-  if (g_inInit)
+  else if (guard.error())
     return false;
 
   g_inInit = true;
@@ -282,6 +363,8 @@ bool JNIUtil::JNIGlobalInit(JNIEnv *env)
 
   g_initEnv = NULL;
   g_inInit = false;
+
+  guard.finish();
   return true;
 }
 

Modified: subversion/trunk/subversion/bindings/javahl/native/org_apache_subversion_javahl_NativeResources.cpp
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/native/org_apache_subversion_javahl_NativeResources.cpp?rev=1519803&r1=1519802&r2=1519803&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/native/org_apache_subversion_javahl_NativeResources.cpp (original)
+++ subversion/trunk/subversion/bindings/javahl/native/org_apache_subversion_javahl_NativeResources.cpp Tue Sep  3 19:26:02 2013
@@ -28,11 +28,11 @@
 #include "JNIUtil.h"
 #include "../include/org_apache_subversion_javahl_NativeResources.h"
 
-JNIEXPORT void JNICALL
+JNIEXPORT jboolean JNICALL
 Java_org_apache_subversion_javahl_NativeResources_initNativeLibrary
 (JNIEnv *env, jclass jclazz)
 {
   // No usual JNIEntry here, as the prerequisite native library
   // initialization is performed here.
-  JNIUtil::JNIGlobalInit(env);
+  return jboolean(JNIUtil::JNIGlobalInit(env));
 }

Modified: subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/NativeResources.java
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/NativeResources.java?rev=1519803&r1=1519802&r2=1519803&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/NativeResources.java (original)
+++ subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/NativeResources.java Tue Sep  3 19:26:02 2013
@@ -129,7 +129,9 @@ public class NativeResources
      */
     private static final void init()
     {
-        initNativeLibrary();
+        if (!initNativeLibrary())
+            throw new LinkageError("Native library initialization failed");
+
         version = new Version();
         if (!version.isAtLeast(1, 9, 0))
         {
@@ -141,5 +143,5 @@ public class NativeResources
     /**
      * Initialize the native library layer.
      */
-    private static native void initNativeLibrary();
+    private static native boolean initNativeLibrary();
 }