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();
}