[Pkg-bazaar-commits] ./bzr/unstable r353: - Per-branch locks in read and write modes.
Martin Pool
mbp at sourcefrog.net
Fri Apr 10 07:43:44 UTC 2009
------------------------------------------------------------
revno: 353
committer: Martin Pool <mbp at sourcefrog.net>
timestamp: Thu 2005-05-05 13:34:34 +1000
message:
- Per-branch locks in read and write modes.
(Not on Windows yet.)
modified:
NEWS
bzrlib/branch.py
bzrlib/commands.py
-------------- next part --------------
=== modified file 'NEWS'
--- a/NEWS 2005-05-05 02:46:17 +0000
+++ b/NEWS 2005-05-05 03:34:34 +0000
@@ -12,6 +12,9 @@
* Add ``.*.swp .*.tmp *,v`` to default ignore patterns.
+ * Per-branch locks keyed on ``.bzr/branch-lock``, available in
+ either read or write mode.
+
TESTING:
* Converted black-box test suites from Bourne shell into Python;
=== modified file 'bzrlib/branch.py'
--- a/bzrlib/branch.py 2005-05-05 02:19:16 +0000
+++ b/bzrlib/branch.py 2005-05-05 03:34:34 +0000
@@ -76,7 +76,9 @@
base
Base directory of the branch.
"""
- def __init__(self, base, init=False, find_root=True):
+ _lockmode = None
+
+ def __init__(self, base, init=False, find_root=True, lock_mode='w'):
"""Create new branch object at a particular location.
base -- Base directory for the branch.
@@ -103,6 +105,7 @@
['use "bzr init" to initialize a new working tree',
'current bzr can only operate from top-of-tree'])
self._check_format()
+ self.lock(lock_mode)
self.text_store = ImmutableStore(self.controlfilename('text-store'))
self.revision_store = ImmutableStore(self.controlfilename('revision-store'))
@@ -116,6 +119,46 @@
__repr__ = __str__
+
+ def lock(self, mode='w'):
+ """Lock the on-disk branch, excluding other processes."""
+ try:
+ import fcntl
+
+ if mode == 'w':
+ lm = fcntl.LOCK_EX
+ om = os.O_WRONLY | os.O_CREAT
+ elif mode == 'r':
+ lm = fcntl.LOCK_SH
+ om = os.O_RDONLY
+ else:
+ raise BzrError("invalid locking mode %r" % mode)
+
+ lockfile = os.open(self.controlfilename('branch-lock'), om)
+ fcntl.lockf(lockfile, lm)
+ def unlock(self):
+ fcntl.lockf(lockfile, fcntl.LOCK_UN)
+ os.close(lockfile)
+ self._lockmode = None
+ self.unlock = unlock
+ self._lockmode = mode
+ except ImportError:
+ warning("please write a locking method for platform %r" % sys.platform)
+ def unlock(self):
+ self._lockmode = None
+ self.unlock = unlock
+ self._lockmode = mode
+
+
+ def _need_readlock(self):
+ if self._lockmode not in ['r', 'w']:
+ raise BzrError('need read lock on branch, only have %r' % self._lockmode)
+
+ def _need_writelock(self):
+ if self._lockmode not in ['w']:
+ raise BzrError('need write lock on branch, only have %r' % self._lockmode)
+
+
def abspath(self, name):
"""Return absolute filename for something in the branch"""
return os.path.join(self.base, name)
@@ -174,7 +217,8 @@
for d in ('text-store', 'inventory-store', 'revision-store'):
os.mkdir(self.controlfilename(d))
for f in ('revision-history', 'merged-patches',
- 'pending-merged-patches', 'branch-name'):
+ 'pending-merged-patches', 'branch-name',
+ 'branch-lock'):
self.controlfile(f, 'w').write('')
mutter('created control directory in ' + self.base)
Inventory().write_xml(self.controlfile('inventory','w'))
@@ -201,6 +245,7 @@
def read_working_inventory(self):
"""Read the working inventory."""
+ self._need_readlock()
before = time.time()
# ElementTree does its own conversion from UTF-8, so open in
# binary.
@@ -216,6 +261,7 @@
That is to say, the inventory describing changes underway, that
will be committed to the next revision.
"""
+ self._need_writelock()
## TODO: factor out to atomicfile? is rename safe on windows?
## TODO: Maybe some kind of clean/dirty marker on inventory?
tmpfname = self.controlfilename('inventory.tmp')
@@ -275,6 +321,7 @@
Traceback (most recent call last):
BzrError: ('cannot add: not a regular file or directory: nothere', [])
"""
+ self._need_writelock()
# TODO: Re-adding a file that is removed in the working copy
# should probably put it back with the previous ID.
@@ -315,6 +362,7 @@
def print_file(self, file, revno):
"""Print `file` to stdout."""
+ self._need_readlock()
tree = self.revision_tree(self.lookup_revision(revno))
# use inventory as it was in that revision
file_id = tree.inventory.path2id(file)
@@ -361,6 +409,7 @@
"""
## TODO: Normalize names
## TODO: Remove nested loops; better scalability
+ self._need_writelock()
if isinstance(files, types.StringTypes):
files = [files]
@@ -427,6 +476,7 @@
timestamp -- if not None, seconds-since-epoch for a
postdated/predated commit.
"""
+ self._need_writelock()
## TODO: Show branch names
@@ -596,6 +646,7 @@
def get_revision(self, revision_id):
"""Return the Revision object for a named revision"""
+ self._need_readlock()
r = Revision.read_xml(self.revision_store[revision_id])
assert r.revision_id == revision_id
return r
@@ -607,12 +658,14 @@
TODO: Perhaps for this and similar methods, take a revision
parameter which can be either an integer revno or a
string hash."""
+ self._need_readlock()
i = Inventory.read_xml(self.inventory_store[inventory_id])
return i
def get_revision_inventory(self, revision_id):
"""Return inventory of a past revision."""
+ self._need_readlock()
if revision_id == None:
return Inventory()
else:
@@ -625,6 +678,7 @@
>>> ScratchBranch().revision_history()
[]
"""
+ self._need_readlock()
return [l.rstrip('\r\n') for l in self.controlfile('revision-history', 'r').readlines()]
@@ -674,7 +728,7 @@
`revision_id` may be None for the null revision, in which case
an `EmptyTree` is returned."""
-
+ self._need_readlock()
if revision_id == None:
return EmptyTree()
else:
@@ -714,6 +768,7 @@
"""Write out human-readable log of commits to this branch
utc -- If true, show dates in universal time, not local time."""
+ self._need_readlock()
## TODO: Option to choose either original, utc or local timezone
revno = 1
precursor = None
@@ -761,7 +816,8 @@
"""Rename one file.
This can change the directory or the filename or both.
- """
+ """
+ self._need_writelock()
tree = self.working_tree()
inv = tree.inventory
if not tree.has_filename(from_rel):
@@ -816,6 +872,7 @@
Note that to_name is only the last component of the new name;
this doesn't change the directory.
"""
+ self._need_writelock()
## TODO: Option to move IDs only
assert not isinstance(from_paths, basestring)
tree = self.working_tree()
@@ -885,6 +942,7 @@
TODO: Get state for single files.
"""
+ self._need_readlock()
# We have to build everything into a list first so that it can
# sorted by name, incorporating all the different sources.
=== modified file 'bzrlib/commands.py'
--- a/bzrlib/commands.py 2005-05-05 02:59:39 +0000
+++ b/bzrlib/commands.py 2005-05-05 03:34:34 +0000
@@ -420,7 +420,7 @@
"""
takes_options = ['timezone', 'verbose']
def run(self, timezone='original', verbose=False):
- Branch('.').write_log(show_timezone=timezone, verbose=verbose)
+ Branch('.', lock_mode='r').write_log(show_timezone=timezone, verbose=verbose)
class cmd_ls(Command):
More information about the Pkg-bazaar-commits
mailing list