~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/lock.py

  • Committer: Sidnei da Silva
  • Date: 2009-07-03 15:06:42 UTC
  • mto: (4531.1.1 integration)
  • mto: This revision was merged to the branch mainline in revision 4532.
  • Revision ID: sidnei.da.silva@canonical.com-20090703150642-hjfra5waj5879cae
- Add top-level make target to build all installers using buildout and another to cleanup

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
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
45
109
 
46
110
 
47
111
class _OSLock(object):
57
121
            return self.f
58
122
        except IOError, e:
59
123
            if e.errno in (errno.EACCES, errno.EPERM):
60
 
                raise errors.ReadOnlyLockError(self.filename, str(e))
 
124
                raise errors.LockFailed(self.filename, str(e))
61
125
            if e.errno != errno.ENOENT:
62
126
                raise
63
127
 
83
147
        raise NotImplementedError()
84
148
 
85
149
 
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
150
_lock_classes = []
104
151
 
105
152
 
143
190
                    self.unlock()
144
191
                # we should be more precise about whats a locking
145
192
                # error and whats a random-other error
146
 
                raise errors.LockContention(e)
 
193
                raise errors.LockContention(self.filename, e)
147
194
 
148
195
        def unlock(self):
149
196
            _fcntl_WriteLock._open_locks.remove(self.filename)
167
214
            except IOError, e:
168
215
                # we should be more precise about whats a locking
169
216
                # error and whats a random-other error
170
 
                raise errors.LockContention(e)
 
217
                raise errors.LockContention(self.filename, e)
171
218
 
172
219
        def unlock(self):
173
220
            count = _fcntl_ReadLock._open_locks[self.filename]
187
234
 
188
235
            :return: A token which can be used to switch back to a read lock.
189
236
            """
190
 
            assert self.filename not in _fcntl_WriteLock._open_locks
 
237
            if self.filename in _fcntl_WriteLock._open_locks:
 
238
                raise AssertionError('file already locked: %r'
 
239
                    % (self.filename,))
191
240
            try:
192
241
                wlock = _fcntl_TemporaryWriteLock(self)
193
242
            except errors.LockError:
213
262
                # write lock.
214
263
                raise errors.LockContention(self.filename)
215
264
 
216
 
            assert self.filename not in _fcntl_WriteLock._open_locks
 
265
            if self.filename in _fcntl_WriteLock._open_locks:
 
266
                raise AssertionError('file already locked: %r'
 
267
                    % (self.filename,))
217
268
 
218
269
            # See if we can open the file for writing. Another process might
219
270
            # have a read lock. We don't use self._open() because we don't want
223
274
                new_f = open(self.filename, 'rb+')
224
275
            except IOError, e:
225
276
                if e.errno in (errno.EACCES, errno.EPERM):
226
 
                    raise errors.ReadOnlyLockError(self.filename, str(e))
 
277
                    raise errors.LockFailed(self.filename, str(e))
227
278
                raise
228
279
            try:
229
280
                # LOCK_NB will cause IOError to be raised if we can't grab a
231
282
                fcntl.lockf(new_f, fcntl.LOCK_EX | fcntl.LOCK_NB)
232
283
            except IOError, e:
233
284
                # TODO: Raise a more specific error based on the type of error
234
 
                raise errors.LockContention(e)
 
285
                raise errors.LockContention(self.filename, e)
235
286
            _fcntl_WriteLock._open_locks.add(self.filename)
236
287
 
237
288
            self.f = new_f
276
327
                raise
277
328
            except Exception, e:
278
329
                self._clear_f()
279
 
                raise errors.LockContention(e)
 
330
                raise errors.LockContention(filename, e)
280
331
 
281
332
        def unlock(self):
282
333
            overlapped = pywintypes.OVERLAPPED()
284
335
                win32file.UnlockFileEx(self.hfile, 0, 0x7fff0000, overlapped)
285
336
                self._clear_f()
286
337
            except Exception, e:
287
 
                raise errors.LockContention(e)
 
338
                raise errors.LockContention(self.filename, e)
288
339
 
289
340
 
290
341
    class _w32c_ReadLock(_w32c_FileLock):
328
379
    _lock_classes.append(('pywin32', _w32c_WriteLock, _w32c_ReadLock))
329
380
 
330
381
 
331
 
if have_ctypes and sys.platform == 'win32':
 
382
if have_ctypes_win32:
332
383
    # These constants were copied from the win32con.py module.
333
384
    LOCKFILE_FAIL_IMMEDIATELY = 1
334
385
    LOCKFILE_EXCLUSIVE_LOCK = 2
393
444
                last_err = _GetLastError()
394
445
                if last_err in (ERROR_LOCK_VIOLATION,):
395
446
                    raise errors.LockContention(filename)
396
 
                raise errors.LockContention('Unknown locking error: %s'
397
 
                                            % (last_err,))
 
447
                raise errors.LockContention(filename,
 
448
                    'Unknown locking error: %s' % (last_err,))
398
449
 
399
450
        def unlock(self):
400
451
            overlapped = OVERLAPPED()
408
459
            if result == 0:
409
460
                self._clear_f()
410
461
                last_err = _GetLastError()
411
 
                raise errors.LockContention('Unknown unlocking error: %s'
412
 
                                            % (last_err,))
 
462
                raise errors.LockContention(self.filename,
 
463
                    'Unknown unlocking error: %s' % (last_err,))
413
464
 
414
465
 
415
466
    class _ctypes_ReadLock(_ctypes_FileLock):