~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/lock.py

  • Committer: Alexander Belchenko
  • Date: 2007-04-30 19:52:44 UTC
  • mto: This revision was merged to the branch mainline in revision 2478.
  • Revision ID: bialix@ukr.net-20070430195244-pxc5r3x72ckh027e
Bugfix #110901: commit message template written with native line-endings; corresponding unit tests added

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
 
 
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):
 
45
 
 
46
 
 
47
class _base_Lock(object):
105
48
 
106
49
    def __init__(self):
107
50
        self.f = None
114
57
            return self.f
115
58
        except IOError, e:
116
59
            if e.errno in (errno.EACCES, errno.EPERM):
117
 
                raise errors.LockFailed(self.filename, str(e))
 
60
                raise errors.ReadOnlyLockError(self.filename, str(e))
118
61
            if e.errno != errno.ENOENT:
119
62
                raise
120
63
 
140
83
        raise NotImplementedError()
141
84
 
142
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
 
143
103
_lock_classes = []
144
104
 
145
105
 
149
109
    lock_EX = fcntl.LOCK_EX
150
110
 
151
111
 
152
 
    class _fcntl_FileLock(_OSLock):
 
112
    class _fcntl_FileLock(_base_Lock):
153
113
 
154
114
        def _unlock(self):
155
115
            fcntl.lockf(self.f, fcntl.LOCK_UN)
183
143
                    self.unlock()
184
144
                # we should be more precise about whats a locking
185
145
                # error and whats a random-other error
186
 
                raise errors.LockContention(self.filename, e)
 
146
                raise errors.LockContention(e)
187
147
 
188
148
        def unlock(self):
189
149
            _fcntl_WriteLock._open_locks.remove(self.filename)
207
167
            except IOError, e:
208
168
                # we should be more precise about whats a locking
209
169
                # error and whats a random-other error
210
 
                raise errors.LockContention(self.filename, e)
 
170
                raise errors.LockContention(e)
211
171
 
212
172
        def unlock(self):
213
173
            count = _fcntl_ReadLock._open_locks[self.filename]
227
187
 
228
188
            :return: A token which can be used to switch back to a read lock.
229
189
            """
230
 
            if self.filename in _fcntl_WriteLock._open_locks:
231
 
                raise AssertionError('file already locked: %r'
232
 
                    % (self.filename,))
 
190
            assert self.filename not in _fcntl_WriteLock._open_locks
233
191
            try:
234
192
                wlock = _fcntl_TemporaryWriteLock(self)
235
193
            except errors.LockError:
238
196
            return True, wlock
239
197
 
240
198
 
241
 
    class _fcntl_TemporaryWriteLock(_OSLock):
 
199
    class _fcntl_TemporaryWriteLock(_base_Lock):
242
200
        """A token used when grabbing a temporary_write_lock.
243
201
 
244
202
        Call restore_read_lock() when you are done with the write lock.
255
213
                # write lock.
256
214
                raise errors.LockContention(self.filename)
257
215
 
258
 
            if self.filename in _fcntl_WriteLock._open_locks:
259
 
                raise AssertionError('file already locked: %r'
260
 
                    % (self.filename,))
 
216
            assert self.filename not in _fcntl_WriteLock._open_locks
261
217
 
262
218
            # See if we can open the file for writing. Another process might
263
219
            # have a read lock. We don't use self._open() because we don't want
267
223
                new_f = open(self.filename, 'rb+')
268
224
            except IOError, e:
269
225
                if e.errno in (errno.EACCES, errno.EPERM):
270
 
                    raise errors.LockFailed(self.filename, str(e))
 
226
                    raise errors.ReadOnlyLockError(self.filename, str(e))
271
227
                raise
272
228
            try:
273
229
                # LOCK_NB will cause IOError to be raised if we can't grab a
275
231
                fcntl.lockf(new_f, fcntl.LOCK_EX | fcntl.LOCK_NB)
276
232
            except IOError, e:
277
233
                # TODO: Raise a more specific error based on the type of error
278
 
                raise errors.LockContention(self.filename, e)
 
234
                raise errors.LockContention(e)
279
235
            _fcntl_WriteLock._open_locks.add(self.filename)
280
236
 
281
237
            self.f = new_f
302
258
    LOCK_NB = win32con.LOCKFILE_FAIL_IMMEDIATELY
303
259
 
304
260
 
305
 
    class _w32c_FileLock(_OSLock):
 
261
    class _w32c_FileLock(_base_Lock):
306
262
 
307
263
        def _lock(self, filename, openmode, lockmode):
308
264
            self._open(filename, openmode)
320
276
                raise
321
277
            except Exception, e:
322
278
                self._clear_f()
323
 
                raise errors.LockContention(filename, e)
 
279
                raise errors.LockContention(e)
324
280
 
325
281
        def unlock(self):
326
282
            overlapped = pywintypes.OVERLAPPED()
328
284
                win32file.UnlockFileEx(self.hfile, 0, 0x7fff0000, overlapped)
329
285
                self._clear_f()
330
286
            except Exception, e:
331
 
                raise errors.LockContention(self.filename, e)
 
287
                raise errors.LockContention(e)
332
288
 
333
289
 
334
290
    class _w32c_ReadLock(_w32c_FileLock):
372
328
    _lock_classes.append(('pywin32', _w32c_WriteLock, _w32c_ReadLock))
373
329
 
374
330
 
375
 
if have_ctypes_win32:
 
331
if have_ctypes and sys.platform == 'win32':
376
332
    # These constants were copied from the win32con.py module.
377
333
    LOCKFILE_FAIL_IMMEDIATELY = 1
378
334
    LOCKFILE_EXCLUSIVE_LOCK = 2
418
374
                    ('hEvent', ctypes.c_void_p), # HANDLE
419
375
                   ]
420
376
 
421
 
    class _ctypes_FileLock(_OSLock):
 
377
    class _ctypes_FileLock(_base_Lock):
422
378
 
423
379
        def _lock(self, filename, openmode, lockmode):
424
380
            self._open(filename, openmode)
437
393
                last_err = _GetLastError()
438
394
                if last_err in (ERROR_LOCK_VIOLATION,):
439
395
                    raise errors.LockContention(filename)
440
 
                raise errors.LockContention(filename,
441
 
                    'Unknown locking error: %s' % (last_err,))
 
396
                raise errors.LockContention('Unknown locking error: %s'
 
397
                                            % (last_err,))
442
398
 
443
399
        def unlock(self):
444
400
            overlapped = OVERLAPPED()
452
408
            if result == 0:
453
409
                self._clear_f()
454
410
                last_err = _GetLastError()
455
 
                raise errors.LockContention(self.filename,
456
 
                    'Unknown unlocking error: %s' % (last_err,))
 
411
                raise errors.LockContention('Unknown unlocking error: %s'
 
412
                                            % (last_err,))
457
413
 
458
414
 
459
415
    class _ctypes_ReadLock(_ctypes_FileLock):