~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: 2007-03-20 22:39:38 UTC
  • mfrom: (2363.3.4 simple_locking)
  • Revision ID: pqm@pqm.ubuntu.com-20070320223938-97fdc295a1111e36
(John Arbash Meinel) allow win32 to grab a write lock after 'bzr status', so that we can record the updated stat values.

Show diffs side-by-side

added added

removed removed

Lines of Context:
118
118
 
119
119
    class _fcntl_WriteLock(_fcntl_FileLock):
120
120
 
121
 
        open_locks = {}
 
121
        _open_locks = set()
122
122
 
123
123
        def __init__(self, filename):
124
 
            # standard IO errors get exposed directly.
125
124
            super(_fcntl_WriteLock, self).__init__()
126
 
            self._open(filename, 'rb+')
127
 
            if self.filename in self.open_locks:
 
125
            # Check we can grab a lock before we actually open the file.
 
126
            self.filename = osutils.realpath(filename)
 
127
            if self.filename in _fcntl_WriteLock._open_locks:
128
128
                self._clear_f()
129
129
                raise errors.LockContention(self.filename)
 
130
 
 
131
            self._open(self.filename, 'rb+')
130
132
            # reserve a slot for this lock - even if the lockf call fails,
131
133
            # at thisi point unlock() will be called, because self.f is set.
132
134
            # TODO: make this fully threadsafe, if we decide we care.
133
 
            self.open_locks[self.filename] = self.filename
 
135
            _fcntl_WriteLock._open_locks.add(self.filename)
134
136
            try:
135
137
                # LOCK_NB will cause IOError to be raised if we can't grab a
136
138
                # lock right away.
144
146
                raise errors.LockError(e)
145
147
 
146
148
        def unlock(self):
147
 
            del self.open_locks[self.filename]
 
149
            _fcntl_WriteLock._open_locks.remove(self.filename)
148
150
            self._unlock()
149
151
 
150
152
 
151
153
    class _fcntl_ReadLock(_fcntl_FileLock):
152
154
 
153
 
        open_locks = {}
 
155
        _open_locks = {}
154
156
 
155
157
        def __init__(self, filename):
156
158
            super(_fcntl_ReadLock, self).__init__()
 
159
            self.filename = osutils.realpath(filename)
 
160
            _fcntl_ReadLock._open_locks.setdefault(self.filename, 0)
 
161
            _fcntl_ReadLock._open_locks[self.filename] += 1
157
162
            self._open(filename, 'rb')
158
163
            try:
159
164
                # LOCK_NB will cause IOError to be raised if we can't grab a
165
170
                raise errors.LockError(e)
166
171
 
167
172
        def unlock(self):
 
173
            count = _fcntl_ReadLock._open_locks[self.filename]
 
174
            if count == 1:
 
175
                del _fcntl_ReadLock._open_locks[self.filename]
 
176
            else:
 
177
                _fcntl_ReadLock._open_locks[self.filename] = count - 1
168
178
            self._unlock()
169
179
 
 
180
        def temporary_write_lock(self):
 
181
            """Try to grab a write lock on the file.
 
182
 
 
183
            On platforms that support it, this will upgrade to a write lock
 
184
            without unlocking the file.
 
185
            Otherwise, this will release the read lock, and try to acquire a
 
186
            write lock.
 
187
 
 
188
            :return: A token which can be used to switch back to a read lock.
 
189
            """
 
190
            assert self.filename not in _fcntl_WriteLock._open_locks
 
191
            try:
 
192
                wlock = _fcntl_TemporaryWriteLock(self)
 
193
            except errors.LockError:
 
194
                # We didn't unlock, so we can just return 'self'
 
195
                return False, self
 
196
            return True, wlock
 
197
 
 
198
 
 
199
    class _fcntl_TemporaryWriteLock(_base_Lock):
 
200
        """A token used when grabbing a temporary_write_lock.
 
201
 
 
202
        Call restore_read_lock() when you are done with the write lock.
 
203
        """
 
204
 
 
205
        def __init__(self, read_lock):
 
206
            super(_fcntl_TemporaryWriteLock, self).__init__()
 
207
            self._read_lock = read_lock
 
208
            self.filename = read_lock.filename
 
209
 
 
210
            count = _fcntl_ReadLock._open_locks[self.filename]
 
211
            if count > 1:
 
212
                # Something else also has a read-lock, so we cannot grab a
 
213
                # write lock.
 
214
                raise errors.LockContention(self.filename)
 
215
 
 
216
            assert self.filename not in _fcntl_WriteLock._open_locks
 
217
 
 
218
            # See if we can open the file for writing. Another process might
 
219
            # have a read lock. We don't use self._open() because we don't want
 
220
            # to create the file if it exists. That would have already been
 
221
            # done by _fcntl_ReadLock
 
222
            try:
 
