Author: rhuijben Date: Tue Jul 13 21:08:49 2010 New Revision: 963863 URL: http://svn.apache.org/viewvc?rev=963863&view=rev Log: Update the wc-db api for wclocks to have a single set of functions for obtaining and releasing locks instead of a separate function for recording and for knowing that we own the lock. Also add an initial implementation for the lock behavior for when we switch to a single database.
* subversion/libsvn_wc/adm_ops.c (svn_wc_add4): Update caller. * subversion/libsvn_wc/lock.c (pool_cleanup_locked): Update caller. (adm_access_alloc): Update caller. (close_single): Update caller. (svn_wc_adm_locked): Update caller. (svn_wc__write_check): Update caller. (svn_wc_locked2): Update caller. (svn_wc__acquire_write_lock): Update caller. (svn_wc__release_write_lock): Update caller and handle errors on unlocking when we don't have a lock to keep current behavior. * subversion/libsvn_wc/log.c (cleanup_internal): Obtain lock with steal_lock mode, instead of ignoring the error. * subversion/libsvn_wc/upgrade.c (upgrade_to_wcng): Update caller. * subversion/libsvn_wc/wc-queries.sql (STMT_FIND_WC_LOCK): New query to obtain existing locks below a new recursive lock. * subversion/libsvn_wc/wc_db.c (relpath_op_depth): New helper function. (svn_wc__db_temp_forget_directory): Ignore error conditions to keep current (mostly broken) behavior. (wclock_obtain_baton): New struct. (wclock_steal): New helper function. (wclock_obtain_cb): New transaction helper function. (svn_wc__db_wclock_set): Renamed to ... (svn_wc__db_wclock_obtain): ... this. Use a transaction to verify current recursive locks and obtain a new lock. (Mostly unused in per directory db layout). (svn_wc__db_wclock_remove): Renamed to ... (svn_wc__db_wclock_release): ... this. Verify lock ownership and add support for single db operations. (svn_wc__db_temp_mark_locked): Remove function. (svn_wc__db_temp_own_lock): Rename to ... (svn_wc__db_wclock_owns_lock): ... this and allow checking for checking a root and/or multiple levels. * subversion/libsvn_wc/wc_db.h (svn_wc__db_wclock_set): Rename to ... (svn_wc__db_wclock_obtain): ... this and document levels_to_lock. (svn_wc__db_wclocked): Add documentation. (svn_wc__db_wclock_remove): Rename to ... (svn_wc__db_wclock_release): ... this. (svn_wc__db_wclock_owns_lock): New function. (svn_wc__db_temp_mark_locked): Remove function. (svn_wc__db_temp_own_lock): Remove function. * subversion/libsvn_wc/wc_db_pdh.c (determine_obstructed_file): #undef when using single_db. (svn_wc__db_pdh_create_wcroot): Initialize new lock store if single db. (svn_wc__db_pdh_parse_local_abspath): Disable obstruction check on single db. * subversion/libsvn_wc/wc_db_private.h (svn_wc__db_wclock_t): New struct. (svn_wc__db_wcroot_t): Name struct (for debugging and svn diff -x -p) Add array of owned locks. (svn_wc__db_pdh_t): Remove obstructed_file and locked for single db. Modified: subversion/trunk/subversion/libsvn_wc/adm_ops.c subversion/trunk/subversion/libsvn_wc/lock.c subversion/trunk/subversion/libsvn_wc/log.c subversion/trunk/subversion/libsvn_wc/upgrade.c subversion/trunk/subversion/libsvn_wc/wc-queries.sql subversion/trunk/subversion/libsvn_wc/wc_db.c subversion/trunk/subversion/libsvn_wc/wc_db.h subversion/trunk/subversion/libsvn_wc/wc_db_pdh.c subversion/trunk/subversion/libsvn_wc/wc_db_private.h Modified: subversion/trunk/subversion/libsvn_wc/adm_ops.c URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/adm_ops.c?rev=963863&r1=963862&r2=963863&view=diff ============================================================================== --- subversion/trunk/subversion/libsvn_wc/adm_ops.c (original) +++ subversion/trunk/subversion/libsvn_wc/adm_ops.c Tue Jul 13 21:08:49 2010 @@ -1272,8 +1272,8 @@ svn_wc_add4(svn_wc_context_t *wc_ctx, if (kind == svn_node_dir && !exists) { /* Lock on parent needs to be propogated into the child db. */ - SVN_ERR(svn_wc__db_wclock_set(db, local_abspath, 0, scratch_pool)); - SVN_ERR(svn_wc__db_temp_mark_locked(db, local_abspath, scratch_pool)); + SVN_ERR(svn_wc__db_wclock_obtain(db, local_abspath, 0, FALSE, + scratch_pool)); } #if (SVN_WC__VERSION < SVN_WC__PROPS_IN_DB) Modified: subversion/trunk/subversion/libsvn_wc/lock.c URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/lock.c?rev=963863&r1=963862&r2=963863&view=diff ============================================================================== --- subversion/trunk/subversion/libsvn_wc/lock.c (original) +++ subversion/trunk/subversion/libsvn_wc/lock.c Tue Jul 13 21:08:49 2010 @@ -204,7 +204,7 @@ pool_cleanup_locked(void *p) { /* There is no remaining work, so we're good to remove any potential "physical" lock. */ - err = svn_wc__db_wclock_remove(db, lock->abspath, scratch_pool); + err = svn_wc__db_wclock_release(db, lock->abspath, scratch_pool); } } svn_error_clear(err); @@ -312,7 +312,8 @@ adm_access_alloc(svn_wc_adm_access_t **a svn_boolean_t owns_lock; /* If the db already owns a lock, we can't add an extra lock record */ - SVN_ERR(svn_wc__db_temp_own_lock(&owns_lock, db, path, scratch_pool)); + SVN_ERR(svn_wc__db_wclock_owns_lock(&owns_lock, db, path, FALSE, + scratch_pool)); /* If DB owns the lock, but when there is no access baton open for this directory, old access baton based code is trying to access data that @@ -321,9 +322,8 @@ adm_access_alloc(svn_wc_adm_access_t **a if (!owns_lock || svn_wc__adm_retrieve_internal2(db, lock->abspath, scratch_pool)) { - SVN_ERR(svn_wc__db_wclock_set(db, lock->abspath, 0, scratch_pool)); - - SVN_ERR(svn_wc__db_temp_mark_locked(db, lock->abspath, scratch_pool)); + SVN_ERR(svn_wc__db_wclock_obtain(db, lock->abspath, 0, FALSE, + scratch_pool)); } } @@ -332,7 +332,7 @@ adm_access_alloc(svn_wc_adm_access_t **a if (err) return svn_error_compose_create( err, - svn_wc__db_wclock_remove(db, lock->abspath, scratch_pool)); + svn_wc__db_wclock_release(db, lock->abspath, scratch_pool)); /* ### does this utf8 thing really/still apply?? */ /* It's important that the cleanup handler is registered *after* at least @@ -516,8 +516,9 @@ close_single(svn_wc_adm_access_t *adm_ac return SVN_NO_ERROR; /* Physically unlock if required */ - SVN_ERR(svn_wc__db_temp_own_lock(&locked, adm_access->db, - adm_access->abspath, scratch_pool)); + SVN_ERR(svn_wc__db_wclock_owns_lock(&locked, adm_access->db, + adm_access->abspath, TRUE, + scratch_pool)); if (locked) { if (!preserve_lock) @@ -528,9 +529,9 @@ close_single(svn_wc_adm_access_t *adm_ac from the working copy. It is an error for the lock to have disappeared if the administrative area still exists. */ - svn_error_t *err = svn_wc__db_wclock_remove(adm_access->db, - adm_access->abspath, - scratch_pool); + svn_error_t *err = svn_wc__db_wclock_release(adm_access->db, + adm_access->abspath, + scratch_pool); if (err) { if (svn_wc__adm_area_exists(adm_access->abspath, scratch_pool)) @@ -1419,9 +1420,9 @@ svn_wc_adm_locked(const svn_wc_adm_acces { svn_boolean_t locked; apr_pool_t *subpool = svn_pool_create(adm_access->pool); - svn_error_t *err = svn_wc__db_temp_own_lock(&locked, adm_access->db, - adm_access->abspath, - subpool); + svn_error_t *err = svn_wc__db_wclock_owns_lock(&locked, adm_access->db, + adm_access->abspath, TRUE, + subpool); svn_pool_destroy(subpool); if (err) @@ -1441,7 +1442,8 @@ svn_wc__write_check(svn_wc__db_t *db, { svn_boolean_t locked; - SVN_ERR(svn_wc__db_temp_own_lock(&locked, db, local_abspath, scratch_pool)); + SVN_ERR(svn_wc__db_wclock_owns_lock(&locked, db, local_abspath, FALSE, + scratch_pool)); if (!locked) return svn_error_createf(SVN_ERR_WC_NOT_LOCKED, NULL, _("No write-lock in '%s'"), @@ -1461,8 +1463,8 @@ svn_wc_locked2(svn_boolean_t *locked_her SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); if (locked_here != NULL) - SVN_ERR(svn_wc__db_temp_own_lock(locked_here, wc_ctx->db, local_abspath, - scratch_pool)); + SVN_ERR(svn_wc__db_wclock_owns_lock(locked_here, wc_ctx->db, local_abspath, + FALSE, scratch_pool)); if (locked != NULL) SVN_ERR(svn_wc__db_wclocked(locked, wc_ctx->db, local_abspath, scratch_pool)); @@ -1652,11 +1654,12 @@ svn_wc__acquire_write_lock(const char ** /* We don't want to try and lock an unversioned directory that obstructs a versioned directory. */ - err = svn_wc__internal_check_wc(&format, wc_ctx->db, local_abspath, iterpool); + err = svn_wc__internal_check_wc(&format, wc_ctx->db, local_abspath, + iterpool); if (!err && format) { - SVN_ERR(svn_wc__db_wclock_set(wc_ctx->db, local_abspath, 0, iterpool)); - SVN_ERR(svn_wc__db_temp_mark_locked(wc_ctx->db, local_abspath, iterpool)); + SVN_ERR(svn_wc__db_wclock_obtain(wc_ctx->db, local_abspath, 0, FALSE, + iterpool)); } svn_error_clear(err); @@ -1677,6 +1680,7 @@ svn_wc__release_write_lock(svn_wc_contex apr_uint64_t id; svn_skel_t *work_item; svn_boolean_t locked_here; + svn_error_t *err; int i; SVN_ERR(svn_wc__db_read_kind(&kind, wc_ctx->db, local_abspath, TRUE, @@ -1713,10 +1717,20 @@ svn_wc__release_write_lock(svn_wc_contex SVN_ERR(svn_wc__release_write_lock(wc_ctx, child_abspath, iterpool)); } - SVN_ERR(svn_wc__db_temp_own_lock(&locked_here, wc_ctx->db, local_abspath, - iterpool)); + err = svn_wc__db_wclock_owns_lock(&locked_here, wc_ctx->db, local_abspath, + TRUE, iterpool); +#ifndef SVN_WC__SINGLE_DB + if (err && err->apr_err == SVN_ERR_WC_NOT_WORKING_COPY) + { + svn_error_clear(err); + locked_here = FALSE; + } + else +#endif + SVN_ERR(err); + if (locked_here) - SVN_ERR(svn_wc__db_wclock_remove(wc_ctx->db, local_abspath, iterpool)); + SVN_ERR(svn_wc__db_wclock_release(wc_ctx->db, local_abspath, iterpool)); svn_pool_destroy(iterpool); Modified: subversion/trunk/subversion/libsvn_wc/log.c URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/log.c?rev=963863&r1=963862&r2=963863&view=diff ============================================================================== --- subversion/trunk/subversion/libsvn_wc/log.c (original) +++ subversion/trunk/subversion/libsvn_wc/log.c Tue Jul 13 21:08:49 2010 @@ -749,7 +749,6 @@ cleanup_internal(svn_wc__db_t *db, apr_pool_t *scratch_pool) { int wc_format; - svn_error_t *err; const apr_array_header_t *children; int i; apr_pool_t *iterpool = svn_pool_create(scratch_pool); @@ -762,12 +761,7 @@ cleanup_internal(svn_wc__db_t *db, SVN_ERR(can_be_cleaned(&wc_format, db, adm_abspath, iterpool)); /* Lock this working copy directory, or steal an existing lock */ - err = svn_wc__db_wclock_set(db, adm_abspath, 0, iterpool); - if (err && err->apr_err == SVN_ERR_WC_LOCKED) - svn_error_clear(err); - else if (err) - return svn_error_return(err); - SVN_ERR(svn_wc__db_temp_mark_locked(db, adm_abspath, iterpool)); + SVN_ERR(svn_wc__db_wclock_obtain(db, adm_abspath, 0, TRUE, iterpool)); /* Run our changes before the subdirectories. We may not have to recurse if we blow away a subdir. */ @@ -816,7 +810,7 @@ cleanup_internal(svn_wc__db_t *db, SVN_ERR(svn_wc__db_pristine_cleanup(db, adm_abspath, iterpool)); /* All done, toss the lock */ - SVN_ERR(svn_wc__db_wclock_remove(db, adm_abspath, iterpool)); + SVN_ERR(svn_wc__db_wclock_release(db, adm_abspath, iterpool)); svn_pool_destroy(iterpool); Modified: subversion/trunk/subversion/libsvn_wc/upgrade.c URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/upgrade.c?rev=963863&r1=963862&r2=963863&view=diff ============================================================================== --- subversion/trunk/subversion/libsvn_wc/upgrade.c (original) +++ subversion/trunk/subversion/libsvn_wc/upgrade.c Tue Jul 13 21:08:49 2010 @@ -1316,7 +1316,7 @@ upgrade_to_wcng(svn_wc__db_t *db, function bumps a working copy all the way to current. */ SVN_ERR(svn_wc__db_temp_reset_format(SVN_WC__VERSION, db, dir_abspath, iterpool)); - SVN_ERR(svn_wc__db_wclock_set(db, dir_abspath, 0, iterpool)); + SVN_ERR(svn_wc__db_wclock_obtain(db, dir_abspath, 0, FALSE, iterpool)); SVN_ERR(svn_wc__write_upgraded_entries(db, sdb, repos_id, wc_id, dir_abspath, entries, iterpool)); @@ -1360,7 +1360,7 @@ upgrade_to_wcng(svn_wc__db_t *db, has run. */ /* ### well, actually.... we don't recursively delete subdir locks here, ### we rely upon their own upgrade processes to do it. */ - SVN_ERR(svn_wc__db_wclock_remove(db, dir_abspath, iterpool)); + SVN_ERR(svn_wc__db_wclock_release(db, dir_abspath, iterpool)); /* Zap all the obsolete files. This removes the old-style lock file. */ wipe_obsolete_files(dir_abspath, iterpool); Modified: subversion/trunk/subversion/libsvn_wc/wc-queries.sql URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc-queries.sql?rev=963863&r1=963862&r2=963863&view=diff ============================================================================== --- subversion/trunk/subversion/libsvn_wc/wc-queries.sql (original) +++ subversion/trunk/subversion/libsvn_wc/wc-queries.sql Tue Jul 13 21:08:49 2010 @@ -391,6 +391,10 @@ WHERE wc_id = ?1 AND local_dir_relpath = DELETE FROM WC_LOCK WHERE wc_id = ?1 AND local_dir_relpath = ?2; +-- STMT_FIND_WC_LOCK +SELECT local_dir_relpath FROM WC_LOCK +WHERE wc_id = ?1 AND local_dir_relpath LIKE ?2 ESCAPE '#'; + -- STMT_APPLY_CHANGES_TO_BASE /* translated_size and last_mod_time are not mentioned here because they will be tweaked after the working-file is installed. Modified: subversion/trunk/subversion/libsvn_wc/wc_db.c URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc_db.c?rev=963863&r1=963862&r2=963863&view=diff ============================================================================== --- subversion/trunk/subversion/libsvn_wc/wc_db.c (original) +++ subversion/trunk/subversion/libsvn_wc/wc_db.c Tue Jul 13 21:08:49 2010 @@ -125,6 +125,22 @@ NOTE: this should match the character used within wc-metadata.sql */ #define LIKE_ESCAPE_CHAR "#" +/* Calculates the depth of the relpath below "" */ +APR_INLINE static int relpath_op_depth(const char *relpath) +{ + int n = 1; + if (*relpath == '\0') + return 0; + + do + { + if (*relpath == '/') + n++; + } + while (*(++relpath)); + + return n; +} typedef struct insert_base_baton_t { /* common to all insertions into BASE */ @@ -6969,6 +6985,7 @@ svn_wc__db_temp_forget_directory(svn_wc_ apr_ssize_t klen; void *val; svn_wc__db_pdh_t *pdh; + svn_error_t *err; apr_hash_this(hi, &key, &klen, &val); pdh = val; @@ -6976,7 +6993,22 @@ svn_wc__db_temp_forget_directory(svn_wc_ if (!svn_dirent_is_ancestor(local_dir_abspath, pdh->local_abspath)) continue; - SVN_ERR(svn_wc__db_wclock_remove(db, pdh->local_abspath, scratch_pool)); + if (pdh->locked) + { + err = svn_wc__db_wclock_release(db, pdh->local_abspath, + scratch_pool); + if (err + && (err->apr_err == SVN_ERR_WC_NOT_WORKING_COPY + || err->apr_err == SVN_ERR_WC_NOT_LOCKED)) + { + svn_error_clear(err); + } + else + SVN_ERR(err); + } + + SVN_ERR_ASSERT(!pdh->locked); + apr_hash_set(db->dir_data, key, klen, NULL); if (pdh->wcroot && pdh->wcroot->sdb && @@ -7573,45 +7605,262 @@ svn_wc__db_temp_wcroot_tempdir(const cha return SVN_NO_ERROR; } - -svn_error_t * -svn_wc__db_wclock_set(svn_wc__db_t *db, - const char *local_abspath, - int levels_to_lock, - apr_pool_t *scratch_pool) +/* Baton for wclock_obtain_cb() */ +struct wclock_obtain_baton { + svn_wc__db_t *db; svn_wc__db_pdh_t *pdh; const char *local_relpath; + const char *local_abspath; + int levels_to_lock; + svn_boolean_t steal_lock; +}; + +/* Helper for wclock_obtain_cb() to steal an existing lock */ +static svn_error_t * +wclock_steal(svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + apr_pool_t *scratch_pool) +{ svn_sqlite__stmt_t *stmt; - svn_error_t *err; - SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_DELETE_WC_LOCK)); + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); - SVN_ERR(svn_wc__db_pdh_parse_local_abspath(&pdh, &local_relpath, db, - local_abspath, svn_sqlite__mode_readwrite, - scratch_pool, scratch_pool)); - VERIFY_USABLE_PDH(pdh); + SVN_ERR(svn_sqlite__step_done(stmt)); + return SVN_NO_ERROR; +} + +/* svn_sqlite__transaction_callback_t for svn_wc__db_wclock_obtain() */ +static svn_error_t * +wclock_obtain_cb(void *baton, + svn_sqlite__db_t* sdb, + apr_pool_t *scratch_pool) +{ + struct wclock_obtain_baton *bt = baton; + svn_sqlite__stmt_t *stmt; + svn_wc__db_wcroot_t *wcroot = bt->pdh->wcroot; + svn_error_t *err; + const char *lock_relpath; + int max_depth; + int lock_depth; + svn_boolean_t got_row; + +#ifdef SVN_WC__SINGLE_DB + svn_wc__db_wclock_t lock; +#else /* ### Can only lock this directory in the per-dir layout. This is ### a temporary restriction until metadata gets centralised. ### Perhaps this should be a runtime error, rather than an ### assert? Perhaps check the path is versioned? */ - SVN_ERR_ASSERT(*local_relpath == '\0'); + SVN_ERR_ASSERT(*bt->local_relpath == '\0'); +#endif - SVN_ERR(svn_sqlite__get_statement(&stmt, pdh->wcroot->sdb, + /* Check if there are nodes locked below the new lock root */ + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_FIND_WC_LOCK)); + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, + apr_pstrcat(scratch_pool, + escape_sqlite_like(bt->local_relpath, + scratch_pool), + "/%", NULL))); + + lock_depth = relpath_op_depth(bt->local_relpath); + max_depth = lock_depth + bt->levels_to_lock; + + SVN_ERR(svn_sqlite__step(&got_row, stmt)); + + while (got_row) + { + const char *lock_abspath; + svn_boolean_t own_lock; + + lock_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool); + + /* If we are not locking with depth infinity, check if this lock + voids our lock request */ + if (bt->levels_to_lock >= 0 + && relpath_op_depth(lock_relpath) > max_depth) + { + SVN_ERR(svn_sqlite__step(&got_row, stmt)); + continue; + } + + lock_abspath = svn_dirent_join(wcroot->abspath, + lock_relpath, scratch_pool); + + /* Check if we are the lock owner, because we should be able to + extend our lock. */ + err = svn_wc__db_wclock_owns_lock(&own_lock, bt->db, lock_abspath, + TRUE, scratch_pool); + + if (err) + SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt))); + + if (!own_lock && !bt->steal_lock) + { + err = svn_error_createf(SVN_ERR_WC_LOCKED, NULL, + _("'%s' is already locked."), + svn_dirent_local_style(lock_abspath, + scratch_pool)); + return svn_error_createf(SVN_ERR_WC_LOCKED, err, + _("Working copy '%s' locked."), + svn_dirent_local_style(bt->local_abspath, + scratch_pool)); + } + else if (!own_lock) + { + err = wclock_steal(wcroot, lock_relpath, scratch_pool); + + if (err) + SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt))); + } + + SVN_ERR(svn_sqlite__step(&got_row, stmt)); + } + + SVN_ERR(svn_sqlite__reset(stmt)); + + if (bt->steal_lock) + SVN_ERR(wclock_steal(wcroot, bt->local_relpath, scratch_pool)); + + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_WC_LOCK)); + lock_relpath = bt->local_relpath; + + while (TRUE) + { + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, lock_relpath)); + + SVN_ERR(svn_sqlite__step(&got_row, stmt)); + + if (got_row) + { + int levels = svn_sqlite__column_int(stmt, 0); + if (levels >= 0) + levels += relpath_op_depth(lock_relpath); + + if (levels == -1 || levels >= lock_depth) + { + SVN_ERR(svn_sqlite__reset(stmt)); + + err = svn_error_createf( + SVN_ERR_WC_LOCKED, NULL, + _("'%s' is already locked."), + svn_dirent_local_style( + svn_dirent_join(wcroot->abspath, + lock_relpath, + scratch_pool), + scratch_pool)); + return svn_error_createf( + SVN_ERR_WC_LOCKED, err, + _("Working copy '%s' locked."), + svn_dirent_local_style(bt->local_abspath, + scratch_pool)); + } + + break; /* There can't be interesting locks on higher nodes */ + } + + if (!*lock_relpath) + break; + + lock_relpath = svn_relpath_dirname(lock_relpath, scratch_pool); + } + + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_WC_LOCK)); - SVN_ERR(svn_sqlite__bindf(stmt, "isi", pdh->wcroot->wc_id, local_relpath, - (apr_int64_t) levels_to_lock)); + SVN_ERR(svn_sqlite__bindf(stmt, "isi", wcroot->wc_id, + bt->local_relpath, + (apr_int64_t) bt->levels_to_lock)); err = svn_sqlite__insert(NULL, stmt); if (err) return svn_error_createf(SVN_ERR_WC_LOCKED, err, _("Working copy '%s' locked"), - svn_dirent_local_style(local_abspath, + svn_dirent_local_style(bt->local_abspath, scratch_pool)); + /* And finally store that we obtained the lock */ +#ifdef SVN_WC__SINGLE_DB + lock.relpath = apr_pstrdup(wcroot->owned_locks->pool, bt->local_relpath); + lock.levels = bt->levels_to_lock; + APR_ARRAY_PUSH(wcroot->owned_locks, svn_wc__db_wclock_t) = lock; +#else + bt->pdh->locked = TRUE; +#endif + return SVN_NO_ERROR; } +svn_error_t * +svn_wc__db_wclock_obtain(svn_wc__db_t *db, + const char *local_abspath, + int levels_to_lock, + svn_boolean_t steal_lock, + apr_pool_t *scratch_pool) +{ + struct wclock_obtain_baton baton; + + SVN_ERR_ASSERT(levels_to_lock >= -1); + SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); + + SVN_ERR(svn_wc__db_pdh_parse_local_abspath(&baton.pdh, &baton.local_relpath, + db, local_abspath, + svn_sqlite__mode_readwrite, + scratch_pool, scratch_pool)); + VERIFY_USABLE_PDH(baton.pdh); + + if (!steal_lock) +#ifndef SVN_WC__SINGLE_DB + { + if (baton.pdh->locked) + return svn_error_createf(SVN_ERR_WC_LOCKED, NULL, + _("'%s' is already locked."), + svn_dirent_local_style(local_abspath, + scratch_pool)); + } +#else + { + int i; + svn_wc__db_wcroot_t *wcroot = baton.pdh->wcroot; + int depth = relpath_op_depth(baton.local_relpath); + + for (i = 0; i < wcroot->owned_locks->nelts; i++) + { + svn_wc__db_wclock_t lock = APR_ARRAY_IDX(wcroot->owned_locks, + i, svn_wc__db_wclock_t); + + if ((*lock.relpath == '\0' + || svn_relpath_is_ancestor(lock.relpath, baton.local_relpath) + && (lock.levels == -1 + || (lock.levels + relpath_op_depth(lock.relpath)) >= depth))) + { + const char *lock_abspath + = svn_dirent_join(baton.pdh->wcroot->abspath, lock.relpath, + scratch_pool); + + return svn_error_createf(SVN_ERR_WC_LOCKED, NULL, + _("'%s' is already locked via '%s'."), + svn_dirent_local_style(local_abspath, + scratch_pool), + svn_dirent_local_style(lock_abspath, + scratch_pool)); + } + } + } +#endif + + baton.db = db; + baton.local_abspath = local_abspath; + baton.steal_lock = steal_lock; + baton.levels_to_lock = levels_to_lock; + + return svn_error_return( + svn_sqlite__with_transaction(baton.pdh->wcroot->sdb, + wclock_obtain_cb, + &baton, + scratch_pool)); +} + /* */ static svn_error_t * @@ -7674,60 +7923,135 @@ svn_wc__db_wclocked(svn_boolean_t *locke svn_error_t * -svn_wc__db_wclock_remove(svn_wc__db_t *db, - const char *local_abspath, - apr_pool_t *scratch_pool) +svn_wc__db_wclock_release(svn_wc__db_t *db, + const char *local_abspath, + apr_pool_t *scratch_pool) { svn_sqlite__stmt_t *stmt; svn_wc__db_pdh_t *pdh; + const char *local_relpath; +#ifdef SVN_WC__SINGLE_DB + int i; + apr_array_header_t *owned_locks; +#endif - SVN_ERR(get_statement_for_path(&stmt, db, local_abspath, - STMT_DELETE_WC_LOCK, scratch_pool)); - SVN_ERR(svn_sqlite__step_done(stmt)); + SVN_ERR(svn_wc__db_pdh_parse_local_abspath(&pdh, &local_relpath, db, + local_abspath, svn_sqlite__mode_readwrite, + scratch_pool, scratch_pool)); - /* If we've just removed the "physical" lock, we also need to ensure we - don't continue to think we own the lock. */ - pdh = svn_wc__db_pdh_get_or_create(db, local_abspath, FALSE, scratch_pool); - if (pdh) - pdh->locked = FALSE; + VERIFY_USABLE_PDH(pdh); - return SVN_NO_ERROR; -} + /* First check and remove the owns-lock information as failure in + removing the db record implies that we have to steal the lock later. */ +#ifndef SVN_WC__SINGLE_DB + SVN_ERR_ASSERT(*local_relpath == '\0'); + if (!pdh->locked) + return svn_error_createf(SVN_ERR_WC_NOT_LOCKED, NULL, + _("Working copy not locked at '%s'."), + svn_dirent_local_style(local_abspath, + scratch_pool)); + pdh->locked = FALSE; +#else + owned_locks = pdh->wcroot->owned_locks; + for (i = 0; i < owned_locks->nelts; i++) + { + const char *lock_abspath = APR_ARRAY_IDX(owned_locks, i, const char*); + if (strcmp(lock_abspath, local_abspath) == 0) + break; + } -svn_error_t * -svn_wc__db_temp_mark_locked(svn_wc__db_t *db, - const char *local_dir_abspath, - apr_pool_t *scratch_pool) -{ - svn_wc__db_pdh_t *pdh; + if (i >= owned_locks->nelts) + return svn_error_createf(SVN_ERR_WC_NOT_LOCKED, NULL, + _("Working copy not locked at '%s'."), + svn_dirent_local_style(local_abspath, + scratch_pool)); - SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath)); + if (i < owned_locks->nelts) + { + owned_locks->nelts--; - pdh = svn_wc__db_pdh_get_or_create(db, local_dir_abspath, TRUE, - scratch_pool); - pdh->locked = TRUE; + /* Move the last item in the array to the to be deleted place */ + if (owned_locks->nelts > 0) + APR_ARRAY_IDX(owned_locks, i, const char*) = + APR_ARRAY_IDX(owned_locks, owned_locks->nelts, const char*); + } +#endif + + SVN_ERR(svn_sqlite__get_statement(&stmt, pdh->wcroot->sdb, + STMT_DELETE_WC_LOCK)); + + SVN_ERR(svn_sqlite__bindf(stmt, "is", pdh->wcroot->wc_id, local_relpath)); + + SVN_ERR(svn_sqlite__step_done(stmt)); return SVN_NO_ERROR; } - svn_error_t * -svn_wc__db_temp_own_lock(svn_boolean_t *own_lock, - svn_wc__db_t *db, - const char *local_dir_abspath, - apr_pool_t *scratch_pool) +svn_wc__db_wclock_owns_lock(svn_boolean_t *own_lock, + svn_wc__db_t *db, + const char *local_abspath, + svn_boolean_t exact, + apr_pool_t *scratch_pool) { svn_wc__db_pdh_t *pdh; + const char *local_relpath; +#ifdef SVN_WC__SINGLE_DB + apr_array_header_t *owned_locks; + int lock_level, i; +#endif - SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath)); + SVN_ERR(svn_wc__db_pdh_parse_local_abspath(&pdh, &local_relpath, db, + local_abspath, svn_sqlite__mode_readwrite, + scratch_pool, scratch_pool)); - pdh = svn_wc__db_pdh_get_or_create(db, local_dir_abspath, FALSE, - scratch_pool); - *own_lock = (pdh != NULL && pdh->locked); + if (!pdh->wcroot) + return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL, + _("The node '%s' was not found."), + svn_dirent_local_style(local_abspath, + scratch_pool)); - return SVN_NO_ERROR; + VERIFY_USABLE_PDH(pdh); + *own_lock = FALSE; +#ifndef SVN_WC__SINGLE_DB + /* In !SINGLE_DB mode we only allow locking the directories */ + if (*local_relpath == '\0') + *own_lock = (pdh != NULL && pdh->locked); +#else + owned_locks = pdh->wcroot->owned_locks; + lock_level = relpath_op_depth(local_relpath); + + if (exact) + for (i = 0; i < owned_locks->nelts; i++) + { + svn_wc__db_wclock_t lock = APR_ARRAY_IDX(owned_locks, i, + svn_wc__db_wclock_t); + + if (strcmp(lock.relpath, local_relpath) == 0) + { + *own_lock = TRUE; + return SVN_NO_ERROR; + } + } + else + for (i = 0; i < owned_locks->nelts; i++) + { + svn_wc__db_wclock_t lock = APR_ARRAY_IDX(owned_locks, i, + svn_wc__db_wclock_t); + + if ((*lock.relpath == '\0' + || svn_relpath_is_ancestor(lock.relpath, local_relpath)) + && (lock.levels == -1 + || relpath_op_depth(lock.relpath) + lock.levels >= lock_level)) + { + *own_lock = TRUE; + return SVN_NO_ERROR; + } + } +#endif + return SVN_NO_ERROR; } svn_error_t * Modified: subversion/trunk/subversion/libsvn_wc/wc_db.h URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc_db.h?rev=963863&r1=963862&r2=963863&view=diff ============================================================================== --- subversion/trunk/subversion/libsvn_wc/wc_db.h (original) +++ subversion/trunk/subversion/libsvn_wc/wc_db.h Tue Jul 13 21:08:49 2010 @@ -2185,23 +2185,39 @@ svn_wc__db_wq_completed(svn_wc__db_t *db that they still function correctly, even in the new world. 'levels to lock' should not be exposed through the wc-ng APIs at all: users either get to lock the entire tree (rooted at some subdir, of course), or none. + + An infinite depth lock is obtained with LEVELS_TO_LOCK set to -1, but until + we move to a single DB only depth 0 is supported. */ svn_error_t * -svn_wc__db_wclock_set(svn_wc__db_t *db, - const char *local_abspath, - int levels_to_lock, - apr_pool_t *scratch_pool); +svn_wc__db_wclock_obtain(svn_wc__db_t *db, + const char *local_abspath, + int levels_to_lock, + svn_boolean_t steal_lock, + apr_pool_t *scratch_pool); +/* Check if somebody has a wclock on LOCAL_ABSPATH */ svn_error_t * svn_wc__db_wclocked(svn_boolean_t *locked, svn_wc__db_t *db, const char *local_abspath, apr_pool_t *scratch_pool); +/* Release the previously obtained lock on LOCAL_ABSPATH */ svn_error_t * -svn_wc__db_wclock_remove(svn_wc__db_t *db, - const char *local_abspath, - apr_pool_t *scratch_pool); +svn_wc__db_wclock_release(svn_wc__db_t *db, + const char *local_abspath, + apr_pool_t *scratch_pool); + +/* Checks whether DB currently owns a lock to operate on LOCAL_ABSPATH. + If EXACT is TRUE only lock roots are checked. */ +svn_error_t * +svn_wc__db_wclock_owns_lock(svn_boolean_t *own_lock, + svn_wc__db_t *db, + const char *local_abspath, + svn_boolean_t exact, + apr_pool_t *scratch_pool); + /* @defgroup svn_wc__db_temp Various temporary functions during transition @@ -2371,19 +2387,6 @@ svn_wc__db_temp_wcroot_tempdir(const cha apr_pool_t *result_pool, apr_pool_t *scratch_pool); - -svn_error_t * -svn_wc__db_temp_mark_locked(svn_wc__db_t *db, - const char *local_dir_abspath, - apr_pool_t *scratch_pool); - - -svn_error_t * -svn_wc__db_temp_own_lock(svn_boolean_t *own_lock, - svn_wc__db_t *db, - const char *local_dir_abspath, - apr_pool_t *scratch_pool); - svn_error_t * svn_wc__db_temp_op_set_base_incomplete(svn_wc__db_t *db, const char *local_dir_abspath, Modified: subversion/trunk/subversion/libsvn_wc/wc_db_pdh.c URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc_db_pdh.c?rev=963863&r1=963862&r2=963863&view=diff ============================================================================== --- subversion/trunk/subversion/libsvn_wc/wc_db_pdh.c (original) +++ subversion/trunk/subversion/libsvn_wc/wc_db_pdh.c Tue Jul 13 21:08:49 2010 @@ -85,7 +85,7 @@ get_old_version(int *version, return SVN_NO_ERROR; } - +#ifndef SVN_WC__SINGLE_DB /* The filesystem has a directory at LOCAL_RELPATH. Examine the metadata to determine if a *file* was supposed to be there. @@ -129,6 +129,7 @@ determine_obstructed_file(svn_boolean_t return svn_sqlite__reset(stmt); } +#endif /* */ @@ -325,6 +326,12 @@ svn_wc__db_pdh_create_wcroot(svn_wc__db_ (*wcroot)->sdb = sdb; (*wcroot)->wc_id = wc_id; (*wcroot)->format = format; +#ifdef SVN_WC__SINGLE_DB + /* 8 concurrent locks is probably more than a typical wc_ng based svn client + uses. */ + (*wcroot)->owned_locks = apr_array_make(result_pool, 8, + sizeof(svn_wc__db_wclock_t)); +#endif /* SDB will be NULL for pre-NG working copies. We only need to run a cleanup when the SDB is present. */ @@ -689,6 +696,7 @@ svn_wc__db_pdh_parse_local_abspath(svn_w } } +#ifndef SVN_WC__SINGLE_DB if (parent_pdh) { const char *lookfor_relpath = svn_dirent_basename(local_abspath, @@ -710,6 +718,7 @@ svn_wc__db_pdh_parse_local_abspath(svn_w return SVN_NO_ERROR; } } +#endif } /* The PDH is complete. Stash it into DB. */ Modified: subversion/trunk/subversion/libsvn_wc/wc_db_private.h URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc_db_private.h?rev=963863&r1=963862&r2=963863&view=diff ============================================================================== --- subversion/trunk/subversion/libsvn_wc/wc_db_private.h (original) +++ subversion/trunk/subversion/libsvn_wc/wc_db_private.h Tue Jul 13 21:08:49 2010 @@ -56,11 +56,20 @@ struct svn_wc__db_t { apr_pool_t *state_pool; }; +/* Hold information about an owned lock */ +typedef struct svn_wc__db_wclock_t +{ + /* Relative path of the lock root */ + const char *relpath; + /* Number of levels locked (0 for infinity) */ + int levels; +} svn_wc__db_wclock_t; + /** Hold information about a WCROOT. * * This structure is referenced by all per-directory handles underneath it. */ -typedef struct { +typedef struct svn_wc__db_wcroot_t { /* Location of this wcroot in the filesystem. */ const char *abspath; @@ -75,6 +84,12 @@ typedef struct { format has not (yet) been determined, this will be UNKNOWN_FORMAT. */ int format; +#ifdef SVN_WC__SINGLE_DB + /* Array of svn_wc__db_wclock_t fields (not pointers!). + Typically just one or two locks maximum. */ + apr_array_header_t *owned_locks; +#endif + } svn_wc__db_wcroot_t; /** Pristine Directory Handle @@ -83,6 +98,7 @@ typedef struct { * a given working copy directory. */ typedef struct svn_wc__db_pdh_t { +#ifndef SVN_WC__SINGLE_DB /* This (versioned) working copy directory is obstructing what *should* be a file in the parent directory (according to its metadata). @@ -90,6 +106,7 @@ typedef struct svn_wc__db_pdh_t { ### obstruction is only possible with per-dir wc.db databases. */ svn_boolean_t obstructed_file; +#endif /* The absolute path to this working copy directory. */ const char *local_abspath; @@ -100,8 +117,10 @@ typedef struct svn_wc__db_pdh_t { /* The parent directory's per-dir information. */ struct svn_wc__db_pdh_t *parent; +#ifndef SVN_WC__SINGLE_DB /* Whether this process owns a write-lock on this directory. */ svn_boolean_t locked; +#endif /* Hold onto the old-style access baton that corresponds to this PDH. */ svn_wc_adm_access_t *adm_access;