On the javahl-1.7-extensions branch: Synced JavaHL with trunk up to r1520245.

git-svn-id: https://svn.apache.org/repos/asf/subversion/branches/javahl-1.7-extensions@1520549 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/subversion/bindings/javahl/native/CreateJ.cpp b/subversion/bindings/javahl/native/CreateJ.cpp
index ee3bf5b..2539e65 100644
--- a/subversion/bindings/javahl/native/CreateJ.cpp
+++ b/subversion/bindings/javahl/native/CreateJ.cpp
@@ -835,7 +835,13 @@
   jlong jhunkModifiedStart = wcNotify->hunk_modified_start;
   jlong jhunkModifiedLength = wcNotify->hunk_modified_length;
   jlong jhunkMatchedLine = wcNotify->hunk_matched_line;
-  jint jhunkFuzz = wcNotify->hunk_fuzz;
+  jint jhunkFuzz = static_cast<jint>(wcNotify->hunk_fuzz);
+  if (jhunkFuzz < 0 || jhunkFuzz != wcNotify->hunk_fuzz)
+    {
+      env->ThrowNew(env->FindClass("java.lang.ArithmeticException"),
+                    "Overflow converting C svn_linenum_t to Java int");
+      POP_AND_RETURN_NULL;
+    }
 
   // call the Java method
   jobject jInfo = env->NewObject(clazz, midCT, jPath, jAction,
diff --git a/subversion/bindings/javahl/native/JNIUtil.cpp b/subversion/bindings/javahl/native/JNIUtil.cpp
index 8f520c4..39e9312 100644
--- a/subversion/bindings/javahl/native/JNIUtil.cpp
+++ b/subversion/bindings/javahl/native/JNIUtil.cpp
@@ -36,6 +36,7 @@
 #include <apr_general.h>
 #include <apr_lib.h>
 #include <apr_file_info.h>
+#include <apr_time.h>
 
 #include "svn_pools.h"
 #include "svn_fs.h"
@@ -106,6 +107,93 @@
   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 +201,10 @@
 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 +364,8 @@
 
   g_initEnv = NULL;
   g_inInit = false;
+
+  guard.finish();
   return true;
 }
 
@@ -579,7 +663,7 @@
     POP_AND_RETURN_NOTHING();
 
   const jsize stSize = static_cast<jsize>(newStackTrace.size());
-  if (stSize != newStackTrace.size())
+  if (stSize < 0 || stSize != newStackTrace.size())
     {
       env->ThrowNew(env->FindClass("java.lang.ArithmeticException"),
                     "Overflow converting C size_t to JNI jsize");
diff --git a/subversion/bindings/javahl/native/RemoteSession.cpp b/subversion/bindings/javahl/native/RemoteSession.cpp
index 0ca212c..60c8739 100644
--- a/subversion/bindings/javahl/native/RemoteSession.cpp
+++ b/subversion/bindings/javahl/native/RemoteSession.cpp
@@ -1073,8 +1073,10 @@
                                apr_array_header_t* prop_diffs,
                                apr_pool_t* scratch_pool)
     {
-      *delta_handler = svn_delta_noop_window_handler;
-      *delta_handler_baton = NULL;
+      if (delta_handler)
+        *delta_handler = svn_delta_noop_window_handler;
+      if (delta_handler_baton)
+        *delta_handler_baton = NULL;
 
       FileRevisionHandler* const self =
         static_cast<FileRevisionHandler*>(baton);
diff --git a/subversion/bindings/javahl/native/org_apache_subversion_javahl_NativeResources.cpp b/subversion/bindings/javahl/native/org_apache_subversion_javahl_NativeResources.cpp
index 9705304..3bf69a4 100644
--- a/subversion/bindings/javahl/native/org_apache_subversion_javahl_NativeResources.cpp
+++ b/subversion/bindings/javahl/native/org_apache_subversion_javahl_NativeResources.cpp
@@ -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));
 }
