~bzr-pqm/bzr/bzr.dev

5752.3.8 by John Arbash Meinel
Merge bzr.dev 5764 to resolve release-notes (aka NEWS) conflicts
1
# Copyright (C) 2005-2011 Canonical Ltd
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
2
#
1185.65.22 by Robert Collins
lockable_files was extracted from branch.py - give it a copyright statement
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
7
#
1185.65.22 by Robert Collins
lockable_files was extracted from branch.py - give it a copyright statement
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
12
#
1185.65.22 by Robert Collins
lockable_files was extracted from branch.py - give it a copyright statement
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1185.65.22 by Robert Collins
lockable_files was extracted from branch.py - give it a copyright statement
16
3535.5.1 by John Arbash Meinel
cleanup a few imports to be lazily loaded.
17
from bzrlib.lazy_import import lazy_import
18
lazy_import(globals(), """
19
import warnings
20
21
from bzrlib import (
3468.3.3 by Martin Pool
Fix import
22
    counted_lock,
3535.5.1 by John Arbash Meinel
cleanup a few imports to be lazily loaded.
23
    errors,
4509.3.25 by Martin Pool
Add an option for unlock errors to be non-fatal
24
    lock,
3535.5.1 by John Arbash Meinel
cleanup a few imports to be lazily loaded.
25
    osutils,
26
    transactions,
27
    urlutils,
28
    )
29
""")
30
31
from bzrlib.decorators import (
4634.85.9 by Andrew Bennetts
Add some experimental decorators: @only_raises(..) and @cleanup_method.
32
    only_raises,
3535.5.1 by John Arbash Meinel
cleanup a few imports to be lazily loaded.
33
    )
1685.1.45 by John Arbash Meinel
Moved url functions into bzrlib.urlutils
34
1185.65.10 by Robert Collins
Rename Controlfiles to LockableFiles.
35
1553.5.41 by Martin Pool
Add new LockableFiles.LockDirStrategy; not used yet
36
# XXX: The tracking here of lock counts and whether the lock is held is
37
# somewhat redundant with what's done in LockDir; the main difference is that
38
# LockableFiles permits reentrancy.
1185.65.27 by Robert Collins
Tweak storage towards mergability.
39
4041.1.5 by Michael Hudson
review comments
40
class _LockWarner(object):
41
    """Hold a counter for a lock and warn if GCed while the count is >= 1.
4041.1.4 by Michael Hudson
this makes more sense
42
43
    This is separate from LockableFiles because putting a __del__ on
44
    LockableFiles can result in uncollectable cycles.
45
    """
46
47
    def __init__(self, repr):
48
        self.lock_count = 0
4041.1.1 by Michael Hudson
this is terrible but it works
49
        self.repr = repr
50
51
    def __del__(self):
4041.1.4 by Michael Hudson
this makes more sense
52
        if self.lock_count >= 1:
4041.1.5 by Michael Hudson
review comments
53
            # There should have been a try/finally to unlock this.
4041.1.1 by Michael Hudson
this is terrible but it works
54
            warnings.warn("%r was gc'd while locked" % self.repr)
4041.1.3 by Michael Hudson
_this_ works
55
4041.1.1 by Michael Hudson
this is terrible but it works
56
1185.66.3 by Aaron Bentley
Renamed ControlFiles to LockableFiles
57
class LockableFiles(object):
1553.5.38 by Martin Pool
More explanation for LockableFiles
58
    """Object representing a set of related files locked within the same scope.
59
4570.3.1 by Martin Pool
Update LockableFiles docstring
60
    This coordinates access to the lock along with providing a transaction.
1553.5.38 by Martin Pool
More explanation for LockableFiles
61
1553.5.39 by Martin Pool
More lock docs
62
    LockableFiles manage a lock count and can be locked repeatedly by
63
    a single caller.  (The underlying lock implementation generally does not
64
    support this.)
65
1553.5.38 by Martin Pool
More explanation for LockableFiles
66
    Instances of this class are often called control_files.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
67
68
    This class is now deprecated; code should move to using the Transport
69
    directly for file operations and using the lock or CountedLock for
3407.2.9 by Martin Pool
doc
70
    locking.
3407.2.19 by Martin Pool
CountedLock should manage lock tokens
71
    
72
    :ivar _lock: The real underlying lock (e.g. a LockDir)
73
    :ivar _counted_lock: A lock decorated with a semaphore, so that it 
74
        can be re-entered.
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
75
    """
