/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/lockable_files.py

  • Committer: Marius Kruger
  • Date: 2010-07-10 21:28:56 UTC
  • mto: (5384.1.1 integration)
  • mto: This revision was merged to the branch mainline in revision 5385.
  • Revision ID: marius.kruger@enerweb.co.za-20100710212856-uq4ji3go0u5se7hx
* Update documentation
* add NEWS

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2011 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2008, 2009 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
 
from __future__ import absolute_import
18
 
 
19
 
from .lazy_import import lazy_import
 
17
from bzrlib.lazy_import import lazy_import
20
18
lazy_import(globals(), """
21
 
from breezy import (
 
19
import codecs
 
20
import warnings
 
21
 
 
22
from bzrlib import (
22
23
    counted_lock,
 
24
    errors,
23
25
    lock,
 
26
    osutils,
24
27
    transactions,
25
28
    urlutils,
26
29
    )
27
30
""")
28
31
 
29
 
from . import (
30
 
    errors,
31
 
    )
32
 
from .decorators import (
 
32
from bzrlib.decorators import (
33
33
    only_raises,
34
34
    )
35
35
 
36
36
 
 
37
# XXX: The tracking here of lock counts and whether the lock is held is
 
38
# somewhat redundant with what's done in LockDir; the main difference is that
 
39
# LockableFiles permits reentrancy.
 
40
 
 
41
class _LockWarner(object):
 
42
    """Hold a counter for a lock and warn if GCed while the count is >= 1.
 
43
 
 
44
    This is separate from LockableFiles because putting a __del__ on
 
45
    LockableFiles can result in uncollectable cycles.
 
46
    """
 
47
 
 
48
    def __init__(self, repr):
 
49
        self.lock_count = 0
 
50
        self.repr = repr
 
51
 
 
52
    def __del__(self):
 
53
        if self.lock_count >= 1:
 
54
            # There should have been a try/finally to unlock this.
 
55
            warnings.warn("%r was gc'd while locked" % self.repr)
 
56
 
 
57
 
37
58
class LockableFiles(object):
38
59
    """Object representing a set of related files locked within the same scope.
39
60
 
48
69
    This class is now deprecated; code should move to using the Transport
49
70
    directly for file operations and using the lock or CountedLock for
50
71
    locking.
51
 
 
 
72
    
52
73
    :ivar _lock: The real underlying lock (e.g. a LockDir)
53
 
    :ivar _lock_count: If _lock_mode is true, a positive count of the number
54
 
        of times the lock has been taken (and not yet released) *by this
55
 
        process*, through this particular object instance.
56
 
    :ivar _lock_mode: None, or 'r' or 'w'
 
74
    :ivar _counted_lock: A lock decorated with a semaphore, so that it 
 
75
        can be re-entered.
57
76
    """
58
77
 
 
78
    # _lock_mode: None, or 'r' or 'w'
 
79
 
 
80
    # _lock_count: If _lock_mode is true, a positive count of the number of
 
81
    # times the lock has been taken *by this process*.
 
82
 
59
83
    def __init__(self, transport, lock_name, lock_class):
60
84
        """Create a LockableFiles group
61
85
 
69
93
        self.lock_name = lock_name
70
94
        self._transaction = None
71
95
        self._lock_mode = None
72
 
        self._lock_count = 0
 
96
        self._lock_warner = _LockWarner(repr(self))
73
97
        self._find_modes()
74
98
        esc_name = self._escape(lock_name)
75
99
        self._lock = lock_class(transport, esc_name,
88
112
    def __repr__(self):
89
113
        return '%s(%r)' % (self.__class__.__name__,
90
114
                           self._transport)
91
 
 
92
115
    def __str__(self):
93
116
        return 'LockableFiles(%s, %s)' % (self.lock_name, self._transport.base)
94
117
 
101
124
 
102
125
    def _escape(self, file_or_path):
103
126
        """DEPRECATED: Do not use outside this class"""
 
127
        if not isinstance(file_or_path, basestring):
 
128
            file_or_path = '/'.join(file_or_path)
104
129
        if file_or_path == '':
105
130
            return u''
106
 
        return urlutils.escape(file_or_path)
 
131
        return urlutils.escape(osutils.safe_unicode(file_or_path))
107
132
 
108
133
    def _find_modes(self):
109
134
        """Determine the appropriate modes for files and directories.
116
141
        try:
117
142
            st = self._transport.stat('.')
118
143
        except errors.TransportNotPossible:
119
 
            self._dir_mode = 0o755
120
 
            self._file_mode = 0o644
 
144
            self._dir_mode = 0755
 
145
            self._file_mode = 0644
121
146
        else:
122
147
            # Check the directory mode, but also make sure the created
123
148
            # directories and files are read-write for this user. This is
124
149
            # mostly a workaround for filesystems which lie about being able to
125
150
            # write to a directory (cygwin & win32)
126
 
            self._dir_mode = (st.st_mode & 0o7777) | 0o0700
 
151
            self._dir_mode = (st.st_mode & 07777) | 00700
127
152
            # Remove the sticky and execute bits for files
128
 
            self._file_mode = self._dir_mode & ~0o7111
 
153
            self._file_mode = self._dir_mode & ~07111
129
154
 
130
155
    def leave_in_place(self):
131
156
        """Set this LockableFiles to not clear the physical lock on unlock."""
150
175
        some other way, and need to synchronise this object's state with that
151
176
        fact.
152
177
        """
 
178
        # TODO: Upgrade locking to support using a Transport,
 
179
        # and potentially a remote locking protocol
153
180
        if self._lock_mode:
154
 
            if (self._lock_mode != 'w'
155
 
                    or not self.get_transaction().writeable()):
 
181
            if self._lock_mode != 'w' or not self.get_transaction().writeable():
156
182
                raise errors.ReadOnlyError(self)
157
183
            self._lock.validate_token(token)
158
 
            self._lock_count += 1
 
184
            self._lock_warner.lock_count += 1
159
185
            return self._token_from_lock
160
186
        else:
161
187
            token_from_lock = self._lock.lock_write(token=token)
162
 
            # traceback.print_stack()
 
188
            #traceback.print_stack()
163
189
            self._lock_mode = 'w'
164
 
            self._lock_count = 1
 
190
            self._lock_warner.lock_count = 1
165
191
            self._set_write_transaction()
166
192
            self._token_from_lock = token_from_lock
167
193
            return token_from_lock
170
196
        if self._lock_mode:
171
197
            if self._lock_mode not in ('r', 'w'):
172
198
                raise ValueError("invalid lock mode %r" % (self._lock_mode,))
173
 
            self._lock_count += 1
 
199
            self._lock_warner.lock_count += 1
174
200
        else:
175
201
            self._lock.lock_read()
176
 
            # traceback.print_stack()
 
202
            #traceback.print_stack()
177
203
            self._lock_mode = 'r'
178
 
            self._lock_count = 1
 
204
            self._lock_warner.lock_count = 1
179
205
            self._set_read_transaction()
180
206
 
181
207
    def _set_read_transaction(self):
192
218
    def unlock(self):
193
219
        if not self._lock_mode:
194
220
            return lock.cant_unlock_not_held(self)
195
 
        if self._lock_count > 1:
196
 
            self._lock_count -= 1
 
221
        if self._lock_warner.lock_count > 1:
 
222
            self._lock_warner.lock_count -= 1
197
223
        else:
198
 
            # traceback.print_stack()
 
224
            #traceback.print_stack()
199
225
            self._finish_transaction()
200
226
            try:
201
227
                self._lock.unlock()
202
228
            finally:
203
 
                self._lock_count = 0
204
 
                self._lock_mode = None
 
229
                self._lock_mode = self._lock_warner.lock_count = None
 
230
 
 
231
    @property
 
232
    def _lock_count(self):
 
233
        return self._lock_warner.lock_count
205
234
 
206
235
    def is_locked(self):
207
236
        """Return true if this LockableFiles group is locked"""
208
 
        return self._lock_count >= 1
 
237
        return self._lock_warner.lock_count >= 1
209
238
 
210
239
    def get_physical_lock_status(self):
211
240
        """Return physical lock status.
258
287
    This is suitable for use only in WorkingTrees (which are at present
259
288
    always local).
260
289
    """
261
 
 
262
290
    def __init__(self, transport, escaped_name, file_modebits, dir_modebits):
263
291
        self._transport = transport
264
292
        self._escaped_name = escaped_name
292
320
    def create(self, mode=None):
293
321
        """Create lock mechanism"""
294
322
        # for old-style locks, create the file now
295
 
        self._transport.put_bytes(self._escaped_name, b'',
296
 
                                  mode=self._file_modebits)
 
323
        self._transport.put_bytes(self._escaped_name, '',
 
324
                            mode=self._file_modebits)
297
325
 
298
326
    def validate_token(self, token):
299
327
        if token is not None:
300
328
            raise errors.TokenLockingNotSupported(self)
 
329