diff --git a/subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNConfig.java b/subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNConfig.java
index 12206e1..2c7e5ed 100644
--- a/subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNConfig.java
+++ b/subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNConfig.java
@@ -112,19 +112,19 @@
     public static final String HTTP_CHUNKED_REQUESTS     = "http-chunked-requests";
 
     /**
-     * "true" value in configuraition. One of the values returned by
+     * "true" value in configuration. One of the values returned by
      * {@link Category#getYesNoAsk}.
      */
     public static final String TRUE = "TRUE";
 
     /**
-     * "false" value in configuraition. One of the values returned by
+     * "false" value in configuration. One of the values returned by
      * {@link Category#getYesNoAsk}.
      */
     public static final String FALSE = "FALSE";
 
     /**
-     * "ask" value in configuraition. One of the values returned by
+     * "ask" value in configuration. One of the values returned by
      * {@link Category#getYesNoAsk}.
      */
     public static final String ASK = "ASK";
diff --git a/subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNEditor.java b/subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNEditor.java
index 608e6a2..50afd49 100644
--- a/subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNEditor.java
+++ b/subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNEditor.java
@@ -261,8 +261,7 @@
      * <code>sourceRelativePath</code> at the start of the whole edit
      * and<code>sourceRelativePath</code> at
      * <code>sourceRevision</code> must lie within the same node-rev
-     * (aka history-segment).  This is just like the #delete() and
-     * #rotate().
+     * (aka history-segment).  This is just like the #delete().
      * <p>
      * For a description of <code>replacesRevision</code>, see #addFile().
      *
@@ -274,42 +273,6 @@
               long replacesRevision)
             throws ClientException;
 
-// Not implemented in the native API
-//    /**
-//     * Perform a rotation among multiple nodes in the target tree.
-//     * <p>
-//     * The <code>elements</code> list specifies the nodes in the tree
-//     * which are located at a path and expected to be at a specific
-//     * revision. These nodes are simultaneously moved in a rotation
-//     * pattern.  For example, the node at index 0
-//     * <code>elements</code> will be moved to the relative path
-//     * specified at index 1 of <code>elements</code>. The node at
-//     * index 1 will be moved to the location at index 2. The node at
-//     * index N-1 will be moved to the relative path specified at index 0.
-//     * <p>
-//     * The simplest form of this operation is to swap nodes A and
-//     * B. One may think to move A to a temporary location T, then move
-//     * B to A, then move T to B. However, this last move violations
-//     * the Once Rule by moving T (which had already by edited by the
-//     * move from A). In order to keep the restrictions against
-//     * multiple moves of a single node, the rotation operation is
-//     * needed for certain types of tree edits.
-//     *
-//     * @throws ClientException
-//     */
-//    void rotate(Iterable<RotatePair> elements) throws ClientException;
-//
-//    public static final class RotatePair
-//    {
-//        public RotatePair(String relativePath, long revision)
-//        {
-//            this.relativePath = relativePath;
-//            this.revision = revision;
-//        }
-//        public final String relativePath;
-//        public final long revision;
-//    }
-
     /**
      * Signal that the edit has been completed successfully.
      * After this method is called, the editor is considered closed.
diff --git a/subversion/bindings/javahl/src/org/apache/subversion/javahl/NativeResources.java b/subversion/bindings/javahl/src/org/apache/subversion/javahl/NativeResources.java
index 3317bd3..1083520 100644
--- a/subversion/bindings/javahl/src/org/apache/subversion/javahl/NativeResources.java
+++ b/subversion/bindings/javahl/src/org/apache/subversion/javahl/NativeResources.java
@@ -129,7 +129,9 @@
      */
     private static final void init()
     {
-        initNativeLibrary();
+        if (!initNativeLibrary())
+            throw new LinkageError("Native library initialization failed");
+
         version = new Version();
         if (!version.isAtLeast(1, 7, 0))
         {
@@ -141,5 +143,5 @@
     /**
      * Initialize the native library layer.
      */
-    private static native void initNativeLibrary();
+    private static native boolean initNativeLibrary();
 }
diff --git a/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/CommitEditor.java b/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/CommitEditor.java
index 9bbef67..450f14c 100644
--- a/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/CommitEditor.java
+++ b/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/CommitEditor.java
@@ -131,9 +131,6 @@
         notimplemented("move");
     }
 
-//    public void rotate(Iterable<RotatePair> elements)
-//            throws ClientException;
-
     public void complete() throws ClientException
     {
         notimplemented("complete");