Hi!
Recently I've spent some time investigating how lock and unlock are implemented
in FSFS and can suggest the following simplification. I attempted just to make
this part clearer and a new code is intended to behave exactly the same.
The patch is attached.
Log message:
[[[
Simplify implementation of svn_fs_fs__lock() / svn_fs_fs__unlock().
* subversion/libsvn_fs_fs/lock.c
(schedule_index_update): New helper function.
(struct lock_info_t,
struct unlock_info_t): Drop the unused fields.
(lock_body,
unlock_body): Rework the algorithm.
]]]
PS. I've noticed at least one improper pool usage in locking code:
[[[
info->lock = svn_lock_create(lb->result_pool);
...
info->lock->path = info->path;
info->lock->owner = lb->fs->access_ctx->username;
info->lock->comment = lb->comment;
info->lock->is_dav_comment = lb->is_dav_comment;
info->lock->creation_date = apr_time_now();
info->lock->expiration_date = lb->expiration_date;
]]]
The svn_lock_t * object is created in the result_pool, but its fields like PATH
and OWNER are not getting apr_pstrdup()'ed. I'm going to fix this in another
patch.
Index: subversion/libsvn_fs_fs/lock.c
===================================================================
--- subversion/libsvn_fs_fs/lock.c (revision 1658964)
+++ subversion/libsvn_fs_fs/lock.c (working copy)
@@ -716,6 +716,35 @@ svn_fs_fs__allow_locked_operation(const char *path
return SVN_NO_ERROR;
}
+/* Helper function called from the lock and unlock code.
+ UPDATE is a map from "const char *" parent paths to "apr_array_header_t *"
+ arrays of child paths. For all of the parent paths of PATH this function
+ adds PATH to the corresponding array of child paths. */
+static void
+schedule_index_update(apr_hash_t *updates,
+ const char *path,
+ apr_pool_t *scratch_pool)
+{
+ apr_pool_t *hashpool = apr_hash_pool_get(updates);
+ const char *parent_path = path;
+
+ while (! svn_fspath__is_root(parent_path, strlen(parent_path)))
+ {
+ apr_array_header_t *children;
+
+ parent_path = svn_fspath__dirname(parent_path, scratch_pool);
+ children = svn_hash_gets(updates, parent_path);
+
+ if (! children)
+ {
+ children = apr_array_make(hashpool, 8, sizeof(const char *));
+ svn_hash_sets(updates, apr_pstrdup(hashpool, parent_path), children);
+ }
+
+ APR_ARRAY_PUSH(children, const char *) = path;
+ }
+}
+
/* The effective arguments for lock_body() below. */
struct lock_baton {
svn_fs_t *fs;
@@ -828,7 +857,6 @@ check_lock(svn_error_t **fs_err,
struct lock_info_t {
const char *path;
- const char *component;
svn_lock_t *lock;
svn_error_t *fs_err;
};
@@ -851,7 +879,9 @@ lock_body(void *baton, apr_pool_t *pool)
svn_fs_root_t *root;
svn_revnum_t youngest;
const char *rev_0_path;
- int i, outstanding = 0;
+ int i;
+ apr_hash_t *index_updates = apr_hash_make(pool);
+ apr_hash_index_t *hi;
apr_pool_t *iterpool = svn_pool_create(pool);
/* Until we implement directory locks someday, we only allow locks
@@ -865,35 +895,29 @@ lock_body(void *baton, apr_pool_t *pool)
{
const svn_sort__item_t *item = &APR_ARRAY_IDX(lb->targets, i,
svn_sort__item_t);
- const svn_fs_lock_target_t *target = item->value;
struct lock_info_t info;
svn_pool_clear(iterpool);
info.path = item->key;
- SVN_ERR(check_lock(&info.fs_err, info.path, target, lb, root, iterpool));
info.lock = NULL;
- info.component = NULL;
+ info.fs_err = SVN_NO_ERROR;
+
+ SVN_ERR(check_lock(&info.fs_err, info.path, item->value, lb, root,
+ iterpool));
+
+ /* If no error occurred while pre-checking, schedule the index updates
for
+ this path. */
+ if (!info.fs_err)
+ schedule_index_update(index_updates, info.path, iterpool);
+
APR_ARRAY_PUSH(lb->infos, struct lock_info_t) = info;
- if (!info.fs_err)
- ++outstanding;
}
rev_0_path = svn_fs_fs__path_rev_absolute(lb->fs, 0, pool);
- /* Given the paths:
+ /* We apply the scheduled index updates before writing the actual locks.
- /foo/bar/f
- /foo/bar/g
- /zig/x
-
- we loop through repeatedly. The first pass sees '/' on all paths
- and writes the '/' index. The second pass sees '/foo' twice and
- writes that index followed by '/zig' and that index. The third
- pass sees '/foo/bar' twice and writes that index, and then writes
- the lock for '/zig/x'. The fourth pass writes the locks for
- '/foo/bar/f' and '/foo/bar/g'.
-
Writing indices before locks is correct: if interrupted it leaves
indices without locks rather than locks without indices. An
index without a lock is consistent in that it always shows up as
@@ -901,92 +925,47 @@ lock_body(void *baton, apr_pool_t *pool)
index is inconsistent, svn_fs_fs__allow_locked_operation will
show locked on the file but unlocked on the parent. */
-
- while (outstanding)
+ for (hi = apr_hash_first(pool, index_updates); hi; hi = apr_hash_next(hi))
{
- const char *last_path = NULL;
- apr_array_header_t *paths;
+ const char *path = apr_hash_this_key(hi);
+ apr_array_header_t *children = apr_hash_this_val(hi);
svn_pool_clear(iterpool);
- paths = apr_array_make(iterpool, 1, sizeof(const char *));
+ SVN_ERR(add_to_digest(lb->fs->path, children, path, rev_0_path,
+ iterpool));
+ }
- for (i = 0; i < lb->infos->nelts; ++i)
- {
- struct lock_info_t *info = &APR_ARRAY_IDX(lb->infos, i,
- struct lock_info_t);
- const svn_sort__item_t *item = &APR_ARRAY_IDX(lb->targets, i,
- svn_sort__item_t);
- const svn_fs_lock_target_t *target = item->value;
+ for (i = 0; i < lb->infos->nelts; ++i)
+ {
+ struct lock_info_t *info = &APR_ARRAY_IDX(lb->infos, i,
+ struct lock_info_t);
+ svn_sort__item_t *item = &APR_ARRAY_IDX(lb->targets, i,
svn_sort__item_t);
+ svn_fs_lock_target_t *target = item->value;
- if (!info->fs_err && !info->lock)
- {
- if (!info->component)
- {
- info->component = info->path;
- APR_ARRAY_PUSH(paths, const char *) = info->path;
- last_path = "/";
- }
- else
- {
- info->component = strchr(info->component + 1, '/');
- if (!info->component)
- {
- /* The component is a path to lock, this cannot
- match a previous path that need to be indexed. */
- if (paths->nelts)
- {
- SVN_ERR(add_to_digest(lb->fs->path, paths, last_path,
- rev_0_path, iterpool));
- apr_array_clear(paths);
- last_path = NULL;
- }
+ svn_pool_clear(iterpool);
- info->lock = svn_lock_create(lb->result_pool);
- if (target->token)
- info->lock->token = target->token;
- else
- SVN_ERR(svn_fs_fs__generate_lock_token(
- &(info->lock->token), lb->fs,
- lb->result_pool));
- info->lock->path = info->path;
- info->lock->owner = lb->fs->access_ctx->username;
- info->lock->comment = lb->comment;
- info->lock->is_dav_comment = lb->is_dav_comment;
- info->lock->creation_date = apr_time_now();
- info->lock->expiration_date = lb->expiration_date;
+ if (! info->fs_err)
+ {
+ info->lock = svn_lock_create(lb->result_pool);
+ if (target->token)
+ info->lock->token = target->token;
+ else
+ SVN_ERR(svn_fs_fs__generate_lock_token(&(info->lock->token),
lb->fs,
+ lb->result_pool));
- info->fs_err = set_lock(lb->fs->path, info->lock,
- rev_0_path, iterpool);
- --outstanding;
- }
- else
- {
- /* The component is a path to an index. */
- apr_size_t len = info->component - info->path;
+ info->lock->path = info->path;
+ info->lock->owner = lb->fs->access_ctx->username;
+ info->lock->comment = lb->comment;
+ info->lock->is_dav_comment = lb->is_dav_comment;
+ info->lock->creation_date = apr_time_now();
+ info->lock->expiration_date = lb->expiration_date;
- if (last_path
- && (strncmp(last_path, info->path, len)
- || strlen(last_path) != len))
- {
- /* No match to the previous paths to index. */
- SVN_ERR(add_to_digest(lb->fs->path, paths, last_path,
- rev_0_path, iterpool));
- apr_array_clear(paths);
- last_path = NULL;
- }
- APR_ARRAY_PUSH(paths, const char *) = info->path;
- if (!last_path)
- last_path = apr_pstrndup(iterpool, info->path, len);
- }
- }
- }
-
- if (last_path && i == lb->infos->nelts - 1)
- SVN_ERR(add_to_digest(lb->fs->path, paths, last_path,
- rev_0_path, iterpool));
+ info->fs_err = set_lock(lb->fs->path, info->lock, rev_0_path,
+ iterpool);
}
}
-
+
+ svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
@@ -1027,10 +1006,8 @@ check_unlock(svn_error_t **fs_err,
struct unlock_info_t {
const char *path;
- const char *component;
svn_error_t *fs_err;
svn_boolean_t done;
- int components;
};
/* The body of svn_fs_fs__unlock(), which see.
@@ -1051,7 +1028,9 @@ unlock_body(void *baton, apr_pool_t *pool)
svn_fs_root_t *root;
svn_revnum_t youngest;
const char *rev_0_path;
- int i, max_components = 0, outstanding = 0;
+ int i;
+ apr_hash_t *indices_updates = apr_hash_make(pool);
+ apr_hash_index_t *hi;
apr_pool_t *iterpool = svn_pool_create(pool);
SVN_ERR(ub->fs->vtable->youngest_rev(&youngest, ub->fs, pool));
@@ -1062,96 +1041,56 @@ unlock_body(void *baton, apr_pool_t *pool)
const svn_sort__item_t *item = &APR_ARRAY_IDX(ub->targets, i,
svn_sort__item_t);
const char *token = item->value;
- struct unlock_info_t info = { 0 };
+ struct unlock_info_t info;
svn_pool_clear(iterpool);
info.path = item->key;
+ info.fs_err = SVN_NO_ERROR;
+ info.done = FALSE;
+
if (!ub->skip_check)
SVN_ERR(check_unlock(&info.fs_err, info.path, token, ub, root,
iterpool));
+
+ /* If no error occurred while pre-checking, schedule the index updates
for
+ this path. */
if (!info.fs_err)
- {
- const char *s;
+ schedule_index_update(indices_updates, info.path, iterpool);
- info.components = 1;
- info.component = info.path;
- while((s = strchr(info.component + 1, '/')))
- {
- info.component = s;
- ++info.components;
- }
-
- if (info.components > max_components)
- max_components = info.components;
-
- ++outstanding;
- }
APR_ARRAY_PUSH(ub->infos, struct unlock_info_t) = info;
}
rev_0_path = svn_fs_fs__path_rev_absolute(ub->fs, 0, pool);
- for (i = max_components; i >= 0; --i)
+ /* Unlike the lock_body(), we need to delete locks *before* we start to
+ update indices. */
+
+ for (i = 0; i < ub->infos->nelts; ++i)
{
- const char *last_path = NULL;
- apr_array_header_t *paths;
- int j;
+ struct unlock_info_t *info = &APR_ARRAY_IDX(ub->infos, i,
+ struct unlock_info_t);
svn_pool_clear(iterpool);
- paths = apr_array_make(pool, 1, sizeof(const char *));
- for (j = 0; j < ub->infos->nelts; ++j)
+ if (! info->fs_err)
{
- struct unlock_info_t *info = &APR_ARRAY_IDX(ub->infos, j,
- struct unlock_info_t);
+ SVN_ERR(delete_lock(ub->fs->path, info->path, iterpool));
+ info->done = TRUE;
+ }
+ }
- if (!info->fs_err && info->path)
- {
+ for (hi = apr_hash_first(pool, indices_updates); hi; hi = apr_hash_next(hi))
+ {
+ const char *path = apr_hash_this_key(hi);
+ apr_array_header_t *children = apr_hash_this_val(hi);
- if (info->components == i)
- {
- SVN_ERR(delete_lock(ub->fs->path, info->path, iterpool));
- info->done = TRUE;
- }
- else if (info->components > i)
- {
- apr_size_t len = info->component - info->path;
-
- if (last_path
- && strcmp(last_path, "/")
- && (strncmp(last_path, info->path, len)
- || strlen(last_path) != len))
- {
- SVN_ERR(delete_from_digest(ub->fs->path, paths,
last_path,
- rev_0_path, iterpool));
- apr_array_clear(paths);
- last_path = NULL;
- }
- APR_ARRAY_PUSH(paths, const char *) = info->path;
- if (!last_path)
- {
- if (info->component > info->path)
- last_path = apr_pstrndup(pool, info->path, len);
- else
- last_path = "/";
- }
-
- if (info->component > info->path)
- {
- --info->component;
- while(info->component[0] != '/')
- --info->component;
- }
- }
- }
-
- if (last_path && j == ub->infos->nelts - 1)
- SVN_ERR(delete_from_digest(ub->fs->path, paths, last_path,
- rev_0_path, iterpool));
- }
+ svn_pool_clear(iterpool);
+ SVN_ERR(delete_from_digest(ub->fs->path, children, path, rev_0_path,
+ iterpool));
}
+ svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}