~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/lock.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2009-04-09 20:23:07 UTC
  • mfrom: (4265.1.4 bbc-merge)
  • Revision ID: pqm@pqm.ubuntu.com-20090409202307-n0depb16qepoe21o
(jam) Change _fetch_uses_deltas = False for CHK repos until we can
        write a better fix.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2007, 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
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
 
18
18
"""Locking using OS file locks or file existence.
42
42
    osutils,
43
43
    trace,
44
44
    )
45
 
 
46
 
 
47
 
class _base_Lock(object):
 
45
from bzrlib.hooks import HookPoint, Hooks
 
46
 
 
47
 
 
48
class LockHooks(Hooks):
 
49
 
 
50
    def __init__(self):
 
51
        Hooks.__init__(self)
 
52
        self.create_hook(HookPoint('lock_acquired',
 
53
            "Called with a bzrlib.lock.LockResult when a physical lock is "
 
54
            "acquired.", (1, 8), None))
 
55
        self.create_hook(HookPoint('lock_released',
 
56
            "Called with a bzrlib.lock.LockResult when a physical lock is "
 
57
            "released.", (1, 8), None))
 
58
 
 
59
 
 
60
class Lock(object):
 
61
    """Base class for locks.
 
62
 
 
63
    :cvar hooks: Hook dictionary for operations on locks.
 
64
    """
 
65
 
 
66
    hooks = LockHooks()
 
67
 
 
68
 
 
69
class LockResult(object):
 
70
    """Result of an operation on a lock; passed to a hook"""
 
71
 
 
72
    def __init__(self, lock_url, details=None):
 
73
        """Create a lock result for lock with optional details about the lock."""
 
74
        self.lock_url = lock_url
 
75
        self.details = details
 
76
 
 
77
    def __eq__(self, other):
 
78
        return self.lock_url == other.lock_url and self.details == other.details
 
79
 
 
80
 
 
81
try:
 
82
    import fcntl
 
83
    have_fcntl = True
 
84
except ImportError:
 
85
    have_fcntl = False
 
86
 
 
87
have_pywin32 = False
 
88
have_ctypes_win32 = False
 
89
if sys.platform == 'win32':
 
90
    import msvcrt
 
91
    try:
 
92
        import win32con, win32file, pywintypes, winerror
 
93
        have_pywin32 = True
 
94
    except ImportError:
 
95
        pass
 
96
 
 
97
    try:
 
98
        import ctypes
 
99
        have_ctypes_win32 = True
 
100
    except ImportError:
 
101
        pass
 
102
 
 
103
 
 
104
class _OSLock(object):
48
105
 
49
106
    def __init__(self):
50
107
        self.f = None
57
114
            return self.f
58
115
        except IOError, e:
59
116
            if e.errno in (errno.EACCES, errno.EPERM):
60
 
                raise errors.ReadOnlyLockError(self.filename, str(e))
 
117
                raise errors.LockFailed(self.filename, str(e))
61
118
            if e.errno != errno.ENOENT:
62
119
                raise
63
120
 
83
140
        raise NotImplementedError()
84
141
 
85
142
 
86
 
try:
87
 
    import fcntl
88
 
    have_fcntl = True
89
 
except ImportError:
90
 
    have_fcntl = False
91
 
try:
92
 
    import win32con, win32file, pywintypes, winerror, msvcrt
93
 
    have_pywin32 = True
94
 
except ImportError:
95
 
    have_pywin32 = False
96
 
try:
97
 
    import ctypes, msvcrt
98
 
    have_ctypes = True
99
 
except ImportError:
100
 
    have_ctypes = False
101
 
 
102
 
 
103
143
_lock_classes = []
104
144
 
105
145
 
109
149
    lock_EX = fcntl.LOCK_EX
110
150
 
111
151
 
112
 
    class _fcntl_FileLock(_base_Lock):
 
152
    class _fcntl_FileLock(_OSLock):
113
153
 
114
154
        def _unlock(self):
115
155
            fcntl.lockf(self.f, fcntl.LOCK_UN)
143
183
                    self.unlock()
144
184
                # we should be more precise about whats a locking
145
185
                # error and whats a random-other error
146
 
                raise errors.LockContention(e)
 
186
                raise errors.LockContention(self.filename, e)
147
187
 
148
188
        def unlock(self):
149
189
            _fcntl_WriteLock._open_locks.remove(self.filename)
167
207
            except IOError, e:
168
208
                # we should be more precise about whats a locking
169
209
                # error and whats a random-other error
170
 
                raise errors.LockContention(e)
 
210
                raise errors.LockContention(self.filename, e)
171
211
 
172
212
        def unlock(self):
173
213
            count = _fcntl_ReadLock._open_locks[self.filename]
187
227
 
188
228
            :return: A token which can be used to switch back to a read lock.
189
229
            """
