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