223
                new_f = open(self.filename, 'rb+')
 
224
            except IOError, e:
 
225
                if e.errno in (errno.EACCES, errno.EPERM):
 
226
                    raise errors.ReadOnlyLockError(self.filename, str(e))
 
227
                raise
 
228
            try:
 
229
                # LOCK_NB will cause IOError to be raised if we can't grab a
 
230
                # lock right away.
 
231
                fcntl.lockf(new_f, fcntl.LOCK_SH | fcntl.LOCK_NB)
 
232
            except IOError, e:
 
233
                # TODO: Raise a more specific error based on the type of error
 
234
                raise errors.LockError(e)
 
235
            _fcntl_WriteLock._open_locks.add(self.filename)
 
236
 
 
237
            self.f = new_f
 
238
 
 
239
        def restore_read_lock(self):
 
240
            """Restore the original ReadLock."""
 
241
            # For fcntl, since we never released the read lock, just release the
 
242
            # write lock, and return the original lock.
 
243
            fcntl.lockf(self.f, fcntl.LOCK_UN)
 
244
            self._clear_f()
 
245
            _fcntl_WriteLock._open_locks.remove(self.filename)
 
246
            # Avoid reference cycles
 
247
            read_lock = self._read_lock
 
248
            self._read_lock = None
 
249
            return read_lock
 
250
 
170
251
 
171
252
    _lock_classes.append(('fcntl', _fcntl_WriteLock, _fcntl_ReadLock))
172
253
 
211
292
            super(_w32c_ReadLock, self).__init__()
212
293
            self._lock(filename, 'rb', LOCK_SH + LOCK_NB)
213
294
 
 
295
        def temporary_write_lock(self):
 
296
            """Try to grab a write lock on the file.
 
297
 
 
298
            On platforms that support it, this will upgrade to a write lock
 
299
            without unlocking the file.
 
300
            Otherwise, this will release the read lock, and try to acquire a
 
301
            write lock.
 
302
 
 
303
            :return: A token which can be used to switch back to a read lock.
 
304
            """
 
305
            # I can't find a way to upgrade a read lock to a write lock without
 
306
            # unlocking first. So here, we do just that.
 
307
            self.unlock()
 
308
            try:
 
309
                wlock = _w32c_WriteLock(self.filename)
 
310
            except errors.LockError:
 
311
                return False, _w32c_ReadLock(self.filename)
 
312
            return True, wlock
 
313
 
214
314
 
215
315
    class _w32c_WriteLock(_w32c_FileLock):
216
316
        def __init__(self, filename):
217
317
            super(_w32c_WriteLock, self).__init__()
218
318
            self._lock(filename, 'rb+', LOCK_EX + LOCK_NB)
219
319
 
 
320
        def restore_read_lock(self):
 
321
            """Restore the original ReadLock."""
 
322
            # For win32 we had to completely let go of the original lock, so we
 
323
            # just unlock and create a new read lock.
 
324
            self.unlock()
 
325
            return _w32c_ReadLock(self.filename)
 
326
 
220
327
 
221
328
    _lock_classes.append(('pywin32', _w32c_WriteLock, _w32c_ReadLock))
222
329
 
310
417
            super(_ctypes_ReadLock, self).__init__()
311
418
            self._lock(filename, 'rb', LOCK_SH + LOCK_NB)
312
419
 
 
420
        def temporary_write_lock(self):
 
421
            """Try to grab a write lock on the file.
 
422
 
 
423
            On platforms that support it, this will upgrade to a write lock
 
424
            without unlocking the file.
 
425
            Otherwise, this will release the read lock, and try to acquire a
 
426
            write lock.
 
427
 
 
428
            :return: A token which can be used to switch back to a read lock.
 
429
            """
 
430
            # I can't find a way to upgrade a read lock to a write lock without
 
431
            # unlocking first. So here, we do just that.
 
432
            self.unlock()
 
433
            try:
 
434
                wlock = _ctypes_WriteLock(self.filename)
 
435
            except errors.LockError:
 
436
                return False, _ctypes_ReadLock(self.filename)
 
437
            return True, wlock
313
438
 
314
439
    class _ctypes_WriteLock(_ctypes_FileLock):
315
440
        def __init__(self, filename):
316
441
            super(_ctypes_WriteLock, self).__init__()
317
442
            self._lock(filename, 'rb+', LOCK_EX + LOCK_NB)
318
443
 
 
444
        def restore_read_lock(self):
 
445
            """Restore the original ReadLock."""
 
446
            # For win32 we had to completely let go of the original lock, so we
 
447
            # just unlock and create a new read lock.
 
448
            self.unlock()
 
449
            return _ctypes_ReadLock(self.filename)
 
450
 
319
451
 
320
452
    _lock_classes.append(('ctypes', _ctypes_WriteLock, _ctypes_ReadLock))
321
453