1185.65.10 by Robert Collins
Rename Controlfiles to LockableFiles.
76
1553.5.47 by Martin Pool
cleanup LockableFiles
77
    # _lock_mode: None, or 'r' or 'w'
1553.5.43 by Martin Pool
Get LockableFiles tests running against LockDir
78
1553.5.47 by Martin Pool
cleanup LockableFiles
79
    # _lock_count: If _lock_mode is true, a positive count of the number of
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
80
    # times the lock has been taken *by this process*.
81
1553.5.63 by Martin Pool
Lock type is now mandatory for LockableFiles constructor
82
    def __init__(self, transport, lock_name, lock_class):
1553.5.43 by Martin Pool
Get LockableFiles tests running against LockDir
83
        """Create a LockableFiles group
84
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
85
        :param transport: Transport pointing to the directory holding the
1553.5.43 by Martin Pool
Get LockableFiles tests running against LockDir
86
            control files and lock.
87
        :param lock_name: Name of the lock guarding these files.
1553.5.47 by Martin Pool
cleanup LockableFiles
88
        :param lock_class: Class of lock strategy to use: typically
89
            either LockDir or TransportLock.
1553.5.43 by Martin Pool
Get LockableFiles tests running against LockDir
90
        """
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
91
        self._transport = transport
92
        self.lock_name = lock_name
93
        self._transaction = None
1553.5.47 by Martin Pool
cleanup LockableFiles
94
        self._lock_mode = None
4041.1.5 by Michael Hudson
review comments
95
        self._lock_warner = _LockWarner(repr(self))
1685.1.43 by John Arbash Meinel
Bug in lockable files when _find_mode throws
96
        self._find_modes()
1553.5.43 by Martin Pool
Get LockableFiles tests running against LockDir
97
        esc_name = self._escape(lock_name)
1608.2.1 by Martin Pool
[merge] Storage filename escaping
98
        self._lock = lock_class(transport, esc_name,
1553.5.59 by Martin Pool
Pass file/mode bits through to creation of lock files/dirs
99
                                file_modebits=self._file_mode,
100
                                dir_modebits=self._dir_mode)
3468.3.3 by Martin Pool
Fix import
101
        self._counted_lock = counted_lock.CountedLock(self._lock)
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
102
1553.5.60 by Martin Pool
New LockableFiles.create_lock() method
103
    def create_lock(self):
104
        """Create the lock.
105
106
        This should normally be called only when the LockableFiles directory
107
        is first created on disk.
108
        """
1666.1.4 by Robert Collins
* 'Metadir' is now the default disk format. This improves behaviour in
109
        self._lock.create(mode=self._dir_mode)
1553.5.60 by Martin Pool
New LockableFiles.create_lock() method
110
1553.5.53 by Martin Pool
Add LockableFiles __repr__
111
    def __repr__(self):
112
        return '%s(%r)' % (self.__class__.__name__,
113
                           self._transport)
1185.80.2 by John Arbash Meinel
Traced double locking code to WorkingTree creating its own control files.
114
    def __str__(self):
115
        return 'LockableFiles(%s, %s)' % (self.lock_name, self._transport.base)
116
1687.1.6 by Robert Collins
Extend LockableFiles to support break_lock() calls.
117
    def break_lock(self):
118
        """Break the lock of this lockable files group if it is held.
119
120
        The current ui factory will be used to prompt for user conformation.
121
        """
122
        self._lock.break_lock()
123
1185.69.2 by John Arbash Meinel
Changed LockableFiles to take the root directory directly. Moved mode information into LockableFiles instead of Branch
124
    def _escape(self, file_or_path):
3834.2.2 by Martin Pool
Deprecated LockableFiles._escape
125
        """DEPRECATED: Do not use outside this class"""
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
126
        if not isinstance(file_or_path, basestring):
127
            file_or_path = '/'.join(file_or_path)
128
        if file_or_path == '':
1185.69.2 by John Arbash Meinel
Changed LockableFiles to take the root directory directly. Moved mode information into LockableFiles instead of Branch
129
            return u''
3535.5.1 by John Arbash Meinel
cleanup a few imports to be lazily loaded.
130
        return urlutils.escape(osutils.safe_unicode(file_or_path))
