~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/lock.py

  • Committer: Martin Pool
  • Date: 2005-06-01 06:09:59 UTC
  • Revision ID: mbp@sourcefrog.net-20050601060959-e0be99c26487bd63
Major rework of locking code:

- New ReadLock and WriteLock objects, with unlock methods, and that
  give a warning if they leak without being unlocked

- The lock file is opened readonly for read locks, which should avoid
  problems when the user only has read permission for a branch.

- Selective definitions of locking code is perhaps simpler now

Show diffs side-by-side

added added

removed removed

Lines of Context:
28
28
 
29
29
Eventually we may need to use some kind of lock representation that
30
30
will work on a dumb filesystem without actual locking primitives.
 
31
 
 
32
This defines two classes: ReadLock and WriteLock, which can be
 
33
implemented in different ways on different platforms.  Both have an
 
34
unlock() method.
31
35
"""
32
36
 
33
37
 
34
38
import sys, os
35
39
 
36
 
import bzrlib
37
40
from trace import mutter, note, warning
38
41
from errors import LockError
39
42
 
 
43
class _base_Lock(object):
 
44
    def _open(self, filename, filemode):
 
45
        self.f = open(filename, filemode)
 
46
        return self.f
 
47
    
 
48
 
 
49
    def __del__(self):
 
50
        if self.f:
 
51
            from warnings import warn
 
52
            warn("lock on %r not released" % self.f)
 
53
            self.unlock()
 
54
 
 
55
    def unlock(self):
 
56
        raise NotImplementedError()
 
57
 
 
58
        
 
59
 
 
60
 
 
61
 
 
62
 
 
63
############################################################
 
64
# msvcrt locks
 
65
 
 
66
 
40
67
try:
41
68
    import fcntl
42
 
    LOCK_SH = fcntl.LOCK_SH
43
 
    LOCK_EX = fcntl.LOCK_EX
44
 
    LOCK_NB = fcntl.LOCK_NB
45
 
    def lock(f, flags):
46
 
        try:
47
 
            fcntl.flock(f, flags)
48
 
        except Exception, e:
49
 
            raise LockError(e)
50
 
 
51
 
    def unlock(f):
52
 
        try:
53
 
            fcntl.flock(f, fcntl.LOCK_UN)
54
 
        except Exception, e:
55
 
            raise LockError(e)
 
69
 
 
70
    class _fcntl_FileLock(_base_Lock):
 
71
        f = None
 
72
 
 
73
        def unlock(self):
 
74
            fcntl.flock(self.f, fcntl.LOCK_UN)
 
75
            self.f.close()
 
76
            del self.f 
 
77
 
 
78
 
 
79
    class _fcntl_WriteLock(_fcntl_FileLock):
 
80
        def __init__(self, filename):
 
81
            try:
 
82
                fcntl.flock(self._open(filename, 'wb'), fcntl.LOCK_EX)
 
83
            except Exception, e:
 
84
                raise LockError(e)
 
85
 
 
86
 
 
87
    class _fcntl_ReadLock(_fcntl_FileLock):
 
88
        def __init__(self, filename):
 
89
            try:
 
90
                fcntl.flock(self._open(filename, 'rb'), fcntl.LOCK_SH)
 
91
            except Exception, e:
 
92
                raise LockError(e)
 
93
 
 
94
    WriteLock = _fcntl_WriteLock
 
95
    ReadLock = _fcntl_ReadLock
56
96
 
57
97
except ImportError:
58
98
    try:
59
99
        import win32con, win32file, pywintypes
60
 
        LOCK_SH = 0 # the default
61
 
        LOCK_EX = win32con.LOCKFILE_EXCLUSIVE_LOCK
62
 
        LOCK_NB = win32con.LOCKFILE_FAIL_IMMEDIATELY
63
 
 
64
 
        def lock(f, flags):
65
 
            try:
66
 
                if type(f) == file:
67
 
                    hfile = win32file._get_osfhandle(f.fileno())
68
 
                else:
69
 
                    hfile = win32file._get_osfhandle(f)
70
 
                overlapped = pywintypes.OVERLAPPED()
71
 
                win32file.LockFileEx(hfile, flags, 0, 0x7fff0000, overlapped)
72
 
            except Exception, e:
73
 
                raise LockError(e)
74
 
 
75
 
        def unlock(f):
76
 
            try:
77
 
                if type(f) == file:
78
 
                    hfile = win32file._get_osfhandle(f.fileno())
79
 
                else:
80
 
                    hfile = win32file._get_osfhandle(f)
81
 
                overlapped = pywintypes.OVERLAPPED()
82
 
                win32file.UnlockFileEx(hfile, 0, 0x7fff0000, overlapped)
83
 
            except Exception, e:
84
 
                raise LockError(e)
 
100
 
 
101
 
 
102
        #LOCK_SH = 0 # the default
 
103
        #LOCK_EX = win32con.LOCKFILE_EXCLUSIVE_LOCK
 
104
        #LOCK_NB = win32con.LOCKFILE_FAIL_IMMEDIATELY
 
105
 
 
106
        class _w32c_FileLock(_base_Lock):
 
107
            def _lock(self, filename, openmode, lockmode):
 
108
                try:
 
109
                    self._open(filename, openmode)
 
110
                    self.hfile = win32file._get_osfhandle(self.f.fileno())
 
111
                    overlapped = pywintypes.OVERLAPPED()
 
112
                    win32file.LockFileEx(self.hfile, lockmode, 0, 0x7fff0000, overlapped)
 
113
                except Exception, e:
 
114
                    raise LockError(e)
 
115
 
 
116
            def unlock(self):
 
117
                try:
 
118
                    overlapped = pywintypes.OVERLAPPED()
 
119
                    win32file.UnlockFileEx(self.hfile, 0, 0x7fff0000, overlapped)
 
120
                    self.f.close()
 
121
                    self.f = None
 
122
                except Exception, e:
 
123
                    raise LockError(e)
 
124
 
 
125
 
 
126
 
 
127
        class _w32c_ReadLock(_w32c_FileLock):
 
128
            def __init__(self, filename):
 
129
                _w32c_FileLock._lock(self, filename, 'rb', 0)
 
130
 
 
131
        class _w32c_WriteLock(_w32c_FileLock):
 
132
            def __init__(self, filename):
 
133
                _w32c_FileLock._lock(self, filename, 'wb',
 
134
                                     win32con.LOCKFILE_EXCLUSIVE_LOCK)
 
135
 
 
136
 
 
137
 
 
138
        WriteLock = _w32c_WriteLock
 
139
        ReadLock = _w32c_ReadLock
 
140
 
85
141
    except ImportError:
86
142
        try:
87
143
            import msvcrt
 
144
 
 
145
 
88
146
            # Unfortunately, msvcrt.locking() doesn't distinguish between
89
147
            # read locks and write locks. Also, the way the combinations
90
148
            # work to get non-blocking is not the same, so we
91
149
            # have to write extra special functions here.
92
150
 
93
 
            LOCK_SH = 1
94
 
            LOCK_EX = 2
95
 
            LOCK_NB = 4
96
 
 
97
 
            def lock(f, flags):
 
151
 
 
152
            class _msvc_FileLock(_base_Lock):
 
153
                LOCK_SH = 1
 
154
                LOCK_EX = 2
 
155
                LOCK_NB = 4
 
156
                def unlock(self):
 
157
                    _msvc_unlock(self.f)
 
158
 
 
159
 
 
160
            class _msvc_ReadLock(_msvc_FileLock):
 
161
                def __init__(self, filename):
 
162
                    _msvc_lock(self._open(filename, 'rb'), self.LOCK_SH)
 
163
 
 
164
 
 
165
            class _msvc_WriteLock(_msvc_FileLock):
 
166
                def __init__(self, filename):
 
167
                    _msvc_lock(self._open(filename, 'wb'), self.LOCK_EX)
 
168
 
 
169
 
 
170
 
 
171
            def _msvc_lock(f, flags):
98
172
                try:
99
173
                    # Unfortunately, msvcrt.LK_RLCK is equivalent to msvcrt.LK_LOCK
100
174
                    # according to the comments, LK_RLCK is open the lock for writing.
110
184
                        fn = f
111
185
                        fpos = os.lseek(fn, 0,0)
112
186
                        os.lseek(fn, 0,0)
113
 
                    
114
 
                    if flags & LOCK_SH:
115
 
                        if flags & LOCK_NB:
 
187
 
 
188
                    if flags & self.LOCK_SH:
 
189
                        if flags & self.LOCK_NB:
116
190
                            lock_mode = msvcrt.LK_NBLCK
117
191
                        else:
118
192
                            lock_mode = msvcrt.LK_LOCK
119
 
                    elif flags & LOCK_EX:
120
 
                        if flags & LOCK_NB:
 
193
                    elif flags & self.LOCK_EX:
 
194
                        if flags & self.LOCK_NB:
121
195
                            lock_mode = msvcrt.LK_NBRLCK
122
196
                        else:
123
197
                            lock_mode = msvcrt.LK_RLCK
130
204
                except Exception, e:
131
205
                    raise LockError(e)
132
206
 
133
 
            def unlock(f):
 
207
            def _msvc_unlock(f):
134
208
                try:
135
209
                    if type(f) == file:
136
210
                        fpos = f.tell()
147
221
                        os.lseek(fn, fpos, 0)
148
222
                except Exception, e:
149
223
                    raise LockError(e)
 
224
 
 
225
 
 
226
 
 
227
            WriteLock = _msvc_WriteLock
 
228
            ReadLock = _msvc_ReadLock
150
229
        except ImportError:
151
 
            from warnings import Warning
152
 
            
153
 
            warning("please write a locking method for platform %r" % sys.platform)
154
 
 
155
 
            # Creating no-op lock/unlock for now
156
 
            def lock(f, flags):
157
 
                pass
158
 
            def unlock(f):
159
 
                pass
 
230
            raise NotImplementedError("please write a locking method "
 
231
                                      "for platform %r" % sys.platform)
 
232
 
 
233
 
 
234
 
 
235
 
 
236
 
 
237
 
160
238