Index: subversion/libsvn_fs_fs/fs_fs.c =================================================================== --- subversion/libsvn_fs_fs/fs_fs.c (revision 26899) +++ subversion/libsvn_fs_fs/fs_fs.c (working copy) @@ -144,6 +144,12 @@ svn_fs_fs__path_current(svn_fs_t *fs, ap } static const char * +path_txn_current(svn_fs_t *fs, apr_pool_t *pool) +{ + return svn_path_join(fs->path, PATH_TXN_CURRENT, pool); +} + +static const char * path_lock(svn_fs_t *fs, apr_pool_t *pool) { return svn_path_join(fs->path, PATH_LOCK_FILE, pool); @@ -393,6 +399,118 @@ with_txnlist_lock(svn_fs_t *fs, return err; } + +/* Get a lock on file LOCK_FILENAME, creating it in POOL. */ +static svn_error_t * +get_lock_on_filesystem(const char *lock_filename, + apr_pool_t *pool) +{ + svn_node_kind_t kind; + + /* svn 1.1.1 and earlier deferred lock file creation (for the global + repository 'write-lock') to the first commit. So in case the + repository was created by an earlier version of svn, check the + lock file here. */ + SVN_ERR(svn_io_check_path(lock_filename, &kind, pool)); + if ((kind == svn_node_unknown) || (kind == svn_node_none)) + SVN_ERR(svn_io_file_create(lock_filename, "", pool)); + + SVN_ERR(svn_io_file_lock2(lock_filename, TRUE, FALSE, pool)); + + return SVN_NO_ERROR; +} + +/* Obtain a write lock on the file LOCK_FILENAME (protecting with + LOCK_MUTEX if APR is threaded) in a subpool of POOL, call BODY with + BATON and that subpool, destroy the subpool (releasing the write + lock) and return what BODY returned. */ +static svn_error_t * +with_some_lock(svn_error_t *(*body)(void *baton, + apr_pool_t *pool), + void *baton, + const char *lock_filename, +#if APR_HAS_THREADS + apr_thread_mutex_t *lock_mutex, +#endif + apr_pool_t *pool) +{ + apr_pool_t *subpool = svn_pool_create(pool); + svn_error_t *err; + +#if APR_HAS_THREADS + apr_status_t status; + + /* POSIX fcntl locks are per-process, so we need to serialize locks + within the process. */ + status = apr_thread_mutex_lock(lock_mutex); + if (status) + return svn_error_wrap_apr(status, + _("Can't grab FSFS mutex for '%s'"), + lock_filename); +#endif + + err = get_lock_on_filesystem(lock_filename, subpool); + + if (!err) + err = body(baton, subpool); + + svn_pool_destroy(subpool); + +#if APR_HAS_THREADS + status = apr_thread_mutex_unlock(lock_mutex); + if (status && !err) + return svn_error_wrap_apr(status, + _("Can't ungrab FSFS mutex for '%s'"), + lock_filename); +#endif + + return err; +} + +svn_error_t * +svn_fs_fs__with_write_lock(svn_fs_t *fs, + svn_error_t *(*body)(void *baton, + apr_pool_t *pool), + void *baton, + apr_pool_t *pool) +{ +#if APR_HAS_THREADS + fs_fs_data_t *ffd = fs->fsap_data; + fs_fs_shared_data_t *ffsd = ffd->shared; + apr_thread_mutex_t *mutex = ffsd->fs_write_lock; +#endif + + return with_some_lock(body, baton, + path_lock(fs, pool), +#if APR_HAS_THREADS + mutex, +#endif + pool); +} + +/* Run BODY (with BATON and POOL) while the transaction-current file + of FS is locked. */ +svn_error_t * +with_txn_current_lock(svn_fs_t *fs, + svn_error_t *(*body)(void *baton, + apr_pool_t *pool), + void *baton, + apr_pool_t *pool) +{ +#if APR_HAS_THREADS + fs_fs_data_t *ffd = fs->fsap_data; + fs_fs_shared_data_t *ffsd = ffd->shared; + apr_thread_mutex_t *mutex = ffsd->txn_current_lock; +#endif + + return with_some_lock(body, baton, + path_txn_current(fs, pool), +#if APR_HAS_THREADS + mutex, +#endif + pool); +} + /* A structure used by unlock_proto_rev() and unlock_proto_rev_body(), which see. */ struct unlock_proto_rev_baton @@ -3156,9 +3274,7 @@ static svn_error_t * get_and_increment_txn_key_body(void *baton, apr_pool_t *pool) { struct get_and_increment_txn_key_baton *cb = baton; - const char *txn_current_filename = svn_path_join(cb->fs->path, - PATH_TXN_CURRENT, - pool); + const char *txn_current_filename = path_txn_current(cb->fs, pool); apr_file_t *txn_current_file; const char *tmp_filename; char next_txn_id[MAX_KEY_SIZE+3]; @@ -3235,10 +3351,10 @@ create_txn_dir(const char **id_p, svn_fs number the transaction is based off into the transaction id. */ cb.pool = pool; cb.fs = fs; - SVN_ERR(svn_fs_fs__with_write_lock(fs, - get_and_increment_txn_key_body, - &cb, - pool)); + SVN_ERR(with_txn_current_lock(fs, + get_and_increment_txn_key_body, + &cb, + pool)); *id_p = apr_psprintf(pool, "%ld-%s", rev, cb.txn_id); txn_dir = svn_path_join_many(pool, @@ -4633,67 +4749,6 @@ write_final_current(svn_fs_t *fs, return write_current(fs, rev, new_node_id, new_copy_id, pool); } -/* Get a write lock in FS, creating it in POOL. */ -static svn_error_t * -get_write_lock(svn_fs_t *fs, - apr_pool_t *pool) -{ - const char *lock_filename; - svn_node_kind_t kind; - - lock_filename = path_lock(fs, pool); - - /* svn 1.1.1 and earlier deferred lock file creation to the first - commit. So in case the repository was created by an earlier - version of svn, check the lock file here. */ - SVN_ERR(svn_io_check_path(lock_filename, &kind, pool)); - if ((kind == svn_node_unknown) || (kind == svn_node_none)) - SVN_ERR(svn_io_file_create(lock_filename, "", pool)); - - SVN_ERR(svn_io_file_lock2(lock_filename, TRUE, FALSE, pool)); - - return SVN_NO_ERROR; -} - -svn_error_t * -svn_fs_fs__with_write_lock(svn_fs_t *fs, - svn_error_t *(*body)(void *baton, - apr_pool_t *pool), - void *baton, - apr_pool_t *pool) -{ - apr_pool_t *subpool = svn_pool_create(pool); - svn_error_t *err; - -#if APR_HAS_THREADS - fs_fs_data_t *ffd = fs->fsap_data; - fs_fs_shared_data_t *ffsd = ffd->shared; - apr_status_t status; - - /* POSIX fcntl locks are per-process, so we need to serialize locks - within the process. */ - status = apr_thread_mutex_lock(ffsd->fs_write_lock); - if (status) - return svn_error_wrap_apr(status, _("Can't grab FSFS repository mutex")); -#endif - - err = get_write_lock(fs, subpool); - - if (!err) - err = body(baton, subpool); - - svn_pool_destroy(subpool); - -#if APR_HAS_THREADS - status = apr_thread_mutex_unlock(ffsd->fs_write_lock); - if (status && !err) - return svn_error_wrap_apr(status, - _("Can't ungrab FSFS repository mutex")); -#endif - - return err; -} - /* Verify that the user registed with FS has all the locks necessary to permit all the changes associate with TXN_NAME. The FS write lock is assumed to be held by the caller. */ Index: subversion/libsvn_fs_fs/fs.c =================================================================== --- subversion/libsvn_fs_fs/fs.c (revision 26899) +++ subversion/libsvn_fs_fs/fs.c (working copy) @@ -110,6 +110,13 @@ fs_serialized_init(svn_fs_t *fs, apr_poo if (status) return svn_error_wrap_apr(status, _("Can't create FSFS txn list mutex")); + + /* ... not to mention locking the transaction-current file. */ + status = apr_thread_mutex_create(&ffsd->txn_current_lock, + APR_THREAD_MUTEX_DEFAULT, common_pool); + if (status) + return svn_error_wrap_apr(status, + _("Can't create FSFS txn-current mutex")); #endif key = apr_pstrdup(common_pool, key); Index: subversion/libsvn_fs_fs/fs.h =================================================================== --- subversion/libsvn_fs_fs/fs.h (revision 26899) +++ subversion/libsvn_fs_fs/fs.h (working copy) @@ -136,6 +136,10 @@ typedef struct /* A lock for intra-process synchronization when grabbing the repository write lock. */ apr_thread_mutex_t *fs_write_lock; + + /* A lock for intra-process synchronization when locking the + transaction-current file. */ + apr_thread_mutex_t *txn_current_lock; #endif /* The common pool, under which this object is allocated, subpools Index: subversion/libsvn_fs_fs/structure =================================================================== --- subversion/libsvn_fs_fs/structure (revision 26899) +++ subversion/libsvn_fs_fs/structure (working copy) @@ -72,8 +72,7 @@ in the next transaction name, along with transaction is based on. This sequence number ensures that transaction names are not reused, even if the transaction is aborted and a new transaction based on the same revision is begun. The -"transaction-current" file is read and written under the fs-wide -write-lock. +"transaction-current" file is locked when it is read and written. Filesystem formats ------------------