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