~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: 2008-03-16 14:01:20 UTC
  • mfrom: (3280.2.5 integration)
  • Revision ID: pqm@pqm.ubuntu.com-20080316140120-i3yq8yr1l66m11h7
Start 1.4 development

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007, 2008, 2009 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2007 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
 
18
18
"""Locking using OS file locks or file existence.
42
42
    osutils,
43
43
    trace,
44
44
    )
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
 
        self.create_hook(HookPoint('lock_broken',
59
 
            "Called with a bzrlib.lock.LockResult when a physical lock is "
60
 
            "broken.", (1, 15), None))
61
 
 
62
 
 
63
 
class Lock(object):
64
 
    """Base class for locks.
65
 
 
66
 
    :cvar hooks: Hook dictionary for operations on locks.
67
 
    """
68
 
 
69
 
    hooks = LockHooks()
70
 
 
71
 
 
72
 
class LockResult(object):
73
 
    """Result of an operation on a lock; passed to a hook"""
74
 
 
75
 
    def __init__(self, lock_url, details=None):
76
 
        """Create a lock result for lock with optional details about the lock."""
77
 
        self.lock_url = lock_url
78
 
        self.details = details
79
 
 
80
 
    def __eq__(self, other):
81
 
        return self.lock_url == other.lock_url and self.details == other.details
82
 
 
83
 
    def __repr__(self):
84
 
        return '%s(%s%s)' % (self.__class__.__name__,
85
 
                             self.lock_url, self.details)
86
 
 
87
 
 
88
 
try:
89
 
    import fcntl
90
 
    have_fcntl = True
91
 
except ImportError:
92
 
    have_fcntl = False
93
 
 
94
 
have_pywin32 = False
95
 
have_ctypes_win32 = False
96
 
if sys.platform == 'win32':
97
 
    import msvcrt
98
 
    try:
99
 
        import win32con, win32file, pywintypes, winerror
100
 
        have_pywin32 = True
101
 
    except ImportError:
102
 
        pass
103
 
 
104
 
    try:
105
 
        import ctypes
106
 
        have_ctypes_win32 = True
107
 
    except ImportError:
108
 
        pass
109
45
 
110
46
 
111
47
class _OSLock(object):
147
83
        raise NotImplementedError()
148
84
 
149
85
 
 
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
 
150
103
_lock_classes = []
151
104
 
152
105
 
190
143
                    self.unlock()
191
144
                # we should be more precise about whats a locking
192
145
                # error and whats a random-other error
193
 
                raise errors.LockContention(self.filename, e)
 
146
                raise errors.LockContention(e)
194
147
 
195
148
        def unlock(self):
196
149
            _fcntl_WriteLock._open_locks.remove(self.filename)
214
167
            except IOError, e:
215
168
                # we should be more precise about whats a locking
216
169
                # error and whats a random-other error
217
 
                raise errors.LockContention(self.filename, e)
 
170
                raise errors.LockContention(e)
218
171
 
219
172
        def unlock(self):
220
173
            count = _fcntl_ReadLock._open_locks[self.filename]
234
187
 
235
188
            :return: A token which can be used to switch back to a read lock.
236
189
            """
237
 
            if self.filename in _fcntl_WriteLock._open_locks:
238
 
                raise AssertionError('file already locked: %r'
239
 
                    % (self.filename,))
 
190
            assert self.filename not in _fcntl_WriteLock._open_locks
240
191
            try:
241
192
                wlock = _fcntl_TemporaryWriteLock(self)
242
193
            except errors.LockError:
262
213
                # write lock.
263
214
                raise errors.LockContention(self.filename)
264
215
 
265
 
            if self.filename in _fcntl_WriteLock._open_locks:
266
 
                raise AssertionError('file already locked: %r'
267
 
                    % (self.filename,))
 
216
            assert self.filename not in _fcntl_WriteLock._open_locks
268
217
 
269
218
            # See if we can open the file for writing. Another process might
270
219
            # have a read lock. We don't use self._open() because we don't want
282
231
                fcntl.lockf(new_f, fcntl.LOCK_EX | fcntl.LOCK_NB)
283
232
            except IOError, e:
284
233
                # TODO: Raise a more specific error based on the type of error
285
 
                raise errors.LockContention(self.filename, e)
 
234
                raise errors.LockContention(e)
286
235
            _fcntl_WriteLock._open_locks.add(self.filename)
287
236
 
288
237
            self.f = new_f
327
276
                raise
328
277
            except Exception, e:
329
278
                self._clear_f()
330
 
                raise errors.LockContention(filename, e)
 
279
                raise errors.LockContention(e)
331
280
 
332
281
        def unlock(self):
333
282
            overlapped = pywintypes.OVERLAPPED()
335
284
                win32file.UnlockFileEx(self.hfile, 0, 0x7fff0000, overlapped)
336
285
                self._clear_f()
337
286
            except Exception, e:
338
 
                raise errors.LockContention(self.filename, e)
 
287
                raise errors.LockContention(e)
339
288
 
340
289
 
341
290
    class _w32c_ReadLock(_w32c_FileLock):
379
328
    _lock_classes.append(('pywin32', _w32c_WriteLock, _w32c_ReadLock))
380
329
 
381
330
 
382
 
if have_ctypes_win32:
 
331
if have_ctypes and sys.platform == 'win32':
383
332
    # These constants were copied from the win32con.py module.
384
333
    LOCKFILE_FAIL_IMMEDIATELY = 1
385
334
    LOCKFILE_EXCLUSIVE_LOCK = 2
444
393
                last_err = _GetLastError()
445
394
                if last_err in (ERROR_LOCK_VIOLATION,):
446
395
                    raise errors.LockContention(filename)
447
 
                raise errors.LockContention(filename,
448
 
                    'Unknown locking error: %s' % (last_err,))
 
396
                raise errors.LockContention('Unknown locking error: %s'
 
397
                                            % (last_err,))
449
398
 
450
399
        def unlock(self):
451
400
            overlapped = OVERLAPPED()
459
408
            if result == 0:
460
409
                self._clear_f()
461
410
                last_err = _GetLastError()
462
 
                raise errors.LockContention(self.filename,
463
 
                    'Unknown unlocking error: %s' % (last_err,))
 
411
                raise errors.LockContention('Unknown unlocking error: %s'
 
412
                                            % (last_err,))
464
413
 
465
414
 
466
415
    class _ctypes_ReadLock(_ctypes_FileLock):