~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/lockable_files.py

  • Committer: Andrew Bennetts
  • Date: 2010-10-08 08:15:14 UTC
  • mto: This revision was merged to the branch mainline in revision 5498.
  • Revision ID: andrew.bennetts@canonical.com-20101008081514-dviqzrdfwyzsqbz2
Split NEWS into per-release doc/en/release-notes/bzr-*.txt

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
17
from bzrlib.lazy_import import lazy_import
20
18
lazy_import(globals(), """
 
19
import codecs
21
20
import warnings
22
21
 
23
22
from bzrlib import (
35
34
    )
36
35
 
37
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
 
38
58
class LockableFiles(object):
39
59
    """Object representing a set of related files locked within the same scope.
40
60
 
49
69
    This class is now deprecated; code should move to using the Transport
50
70
    directly for file operations and using the lock or CountedLock for
51
71
    locking.
52
 
 
 
72
    
53
73
    :ivar _lock: The real underlying lock (e.g. a LockDir)
54
 
    :ivar _lock_count: If _lock_mode is true, a positive count of the number
55
 
        of times the lock has been taken (and not yet released) *by this
56
 
        process*, through this particular object instance.
57
 
    :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.
58
76
    """
59
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
 
60
83
    def __init__(self, transport, lock_name, lock_class):
61
84
        """Create a LockableFiles group
62
85
 
70
93
        self.lock_name = lock_name
71
94
        self._transaction = None
72
95
        self._lock_mode = None
73
 
        self._lock_count = 0
 
96
        self._lock_warner = _LockWarner(repr(self))
74
97
        self._find_modes()
75
98
        esc_name = self._escape(lock_name)
76
99
        self._lock = lock_class(transport, esc_name,
89
112
    def __repr__(self):
90
113
        return '%s(%r)' % (self.__class__.__name__,
91
114
                           self._transport)
92
 
 
93
115
    def __str__(self):
94
116
        return 'LockableFiles(%s, %s)' % (self.lock_name, self._transport.base)
95
117
 
153
175
        some other way, and need to synchronise this object's state with that
154
176
        fact.
155
177
        """
 
178
        # TODO: Upgrade locking to support using a Transport,
 
179
        # and potentially a remote locking protocol
156
180
        if self._lock_mode:
157
 
            if (self._lock_mode != 'w'
158
 
                or not self.get_transaction().writeable()):
 
181
            if self._lock_mode != 'w' or not self.get_transaction().writeable():
159
182
                raise errors.ReadOnlyError(self)
160
183
            self._lock.validate_token(token)
161
 
            self._lock_count += 1
 
184
            self._lock_warner.lock_count += 1
162
185
            return self._token_from_lock
163
186
        else:
164
187
            token_from_lock = self._lock.lock_write(token=token)
165
188
            #traceback.print_stack()
166
189
            self._lock_mode = 'w'
167
 
            self._lock_count = 1
 
190
            self._lock_warner.lock_count = 1
168
191
            self._set_write_transaction()
169
192
            self._token_from_lock = token_from_lock
170
193
            return token_from_lock
173
196
        if self._lock_mode:
174
197
            if self._lock_mode not in ('r', 'w'):
175
198
                raise ValueError("invalid lock mode %r" % (self._lock_mode,))
176
 
            self._lock_count += 1
 
199
            self._lock_warner.lock_count += 1
177
200
        else:
178
201
            self._lock.lock_read()
179
202
            #traceback.print_stack()
180
203
            self._lock_mode = 'r'
181
 
            self._lock_count = 1
 
204
            self._lock_warner.lock_count = 1
182
205
            self._set_read_transaction()
183
206
 
184
207
    def _set_read_transaction(self):
195
218
    def unlock(self):
196
219
        if not self._lock_mode:
197
220
            return lock.cant_unlock_not_held(self)
198
 
        if self._lock_count > 1:
199
 
            self._lock_count -= 1
 
221
        if self._lock_warner.lock_count > 1:
 
222
            self._lock_warner.lock_count -= 1
200
223
        else:
201
224
            #traceback.print_stack()
202
225
            self._finish_transaction()
203
226
            try:
204
227
                self._lock.unlock()
205
228
            finally:
206
 
                self._lock_mode = self._lock_count = 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
207
234
 
208
235
    def is_locked(self):
209
236
        """Return true if this LockableFiles group is locked"""
210
 
        return self._lock_count >= 1
 
237
        return self._lock_warner.lock_count >= 1
211
238
 
212
239
    def get_physical_lock_status(self):
213
240
        """Return physical lock status.
299
326
    def validate_token(self, token):
300
327
        if token is not None:
301
328
            raise errors.TokenLockingNotSupported(self)
 
329