1185.69.2 by John Arbash Meinel
Changed LockableFiles to take the root directory directly. Moved mode information into LockableFiles instead of Branch
131
132
    def _find_modes(self):
3416.2.1 by Martin Pool
Add BzrDir._get_file_mode and _get_dir_mode
133
        """Determine the appropriate modes for files and directories.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
134
4570.3.4 by Martin Pool
Doc
135
        :deprecated: Replaced by BzrDir._find_creation_modes.
3416.2.1 by Martin Pool
Add BzrDir._get_file_mode and _get_dir_mode
136
        """
3407.2.19 by Martin Pool
CountedLock should manage lock tokens
137
        # XXX: The properties created by this can be removed or deprecated
138
        # once all the _get_text_store methods etc no longer use them.
139
        # -- mbp 20080512
1185.69.2 by John Arbash Meinel
Changed LockableFiles to take the root directory directly. Moved mode information into LockableFiles instead of Branch
140
        try:
1534.4.28 by Robert Collins
first cut at merge from integration.
141
            st = self._transport.stat('.')
142
        except errors.TransportNotPossible:
1185.69.2 by John Arbash Meinel
Changed LockableFiles to take the root directory directly. Moved mode information into LockableFiles instead of Branch
143
            self._dir_mode = 0755
144
            self._file_mode = 0644
145
        else:
3107.2.1 by John Arbash Meinel
Fix LockableFiles to not use modes that allow the user to write to things they create.
146
            # Check the directory mode, but also make sure the created
147
            # directories and files are read-write for this user. This is
148
            # mostly a workaround for filesystems which lie about being able to
149
            # write to a directory (cygwin & win32)
150
            self._dir_mode = (st.st_mode & 07777) | 00700
1185.69.2 by John Arbash Meinel
Changed LockableFiles to take the root directory directly. Moved mode information into LockableFiles instead of Branch
151
            # Remove the sticky and execute bits for files
152
            self._file_mode = self._dir_mode & ~07111
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
153
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
154
    def leave_in_place(self):
155
        """Set this LockableFiles to not clear the physical lock on unlock."""
156
        self._lock.leave_in_place()
157
158
    def dont_leave_in_place(self):
159
        """Set this LockableFiles to clear the physical lock on unlock."""
160
        self._lock.dont_leave_in_place()
161
2279.7.1 by Andrew Bennetts
``LockableFiles.lock_write()`` now accepts a ``token`` keyword argument, so that
162
    def lock_write(self, token=None):
163
        """Lock this group of files for writing.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
164
2279.7.1 by Andrew Bennetts
``LockableFiles.lock_write()`` now accepts a ``token`` keyword argument, so that
165
        :param token: if this is already locked, then lock_write will fail
166
            unless the token matches the existing lock.
167
        :returns: a token if this instance supports tokens, otherwise None.
168
        :raises TokenLockingNotSupported: when a token is given but this
169
            instance doesn't support using token locks.
170
        :raises MismatchedToken: if the specified token doesn't match the token
171
            of the existing lock.
2018.5.145 by Andrew Bennetts
Add a brief explanation of what tokens are used for to lock_write docstrings.
172
173
        A token should be passed in if you know that you have locked the object
174
        some other way, and need to synchronise this object's state with that
175
        fact.
2279.7.1 by Andrew Bennetts
``LockableFiles.lock_write()`` now accepts a ``token`` keyword argument, so that
176
        """
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
177
        # TODO: Upgrade locking to support using a Transport,
178
        # and potentially a remote locking protocol
179
        if self._lock_mode:
1594.2.22 by Robert Collins
Ensure that lockable files calls finish() on transactions.:
180
            if self._lock_mode != 'w' or not self.get_transaction().writeable():
1694.2.6 by Martin Pool
[merge] bzr.dev
181
                raise errors.ReadOnlyError(self)
2279.7.1 by Andrew Bennetts
``LockableFiles.lock_write()`` now accepts a ``token`` keyword argument, so that
182
            self._lock.validate_token(token)
4041.1.5 by Michael Hudson
review comments
183
            self._lock_warner.lock_count += 1
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
184
            return self._token_from_lock
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
185
        else:
