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