Rev 5325: Implement break-lock for config files. in file:///home/vila/src/bzr/bugs/525571-lock-bazaar-conf-files/
Vincent Ladeuil
v.ladeuil+lp at free.fr
Tue Jun 29 15:55:53 BST 2010
At file:///home/vila/src/bzr/bugs/525571-lock-bazaar-conf-files/
------------------------------------------------------------
revno: 5325
revision-id: v.ladeuil+lp at free.fr-20100629145553-gay36b3uuodjbya7
parent: v.ladeuil+lp at free.fr-20100629091911-uuzfksmfm0z9oxtq
committer: Vincent Ladeuil <v.ladeuil+lp at free.fr>
branch nick: 525571-lock-bazaar-conf-files
timestamp: Tue 2010-06-29 16:55:53 +0200
message:
Implement break-lock for config files.
* bzrlib/tests/blackbox/test_break_lock.py:
(TestConfigBreakLock): Test break-lock and broken lock behaviors
for config files.
* bzrlib/errors.py:
(NoLockDir): New error.
* bzrlib/config.py:
(LockableConfig.break_lock): Break the lock but raise NoLock if
the 'lock' directory doesn't exit.
* bzrlib/builtins.py:
(cmd_break_lock.run): Can now break config locks.
-------------- next part --------------
=== modified file 'NEWS'
--- a/NEWS 2010-06-23 08:14:21 +0000
+++ b/NEWS 2010-06-29 14:55:53 +0000
@@ -36,6 +36,13 @@
New Features
************
+* ``bzr break-lock`` will now break configuration locks if the location
+ provided contains a broken lock. (Vincent Ladeuil, #591215)
+
+* ``bzrlib.config.LockableConfig`` is a base class for config files that
+ needs to be protected against multiple writers.
+ (Vincent Ladeuil, #591215)
+
* Support ``--directory`` option for a number of additional commands:
conflicts, merge-directive, missing, resolve, shelve, switch,
unshelve, whoami. (Martin von Gagern, #527878)
@@ -54,6 +61,9 @@
or pull location in locations.conf or branch.conf.
(Gordon Tyler, #534787)
+* Configuration files in ``${BZR_HOME}`` are now protected
+ against concurrent writers. (Vincent Ladeuil, #525571)
+
* Ensure that wrong path specifications in ``BZR_PLUGINS_AT`` display
proper error messages. (Vincent Ladeuil, #591215)
=== modified file 'bzrlib/builtins.py'
--- a/bzrlib/builtins.py 2010-06-17 08:53:15 +0000
+++ b/bzrlib/builtins.py 2010-06-29 14:55:53 +0000
@@ -4830,7 +4830,8 @@
class cmd_break_lock(Command):
- __doc__ = """Break a dead lock on a repository, branch or working directory.
+ __doc__ = """\
+ Break a dead lock on a repository, branch, working directory or config file.
CAUTION: Locks should only be broken when you are sure that the process
holding the lock has been stopped.
@@ -4841,17 +4842,24 @@
:Examples:
bzr break-lock
bzr break-lock bzr+ssh://example.com/bzr/foo
+ bzr break-lock ~/.bazaar
"""
takes_args = ['location?']
def run(self, location=None, show=False):
if location is None:
location = u'.'
- control, relpath = bzrdir.BzrDir.open_containing(location)
try:
- control.break_lock()
- except NotImplementedError:
- pass
+ # Config locks first
+ conf = config.LockableConfig(lambda : location)
+ conf.break_lock()
+ except errors.NoLockDir:
+ # Then wt, branch, repo, etc.
+ control, relpath = bzrdir.BzrDir.open_containing(location)
+ try:
+ control.break_lock()
+ except NotImplementedError:
+ pass
class cmd_wait_until_signalled(Command):
=== modified file 'bzrlib/config.py'
--- a/bzrlib/config.py 2010-06-29 09:19:11 +0000
+++ b/bzrlib/config.py 2010-06-29 14:55:53 +0000
@@ -509,18 +509,31 @@
If several processes try to write the config file, the accesses need to be
serialized.
"""
+ lock_name = 'lock'
+
+ def __init__(self, get_filename):
+ super(LockableConfig, self).__init__(get_filename)
+ self.dir = osutils.dirname(osutils.safe_unicode(self._get_filename()))
+ self.transport = transport.get_transport(self.dir)
+ self.lock = None
def lock_write(self, token=None):
- conf_dir = os.path.dirname(self._get_filename())
- ensure_config_dir_exists(conf_dir)
- self.transport = transport.get_transport(conf_dir)
- self._lock = lockdir.LockDir(self.transport, 'lock')
+ if self.lock is None:
+ ensure_config_dir_exists(self.dir)
+ self._lock = lockdir.LockDir(self.transport, self.lock_name)
self._lock.lock_write(token)
return lock.LogicalLockResult(self.unlock)
def unlock(self):
self._lock.unlock()
+ def break_lock(self):
+ if not self.transport.has(self.lock_name):
+ raise errors.NoLockDir(self.dir)
+ if self.lock is None:
+ self.lock = lockdir.LockDir(self.transport, self.lock_name)
+ self.lock.break_lock()
+
@needs_write_lock
def _write_config_file(self):
self._write_config_file_atomic()
=== modified file 'bzrlib/errors.py'
--- a/bzrlib/errors.py 2010-06-17 11:52:13 +0000
+++ b/bzrlib/errors.py 2010-06-29 14:55:53 +0000
@@ -1083,6 +1083,16 @@
self.lock = lock
+class NoLockDir(LockError):
+
+ _fmt = "No lock directory (hence no lock) in: %(path)s"
+
+ internal_error = False
+
+ def __init__(self, path):
+ self.path = path
+
+
class TokenLockingNotSupported(LockError):
_fmt = "The object %(obj)s does not support token specifying a token when locking."
=== modified file 'bzrlib/tests/blackbox/test_break_lock.py'
--- a/bzrlib/tests/blackbox/test_break_lock.py 2010-06-11 07:32:12 +0000
+++ b/bzrlib/tests/blackbox/test_break_lock.py 2010-06-29 14:55:53 +0000
@@ -18,17 +18,17 @@
import os
-import bzrlib
from bzrlib import (
+ branch,
+ bzrdir,
+ config,
errors,
lockdir,
+ tests,
)
-from bzrlib.branch import Branch
-from bzrlib.bzrdir import BzrDir
-from bzrlib.tests import TestCaseWithTransport
-
-
-class TestBreakLock(TestCaseWithTransport):
+
+
+class TestBreakLock(tests.TestCaseWithTransport):
# General principal for break-lock: All the elements that might be locked
# by a bzr operation on PATH, are candidates that break-lock may unlock.
@@ -52,14 +52,14 @@
'repo/',
'repo/branch/',
'checkout/'])
- bzrlib.bzrdir.BzrDir.create('master-repo').create_repository()
- self.master_branch = bzrlib.bzrdir.BzrDir.create_branch_convenience(
+ bzrdir.BzrDir.create('master-repo').create_repository()
+ self.master_branch = bzrdir.BzrDir.create_branch_convenience(
'master-repo/master-branch')
- bzrlib.bzrdir.BzrDir.create('repo').create_repository()
- local_branch = bzrlib.bzrdir.BzrDir.create_branch_convenience('repo/branch')
+ bzrdir.BzrDir.create('repo').create_repository()
+ local_branch = bzrdir.BzrDir.create_branch_convenience('repo/branch')
local_branch.bind(self.master_branch)
- checkoutdir = bzrlib.bzrdir.BzrDir.create('checkout')
- bzrlib.branch.BranchReferenceFormat().initialize(
+ checkoutdir = bzrdir.BzrDir.create('checkout')
+ branch.BranchReferenceFormat().initialize(
checkoutdir, target_branch=local_branch)
self.wt = checkoutdir.create_workingtree()
@@ -73,7 +73,7 @@
# however, we dont test breaking the working tree because we
# cannot accurately do so right now: the dirstate lock is held
# by an os lock, and we need to spawn a separate process to lock it
- # thne kill -9 it.
+ # then kill -9 it.
# sketch of test:
# lock most of the dir:
self.wt.branch.lock_write()
@@ -82,22 +82,34 @@
# we need 5 yes's - wt, branch, repo, bound branch, bound repo.
self.run_bzr('break-lock checkout', stdin="y\ny\ny\ny\n")
# a new tree instance should be lockable
- branch = bzrlib.branch.Branch.open('checkout')
- branch.lock_write()
- branch.unlock()
+ b = branch.Branch.open('checkout')
+ b.lock_write()
+ b.unlock()
# and a new instance of the master branch
- mb = branch.get_master_branch()
+ mb = b.get_master_branch()
mb.lock_write()
mb.unlock()
self.assertRaises(errors.LockBroken, self.wt.unlock)
self.assertRaises(errors.LockBroken, self.master_branch.unlock)
-class TestBreakLockOldBranch(TestCaseWithTransport):
+class TestBreakLockOldBranch(tests.TestCaseWithTransport):
def test_break_lock_format_5_bzrdir(self):
# break lock on a format 5 bzrdir should just return
- self.make_branch_and_tree('foo', format=bzrlib.bzrdir.BzrDirFormat5())
+ self.make_branch_and_tree('foo', format=bzrdir.BzrDirFormat5())
out, err = self.run_bzr('break-lock foo')
self.assertEqual('', out)
self.assertEqual('', err)
+
+
+class TestConfigBreakLock(tests.TestCaseWithTransport):
+
+ def test_break_lock(self):
+ def config_file_name():
+ return './my.conf'
+ self.build_tree_contents([(config_file_name(), '[DEFAULT]\none=1\n')])
+ c1 = config.LockableConfig(config_file_name)
+ c1.lock_write()
+ self.run_bzr('break-lock %s' % config_file_name(), stdin="y\n")
+ self.assertRaises(errors.LockBroken, c1.unlock)
=== modified file 'bzrlib/tests/test_config.py'
--- a/bzrlib/tests/test_config.py 2010-06-29 09:19:11 +0000
+++ b/bzrlib/tests/test_config.py 2010-06-29 14:55:53 +0000
@@ -497,7 +497,7 @@
# Collect the thread after the test
self.addCleanup(t1.join)
# Be ready to unblock the thread if the test goes wrong
- self.addCleanup(writing_done.set)
+ self.addCleanup(after_writing.set)
t1.start()
before_writing.wait()
self.assertTrue(c1._lock.is_held)
More information about the bazaar-commits
mailing list