2279.7.1 by Andrew Bennetts
``LockableFiles.lock_write()`` now accepts a ``token`` keyword argument, so that
186
            token_from_lock = self._lock.lock_write(token=token)
1185.80.2 by John Arbash Meinel
Traced double locking code to WorkingTree creating its own control files.
187
            #traceback.print_stack()
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
188
            self._lock_mode = 'w'
4041.1.5 by Michael Hudson
review comments
189
            self._lock_warner.lock_count = 1
4145.1.2 by Robert Collins
Add a refresh_data method on Repository allowing cleaner handling of insertions into RemoteRepository objects with _real_repository instances.
190
            self._set_write_transaction()
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
191
            self._token_from_lock = token_from_lock
2279.7.1 by Andrew Bennetts
``LockableFiles.lock_write()`` now accepts a ``token`` keyword argument, so that
192
            return token_from_lock
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
193
194
    def lock_read(self):
195
        if self._lock_mode:
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
196
            if self._lock_mode not in ('r', 'w'):
197
                raise ValueError("invalid lock mode %r" % (self._lock_mode,))
4041.1.5 by Michael Hudson
review comments
198
            self._lock_warner.lock_count += 1
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
199
        else:
1553.5.47 by Martin Pool
cleanup LockableFiles
200
            self._lock.lock_read()
1185.80.2 by John Arbash Meinel
Traced double locking code to WorkingTree creating its own control files.
201
            #traceback.print_stack()
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
202
            self._lock_mode = 'r'
4041.1.5 by Michael Hudson
review comments
203
            self._lock_warner.lock_count = 1
4145.1.2 by Robert Collins
Add a refresh_data method on Repository allowing cleaner handling of insertions into RemoteRepository objects with _real_repository instances.
204
            self._set_read_transaction()
205
206
    def _set_read_transaction(self):
207
        """Setup a read transaction."""
208
        self._set_transaction(transactions.ReadOnlyTransaction())
209
        # 5K may be excessive, but hey, its a knob.
210
        self.get_transaction().set_cache_size(5000)
211
212
    def _set_write_transaction(self):
213
        """Setup a write transaction."""
214
        self._set_transaction(transactions.WriteTransaction())
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
215
4634.85.9 by Andrew Bennetts
Add some experimental decorators: @only_raises(..) and @cleanup_method.
216
    @only_raises(errors.LockNotHeld, errors.LockBroken)
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
217
    def unlock(self):
218
        if not self._lock_mode:
4509.3.25 by Martin Pool
Add an option for unlock errors to be non-fatal
219
            return lock.cant_unlock_not_held(self)
4041.1.5 by Michael Hudson
review comments
220
        if self._lock_warner.lock_count > 1:
221
            self._lock_warner.lock_count -= 1
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
222
        else:
1185.80.2 by John Arbash Meinel
Traced double locking code to WorkingTree creating its own control files.
223
            #traceback.print_stack()
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
224
            self._finish_transaction()
1687.1.6 by Robert Collins
Extend LockableFiles to support break_lock() calls.
225
            try:
226
                self._lock.unlock()
227
            finally:
4041.1.5 by Michael Hudson
review comments
228
                self._lock_mode = self._lock_warner.lock_count = None
4104.4.1 by Robert Collins
Cherrypick bugfixes from bzr.dev for 1.13:
229
4041.1.3 by Michael Hudson
_this_ works
230
    @property
231
    def _lock_count(self):
4041.1.5 by Michael Hudson
review comments
232
        return self._lock_warner.lock_count
4041.1.3 by Michael Hudson
_this_ works
233
1553.5.35 by Martin Pool
Start break-lock --show
234
    def is_locked(self):
235
        """Return true if this LockableFiles group is locked"""
4041.1.5 by Michael Hudson
review comments
236
        return self._lock_warner.lock_count >= 1
1553.5.35 by Martin Pool
Start break-lock --show
237
1694.2.6 by Martin Pool
[merge] bzr.dev
238
    def get_physical_lock_status(self):
239
        """Return physical lock status.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
240
1694.2.6 by Martin Pool
[merge] bzr.dev
241
        Returns true if a lock is held on the transport. If no lock is held, or
242
        the underlying locking mechanism does not support querying lock
243
        status, false is returned.
244
        """
245
        try:
246
            return self._lock.peek() is not None
247
        except NotImplementedError:
248
            return False
249
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
250
    def get_transaction(self):
