For the last few weeks I've been working on the atomic-revprop branch.

(Its goal is port the new svn_fs_change_rev_prop2() to libsvn_ra, which
should allow callers to specify both a value and an optional "previous
value" and have the revprop change atomic; see [1].)

Currently, the API is implemented over all RA layers, and the test (prop 34)
passes.

To extend svnsync to use the new API for acquiring locks, 



[1] 
http://thread.gmane.org/gmane.comp.version-control.subversion.devel/120330/focus=120416


Index: subversion/include/svn_ra.h
===================================================================
--- subversion/include/svn_ra.h	(revision 983929)
+++ subversion/include/svn_ra.h	(working copy)
@@ -730,6 +730,8 @@ svn_ra_get_dated_revision(svn_ra_session_t *sessio
  * Use @a pool for memory allocation.
  *
  * @since New in 1.7.
+ *
+ * ### need to guarantee specific error code
  */
 svn_error_t *
 svn_ra_change_rev_prop2(svn_ra_session_t *session,
Index: subversion/svnsync/main.c
===================================================================
--- subversion/svnsync/main.c	(revision 983929)
+++ subversion/svnsync/main.c	(working copy)
@@ -284,18 +284,41 @@ check_lib_versions(void)
 }
 
 
+/* Does ERR mean "the current value of the revprop isn't equal to
+   the *OLD_VALUE_P you gave me"?
+ */
+static svn_boolean_t is_atomicity_error(svn_error_t *err)
+{
+  svn_error_t *child;
+  /* ### Hack until ra_dav learns to return the proper error code. */
+  for (child = err; child; child = child->child)
+    if (!strstr(child->message,
+                "revprop '" SVNSYNC_PROP_LOCK "' has unexpected value in "
+                "filesystem"))
+      return TRUE;
+
+  return svn_error_has_cause(err, SVN_ERR_BAD_PROPERTY_VALUE /* ### rename */);
+}
+
 /* Acquire a lock (of sorts) on the repository associated with the
  * given RA SESSION.
  */
 static svn_error_t *
-get_lock(svn_ra_session_t *session, apr_pool_t *pool)
+get_lock(const svn_string_t **lock_string_p,
+         svn_ra_session_t *session,
+         apr_pool_t *pool)
 {
   char hostname_str[APRMAXHOSTLEN + 1] = { 0 };
   svn_string_t *mylocktoken, *reposlocktoken;
   apr_status_t apr_err;
+  svn_boolean_t be_atomic;
   apr_pool_t *subpool;
   int i;
 
+  SVN_ERR(svn_ra_has_capability(session, &be_atomic,
+                                SVN_RA_CAPABILITY_ATOMIC_REVPROPS,
+                                pool));
+
   apr_err = apr_gethostname(hostname_str, sizeof(hostname_str), pool);
   if (apr_err)
     return svn_error_wrap_apr(apr_err, _("Can't get local hostname"));
@@ -303,6 +326,9 @@ static svn_error_t *
   mylocktoken = svn_string_createf(pool, "%s:%s", hostname_str,
                                    svn_uuid_generate(pool));
 
+  /* If we succeed, this is what the property will be set to. */
+  *lock_string_p = mylocktoken;
+
   subpool = svn_pool_create(pool);
 
 #define SVNSYNC_LOCK_RETRIES 10
@@ -331,9 +357,23 @@ static svn_error_t *
         }
       else if (i < SVNSYNC_LOCK_RETRIES - 1)
         {
+          const svn_string_t *unset = NULL;
+          svn_error_t *err;
+
           /* Except in the very last iteration, try to set the lock. */
-          SVN_ERR(svn_ra_change_rev_prop(session, 0, SVNSYNC_PROP_LOCK,
-                                         mylocktoken, subpool));
+          err = svn_ra_change_rev_prop2(session, 0, SVNSYNC_PROP_LOCK,
+                                        be_atomic ? &unset : NULL,
+                                        mylocktoken, subpool);
+
+          if (be_atomic && err && is_atomicity_error(err))
+            /* Someone else has the lock.  Let's loop. */
+            svn_error_clear(err);
+          else if (be_atomic && err == SVN_NO_ERROR)
+            /* We have the lock. */
+            return SVN_NO_ERROR;
+          else
+            /* Genuine error, or we aren't atomic and need to loop. */
+            SVN_ERR(err);
         }
     }
 
@@ -381,13 +421,23 @@ with_locked(svn_ra_session_t *session,
             apr_pool_t *pool)
 {
   svn_error_t *err, *err2;
+  const svn_string_t *lock_string;
 
-  SVN_ERR(get_lock(session, pool));
+  SVN_ERR(get_lock(&lock_string, session, pool));
 
   err = func(session, baton, pool);
 
-  err2 = svn_ra_change_rev_prop(session, 0, SVNSYNC_PROP_LOCK, NULL, pool);
+  SVN_ERR(svn_ra_change_rev_prop2(session, 0, SVNSYNC_PROP_LOCK,
+                                  NULL, svn_string_create("bogus lock token", pool),
+                                  pool));
 
+  err2 = svn_ra_change_rev_prop2(session, 0, SVNSYNC_PROP_LOCK,
+                                 &lock_string, NULL, pool);
+  if (is_atomicity_error(err2))
+    err2 = svn_error_quick_wrap(err2,
+                                _("svnsync lock was stolen; can't remove it"));
+
+
   return svn_error_compose_create(err, svn_error_return(err2));
 }
 

Reply via email to