~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/lock.py

[merge] LockCleanup changes.

Show diffs side-by-side

added added

removed removed

Lines of Context:
26
26
 
27
27
It is not specified whether these locks are reentrant (i.e. can be
28
28
taken repeatedly by a single process) or whether they exclude
29
 
different threads in a single process.  That reentrancy is provided by 
 
29
different threads in a single process.  That reentrancy is provided by
30
30
LockableFiles.
31
31
 
32
32
This defines two classes: ReadLock and WriteLock, which can be
35
35
"""
36
36
 
37
37
import errno
38
 
import os
39
 
import sys
40
38
 
41
 
from bzrlib import errors
42
 
from bzrlib.errors import LockError, LockContention
43
 
from bzrlib.osutils import realpath
44
 
from bzrlib.trace import mutter
 
39
from bzrlib import (
 
40
    errors,
 
41
    osutils,
 
42
    trace,
 
43
    )
45
44
 
46
45
 
47
46
class _base_Lock(object):
48
47
 
49
48
    def __init__(self):
50
49
        self.f = None
 
50
        self.filename = None
51
51
 
52
52
    def _open(self, filename, filemode):
 
53
        self.filename = osutils.realpath(filename)
53
54
        try:
54
 
            self.f = open(filename, filemode)
 
55
            self.f = open(self.filename, filemode)
55
56
            return self.f
56
57
        except IOError, e:
57
58
            if e.errno in (errno.EACCES, errno.EPERM):
58
 
                raise errors.ReadOnlyLockError(e)
 
59
                raise errors.ReadOnlyLockError(self.filename, str(e))
59
60
            if e.errno != errno.ENOENT:
60
61
                raise
61
62
 
62
63
            # maybe this is an old branch (before may 2005)
63
 
            mutter("trying to create missing branch lock %r", filename)
64
 
            
65
 
            self.f = open(filename, 'wb+')
 
64
            trace.mutter("trying to create missing lock %r", self.filename)
 
65
 
 
66
            self.f = open(self.filename, 'wb+')
66
67
            return self.f
67
68
 
68
69
    def _clear_f(self):
76
77
            from warnings import warn
77
78
            warn("lock on %r not released" % self.f)
78
79
            self.unlock()
79
 
            
 
80
 
80
81
    def unlock(self):
81
82
        raise NotImplementedError()
82
83
 
83
84
 
84
 
have_ctypes = have_pywin32 = have_fcntl = False
85
85
try:
86
86
    import fcntl
87
87
    have_fcntl = True
88
88
except ImportError:
89
 
    try:
90
 
        import win32con, win32file, pywintypes, winerror, msvcrt
91
 
        have_pywin32 = True
92
 
    except ImportError:
93
 
        try:
94
 
            import ctypes, msvcrt
95
 
            have_ctypes = True
96
 
        except ImportError:
97
 
            raise NotImplementedError("please write a locking method "
98
 
                                      "for platform %r" % sys.platform)
 
89
    have_fcntl = False
 
90
try:
 
91
    import win32con, win32file, pywintypes, winerror, msvcrt
 
92
    have_pywin32 = True
 
93
except ImportError:
 
94
    have_pywin32 = False
 
95
try:
 
96
    import ctypes, msvcrt
 
97
    have_ctypes = True
 
98
except ImportError:
 
99
    have_ctypes = False
 
100
 
 
101
 
 
102
_lock_classes = []
99
103
 
100
104
 
101
105
if have_fcntl:
116
120
        _open_locks = set()
117
121
 
118
122
        def __init__(self, filename):
119
 
            # standard IO errors get exposed directly.
120
123
            super(_fcntl_WriteLock, self).__init__()
121
 
            self.filename = realpath(filename)
 
124
            # Check we can grab a lock before we actually open the file.
 
125
            self.filename = osutils.realpath(filename)
122
126
            if (self.filename in _fcntl_WriteLock._open_locks
123
127
                or self.filename in _fcntl_ReadLock._open_locks):
124
128
                self._clear_f()
125
129
                raise errors.LockContention(self.filename)
126
130
 
127
 
            self._open(filename, 'rb+')
 
131
            self._open(self.filename, 'rb+')
128
132
            # reserve a slot for this lock - even if the lockf call fails,
129
133
            # at thisi point unlock() will be called, because self.f is set.
130
134
            # TODO: make this fully threadsafe, if we decide we care.
152
156
 
153
157
        def __init__(self, filename):
154
158
            super(_fcntl_ReadLock, self).__init__()
155
 
            self.filename = realpath(filename)
 
159
            self.filename = osutils.realpath(filename)
156
160
            if self.filename in _fcntl_WriteLock._open_locks:
157
161
                raise errors.LockContention(self.filename)
158
162
            _fcntl_ReadLock._open_locks.setdefault(self.filename, 0)
176
180
            self._unlock()
177
181
 
178
182
 
179
 
    WriteLock = _fcntl_WriteLock
180
 
    ReadLock = _fcntl_ReadLock
181
 
 
182
 
elif have_pywin32:
 
183
    _lock_classes.append(('fcntl', _fcntl_WriteLock, _fcntl_ReadLock))
 
184
 
 
185
 
 
186
if have_pywin32:
183
187
    LOCK_SH = 0 # the default
184
188
    LOCK_EX = win32con.LOCKFILE_EXCLUSIVE_LOCK
185
189
    LOCK_NB = win32con.LOCKFILE_FAIL_IMMEDIATELY
203
207
                raise
204
208
            except Exception, e:
205
209
                self._clear_f()
206
 
                raise LockError(e)
 
210
                raise errors.LockError(e)
207
211
 
208
212
        def unlock(self):
209
213
            overlapped = pywintypes.OVERLAPPED()
211
215
                win32file.UnlockFileEx(self.hfile, 0, 0x7fff0000, overlapped)
212
216
                self._clear_f()
213
217
            except Exception, e:
214
 
                raise LockError(e)
 
218
                raise errors.LockError(e)
215
219
 
216
220
 
217
221
    class _w32c_ReadLock(_w32c_FileLock):
219
223
            super(_w32c_ReadLock, self).__init__()
220
224
            self._lock(filename, 'rb', LOCK_SH + LOCK_NB)
221
225
 
 
226
 
222
227
    class _w32c_WriteLock(_w32c_FileLock):
223
228
        def __init__(self, filename):
224
229
            super(_w32c_WriteLock, self).__init__()
225
230
            self._lock(filename, 'rb+', LOCK_EX + LOCK_NB)
226
231
 
227
 
    WriteLock = _w32c_WriteLock
228
 
    ReadLock = _w32c_ReadLock
229
 
else:
230
 
    assert have_ctypes, "We should have ctypes installed"
 
232
 
 
233
    _lock_classes.append(('pywin32', _w32c_WriteLock, _w32c_ReadLock))
 
234
 
 
235
 
 
236
if have_ctypes:
231
237
    # These constants were copied from the win32con.py module.
232
238
    LOCKFILE_FAIL_IMMEDIATELY = 1
233
239
    LOCKFILE_EXCLUSIVE_LOCK = 2
254
260
    #     PVOID Pointer;
255
261
    #   };
256
262
    #   HANDLE hEvent;
257
 
    # } OVERLAPPED, 
 
263
    # } OVERLAPPED,
258
264
 
259
265
    class _inner_struct(ctypes.Structure):
260
266
        _fields_ = [('Offset', ctypes.c_uint), # DWORD
318
324
            super(_ctypes_ReadLock, self).__init__()
319
325
            self._lock(filename, 'rb', LOCK_SH + LOCK_NB)
320
326
 
 
327
 
321
328
    class _ctypes_WriteLock(_ctypes_FileLock):
322
329
        def __init__(self, filename):
323
330
            super(_ctypes_WriteLock, self).__init__()
324
331
            self._lock(filename, 'rb+', LOCK_EX + LOCK_NB)
325
332
 
326
 
    WriteLock = _ctypes_WriteLock
327
 
    ReadLock = _ctypes_ReadLock
 
333
 
 
334
    _lock_classes.append(('ctypes', _ctypes_WriteLock, _ctypes_ReadLock))
 
335
 
 
336
 
 
337
if len(_lock_classes) == 0:
 
338
    raise NotImplementedError(
 
339
        "We must have one of fcntl, pywin32, or ctypes available"
 
340
        " to support OS locking."
 
341
        )
 
342
 
 
343
 
 
344
# We default to using the first available lock class.
 
345
_lock_type, WriteLock, ReadLock = _lock_classes[0]
328
346