251
        """Return the current active transaction.
252
253
        If no transaction is active, this returns a passthrough object
254
        for which all data is immediately flushed and no caching happens.
255
        """
256
        if self._transaction is None:
257
            return transactions.PassThroughTransaction()
258
        else:
259
            return self._transaction
260
261
    def _set_transaction(self, new_transaction):
262
        """Set a new active transaction."""
263
        if self._transaction is not None:
264
            raise errors.LockError('Branch %s is in a transaction already.' %
265
                                   self)
266
        self._transaction = new_transaction
267
268
    def _finish_transaction(self):
269
        """Exit the current transaction."""
270
        if self._transaction is None:
271
            raise errors.LockError('Branch %s is not in a transaction' %
272
                                   self)
273
        transaction = self._transaction
274
        self._transaction = None
275
        transaction.finish()
1553.5.40 by Martin Pool
Factor locking strategy out of LockableFiles so that we can use LockDirs in new formats.
276
277
1553.5.45 by Martin Pool
Clean up Transport-based locks for old branches
278
class TransportLock(object):
279
    """Locking method which uses transport-dependent locks.
280
281
    On the local filesystem these transform into OS-managed locks.
282
283
    These do not guard against concurrent access via different
284
    transports.
285
286
    This is suitable for use only in WorkingTrees (which are at present
287
    always local).
1553.5.40 by Martin Pool
Factor locking strategy out of LockableFiles so that we can use LockDirs in new formats.
288
    """
1553.5.59 by Martin Pool
Pass file/mode bits through to creation of lock files/dirs
289
    def __init__(self, transport, escaped_name, file_modebits, dir_modebits):
1553.5.40 by Martin Pool
Factor locking strategy out of LockableFiles so that we can use LockDirs in new formats.
290
        self._transport = transport
291
        self._escaped_name = escaped_name
1553.5.59 by Martin Pool
Pass file/mode bits through to creation of lock files/dirs
292
        self._file_modebits = file_modebits
293
        self._dir_modebits = dir_modebits
1553.5.40 by Martin Pool
Factor locking strategy out of LockableFiles so that we can use LockDirs in new formats.
294
1687.1.6 by Robert Collins
Extend LockableFiles to support break_lock() calls.
295
    def break_lock(self):
296
        raise NotImplementedError(self.break_lock)
297
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
298
    def leave_in_place(self):
299
        raise NotImplementedError(self.leave_in_place)
300
2018.5.76 by Andrew Bennetts
Testing that repository.{dont_,}leave_lock_in_place raises NotImplementedError if lock_write returns None.
301
    def dont_leave_in_place(self):
302
        raise NotImplementedError(self.dont_leave_in_place)
303
2279.7.1 by Andrew Bennetts
``LockableFiles.lock_write()`` now accepts a ``token`` keyword argument, so that
304
    def lock_write(self, token=None):
305
        if token is not None:
306
            raise errors.TokenLockingNotSupported(self)
1553.5.40 by Martin Pool
Factor locking strategy out of LockableFiles so that we can use LockDirs in new formats.
307
        self._lock = self._transport.lock_write(self._escaped_name)
308
309
    def lock_read(self):
310
        self._lock = self._transport.lock_read(self._escaped_name)
311
312
    def unlock(self):
313
        self._lock.unlock()
314
        self._lock = None
315
1694.2.6 by Martin Pool
[merge] bzr.dev
316
    def peek(self):
317
        raise NotImplementedError()
318
1666.1.4 by Robert Collins
* 'Metadir' is now the default disk format. This improves behaviour in
319
    def create(self, mode=None):
1553.5.59 by Martin Pool
Pass file/mode bits through to creation of lock files/dirs
320
        """Create lock mechanism"""
321
        # for old-style locks, create the file now
1955.3.8 by John Arbash Meinel
avoid some deprecation warnings in other parts of the code
322
        self._transport.put_bytes(self._escaped_name, '',
1553.5.60 by Martin Pool
New LockableFiles.create_lock() method
323
                            mode=self._file_modebits)
2279.7.1 by Andrew Bennetts
``LockableFiles.lock_write()`` now accepts a ``token`` keyword argument, so that
324
325
    def validate_token(self, token):
326
        if token is not None:
327
            raise errors.TokenLockingNotSupported(self)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
328