190
 
            assert self.filename not in _fcntl_WriteLock._open_locks
 
230
            if self.filename in _fcntl_WriteLock._open_locks:
 
231
                raise AssertionError('file already locked: %r'
 
232
                    % (self.filename,))
191
233
            try:
192
234
                wlock = _fcntl_TemporaryWriteLock(self)
193
235
            except errors.LockError:
196
238
            return True, wlock
197
239
 
198
240
 
199
 
    class _fcntl_TemporaryWriteLock(_base_Lock):
 
241
    class _fcntl_TemporaryWriteLock(_OSLock):
200
242
        """A token used when grabbing a temporary_write_lock.
201
243
 
202
244
        Call restore_read_lock() when you are done with the write lock.
213
255
                # write lock.
214
256
                raise errors.LockContention(self.filename)
215
257
 
216
 
            assert self.filename not in _fcntl_WriteLock._open_locks
 
258
            if self.filename in _fcntl_WriteLock._open_locks:
 
259
                raise AssertionError('file already locked: %r'
 
260
                    % (self.filename,))
217
261
 
218
262
            # See if we can open the file for writing. Another process might
219
263
            # have a read lock. We don't use self._open() because we don't want
223
267
                new_f = open(self.filename, 'rb+')
224
268
            except IOError, e:
225
269
                if e.errno in (errno.EACCES, errno.EPERM):
226
 
                    raise errors.ReadOnlyLockError(self.filename, str(e))
 
270
                    raise errors.LockFailed(self.filename, str(e))
227
271
                raise
228
272
            try:
229
273
                # LOCK_NB will cause IOError to be raised if we can't grab a
231
275
                fcntl.lockf(new_f, fcntl.LOCK_EX | fcntl.LOCK_NB)
232
276
            except IOError, e:
233
277
                # TODO: Raise a more specific error based on the type of error
234
 
                raise errors.LockContention(e)
 
278
                raise errors.LockContention(self.filename, e)
235
279
            _fcntl_WriteLock._open_locks.add(self.filename)
236
280
 
237
281
            self.f = new_f
258
302
    LOCK_NB = win32con.LOCKFILE_FAIL_IMMEDIATELY
259
303
 
260
304
 
261
 
    class _w32c_FileLock(_base_Lock):
 
305
    class _w32c_FileLock(_OSLock):
262
306
 
263
307
        def _lock(self, filename, openmode, lockmode):
264
308
            self._open(filename, openmode)
276
320
                raise
277
321
            except Exception, e:
278
322
                self._clear_f()
279
 
                raise errors.LockContention(e)
 
323
                raise errors.LockContention(filename, e)
280
324
 
281
325
        def unlock(self):
282
326
            overlapped = pywintypes.OVERLAPPED()
284
328
                win32file.UnlockFileEx(self.hfile, 0, 0x7fff0000, overlapped)
285
329
                self._clear_f()
286
330
            except Exception, e:
287
 
                raise errors.LockContention(e)
 
331
                raise errors.LockContention(self.filename, e)
288
332
 
289
333
 
290
334
    class _w32c_ReadLock(_w32c_FileLock):
328
372
    _lock_classes.append(('pywin32', _w32c_WriteLock, _w32c_ReadLock))
329
373
 
330
374
 
331
 
if have_ctypes and sys.platform == 'win32':
 
375
if have_ctypes_win32:
332
376
    # These constants were copied from the win32con.py module.
333
377
    LOCKFILE_FAIL_IMMEDIATELY = 1
334
378
    LOCKFILE_EXCLUSIVE_LOCK = 2
374
418
                    ('hEvent', ctypes.c_void_p), # HANDLE
375
419
                   ]
376
420
 
377
 
    class _ctypes_FileLock(_base_Lock):
 
421
    class _ctypes_FileLock(_OSLock):
378
422
 
379
423
        def _lock(self, filename, openmode, lockmode):
380
424
            self._open(filename, openmode)
393
437
                last_err = _GetLastError()
394
438
                if last_err in (ERROR_LOCK_VIOLATION,):
395
439
                    raise errors.LockContention(filename)
396
 
                raise errors.LockContention('Unknown locking error: %s'
397
 
                                            % (last_err,))
 
440
                raise errors.LockContention(filename,
 
441
                    'Unknown locking error: %s' % (last_err,))
398
442
 
399
443
        def unlock(self):
400
444
            overlapped = OVERLAPPED()
408
452
            if result == 0:
409
453
                self._clear_f()
410
454
                last_err = _GetLastError()
411
 
                raise errors.LockContention('Unknown unlocking error: %s'
412
 
                                            % (last_err,))
 
455
                raise errors.LockContention(self.filename,
 
456
                    'Unknown unlocking error: %s' % (last_err,))
413
457
 
414
458
 
415
459
    class _ctypes_ReadLock(_ctypes_FileLock):