24
23
from bzrlib.errors import LockError, ReadOnlyError
25
24
from bzrlib.osutils import file_iterator, safe_unicode
26
25
from bzrlib.symbol_versioning import *
27
from bzrlib.trace import mutter, note
26
from bzrlib.symbol_versioning import deprecated_method, zero_eight
27
from bzrlib.trace import mutter
28
28
import bzrlib.transactions as transactions
30
# XXX: The tracking here of lock counts and whether the lock is held is
31
# somewhat redundant with what's done in LockDir; the main difference is that
32
# LockableFiles permits reentrancy.
34
31
class LockableFiles(object):
35
"""Object representing a set of related files locked within the same scope.
37
These files are used by a WorkingTree, Repository or Branch, and should
38
generally only be touched by that object.
40
LockableFiles also provides some policy on top of Transport for encoding
41
control files as utf-8.
43
LockableFiles manage a lock count and can be locked repeatedly by
44
a single caller. (The underlying lock implementation generally does not
47
Instances of this class are often called control_files.
49
This object builds on top of a Transport, which is used to actually write
50
the files to disk, and an OSLock or LockDir, which controls how access to
51
the files is controlled. The particular type of locking used is set when
52
the object is constructed. In older formats OSLocks are used everywhere.
53
in newer formats a LockDir is used for Repositories and Branches, and
54
OSLocks for the local filesystem.
32
"""Object representing a set of files locked within the same scope
38
If _lock_mode is true, a positive count of the number of times the
39
lock has been taken *by this process*. Others may have compatible
43
Lock object from bzrlib.lock.
57
# _lock_mode: None, or 'r' or 'w'
59
# _lock_count: If _lock_mode is true, a positive count of the number of
60
# times the lock has been taken *by this process*.
62
49
# If set to False (by a plugin, etc) BzrBranch will not set the
63
50
# mode on created files or directories
64
51
_set_file_mode = True
65
52
_set_dir_mode = True
67
def __init__(self, transport, lock_name, lock_class):
68
"""Create a LockableFiles group
70
:param transport: Transport pointing to the directory holding the
71
control files and lock.
72
:param lock_name: Name of the lock guarding these files.
73
:param lock_class: Class of lock strategy to use: typically
74
either LockDir or TransportLock.
54
def __init__(self, transport, lock_name):
76
55
object.__init__(self)
77
56
self._transport = transport
78
57
self.lock_name = lock_name
79
58
self._transaction = None
81
self._lock_mode = None
83
esc_name = self._escape(lock_name)
84
self._lock = lock_class(transport, esc_name,
85
file_modebits=self._file_mode,
86
dir_modebits=self._dir_mode)
88
def create_lock(self):
91
This should normally be called only when the LockableFiles directory
92
is first created on disk.
97
return '%s(%r)' % (self.__class__.__name__,
100
return 'LockableFiles(%s, %s)' % (self.lock_name, self._transport.base)
102
61
def __del__(self):
62
if self._lock_mode or self._lock:
104
63
# XXX: This should show something every time, and be suitable for
105
64
# headless operation and embedding
106
65
from warnings import warn
207
166
# TODO: Upgrade locking to support using a Transport,
208
167
# and potentially a remote locking protocol
209
168
if self._lock_mode:
210
if self._lock_mode != 'w' or not self.get_transaction().writeable():
169
if self._lock_mode != 'w':
211
170
raise ReadOnlyError(self)
212
171
self._lock_count += 1
214
self._lock.lock_write()
215
#note('write locking %s', self)
216
#traceback.print_stack()
173
self._lock = self._transport.lock_write(
174
self._escape(self.lock_name))
217
175
self._lock_mode = 'w'
218
176
self._lock_count = 1
219
self._set_transaction(transactions.WriteTransaction())
177
self._set_transaction(transactions.PassThroughTransaction())
221
179
def lock_read(self):
222
180
# mutter("lock read: %s (%s)", self, self._lock_count)
237
194
def unlock(self):
238
195
# mutter("unlock: %s (%s)", self, self._lock_count)
239
196
if not self._lock_mode:
240
raise errors.LockNotHeld(self)
197
raise errors.BranchNotLocked(self)
241
198
if self._lock_count > 1:
242
199
self._lock_count -= 1
244
#note('unlocking %s', self)
245
#traceback.print_stack()
246
201
self._finish_transaction()
247
202
self._lock.unlock()
248
204
self._lock_mode = self._lock_count = None
251
"""Return true if this LockableFiles group is locked"""
252
return self._lock_count >= 1
254
206
def get_transaction(self):
255
207
"""Return the current active transaction.
277
229
transaction = self._transaction
278
230
self._transaction = None
279
231
transaction.finish()
282
class TransportLock(object):
283
"""Locking method which uses transport-dependent locks.
285
On the local filesystem these transform into OS-managed locks.
287
These do not guard against concurrent access via different
290
This is suitable for use only in WorkingTrees (which are at present
293
def __init__(self, transport, escaped_name, file_modebits, dir_modebits):
294
self._transport = transport
295
self._escaped_name = escaped_name
296
self._file_modebits = file_modebits
297
self._dir_modebits = dir_modebits
299
def lock_write(self):
300
self._lock = self._transport.lock_write(self._escaped_name)
303
self._lock = self._transport.lock_read(self._escaped_name)
310
"""Create lock mechanism"""
311
# for old-style locks, create the file now
312
self._transport.put(self._escaped_name, StringIO(),
313
mode=self._file_modebits)