/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/lock.py

  • Committer: Robert Collins
  • Date: 2010-05-06 11:08:10 UTC
  • mto: This revision was merged to the branch mainline in revision 5223.
  • Revision ID: robertc@robertcollins.net-20100506110810-h3j07fh5gmw54s25
Cleaner matcher matching revised unlocking protocol.

Show diffs side-by-side

added added

removed removed

Lines of Context:
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
 
17
 
17
18
"""Locking using OS file locks or file existence.
18
19
 
19
20
Note: This method of locking is generally deprecated in favour of LockDir, but
33
34
unlock() method.
34
35
"""
35
36
 
36
 
from __future__ import absolute_import
37
 
 
38
 
import contextlib
39
37
import errno
40
38
import os
41
39
import sys
42
40
import warnings
43
41
 
44
 
from . import (
 
42
from bzrlib import (
45
43
    debug,
46
44
    errors,
47
45
    osutils,
48
46
    trace,
49
47
    )
50
 
from .hooks import Hooks
51
 
from .i18n import gettext
 
48
from bzrlib.hooks import HookPoint, Hooks
52
49
 
53
50
 
54
51
class LockHooks(Hooks):
55
52
 
56
53
    def __init__(self):
57
 
        Hooks.__init__(self, "breezy.lock", "Lock.hooks")
58
 
        self.add_hook(
59
 
            'lock_acquired',
60
 
            "Called with a breezy.lock.LockResult when a physical lock is "
61
 
            "acquired.", (1, 8))
62
 
        self.add_hook(
63
 
            'lock_released',
64
 
            "Called with a breezy.lock.LockResult when a physical lock is "
65
 
            "released.", (1, 8))
66
 
        self.add_hook(
67
 
            'lock_broken',
68
 
            "Called with a breezy.lock.LockResult when a physical lock is "
69
 
            "broken.", (1, 15))
 
54
        Hooks.__init__(self)
 
55
        self.create_hook(HookPoint('lock_acquired',
 
56
            "Called with a bzrlib.lock.LockResult when a physical lock is "
 
57
            "acquired.", (1, 8), None))
 
58
        self.create_hook(HookPoint('lock_released',
 
59
            "Called with a bzrlib.lock.LockResult when a physical lock is "
 
60
            "released.", (1, 8), None))
 
61
        self.create_hook(HookPoint('lock_broken',
 
62
            "Called with a bzrlib.lock.LockResult when a physical lock is "
 
63
            "broken.", (1, 15), None))
70
64
 
71
65
 
72
66
class Lock(object):
91
85
 
92
86
    def __repr__(self):
93
87
        return '%s(%s, %s)' % (self.__class__.__name__,
94
 
                               self.lock_url, self.details)
95
 
 
96
 
 
97
 
class LogicalLockResult(object):
98
 
    """The result of a lock_read/lock_write/lock_tree_write call on lockables.
99
 
 
100
 
    :ivar unlock: A callable which will unlock the lock.
101
 
    """
102
 
 
103
 
    def __init__(self, unlock, token=None):
104
 
        self.unlock = unlock
105
 
        self.token = token
106
 
 
107
 
    def __repr__(self):
108
 
        return "LogicalLockResult(%s)" % (self.unlock)
109
 
 
110
 
    def __enter__(self):
111
 
        return self
112
 
 
113
 
    def __exit__(self, exc_type, exc_val, exc_tb):
114
 
        # If there was an error raised, prefer the original one
115
 
        try:
116
 
            self.unlock()
117
 
        except BaseException:
118
 
            if exc_type is None:
119
 
                raise
120
 
        return False
 
88
                             self.lock_url, self.details)
121
89
 
122
90
 
123
91
def cant_unlock_not_held(locked_object):
124
92
    """An attempt to unlock failed because the object was not locked.
125
93
 
126
 
    This provides a policy point from which we can generate either a warning or
127
 
    an exception.
 
94
    This provides a policy point from which we can generate either a warning 
 
95
    or an exception.
128
96
    """
129
97
    # This is typically masking some other error and called from a finally
130
98
    # block, so it's useful to have the option not to generate a new error
132
100
    # raise LockNotHeld.
133
101
    if 'unlock' in debug.debug_flags:
134
102
        warnings.warn("%r is already unlocked" % (locked_object,),
135
 
                      stacklevel=3)
 
103
            stacklevel=3)
136
104
    else:
137
105
        raise errors.LockNotHeld(locked_object)
138
106
 
148
116
if sys.platform == 'win32':
149
117
    import msvcrt
150
118
    try:
151
 
        import win32file
152
 
        import pywintypes
153
 
        import winerror
 
119
        import win32file, pywintypes, winerror
154
120
        have_pywin32 = True
155
121
    except ImportError:
156
122
        pass
173
139
        try:
174
140
            self.f = open(self.filename, filemode)
175
141
            return self.f
176
 
        except IOError as e:
 
142
        except IOError, e:
177
143
            if e.errno in (errno.EACCES, errno.EPERM):
178
144
                raise errors.LockFailed(self.filename, str(e))
179
145
            if e.errno != errno.ENOENT:
191
157
            self.f.close()
192
158
            self.f = None
193
159
 
 
160
    def __del__(self):
 
161
        if self.f:
 
162
            from warnings import warn
 
163
            warn("lock on %r not released" % self.f)
 
164
            self.unlock()
 
165
 
194
166
    def unlock(self):
195
167
        raise NotImplementedError()
196
168
 
206
178
            fcntl.lockf(self.f, fcntl.LOCK_UN)
207
179
            self._clear_f()
208
180
 
 
181
 
209
182
    class _fcntl_WriteLock(_fcntl_FileLock):
210
183
 
211
184
        _open_locks = set()
234
207
                # LOCK_NB will cause IOError to be raised if we can't grab a
235
208
                # lock right away.
236
209
                fcntl.lockf(self.f, fcntl.LOCK_EX | fcntl.LOCK_NB)
237
 
            except IOError as e:
 
210
            except IOError, e:
238
211
                if e.errno in (errno.EAGAIN, errno.EACCES):
239
212
                    # We couldn't grab the lock
240
213
                    self.unlock()
246
219
            _fcntl_WriteLock._open_locks.remove(self.filename)
247
220
            self._unlock()
248
221
 
 
222
 
249
223
    class _fcntl_ReadLock(_fcntl_FileLock):
250
224
 
251
225
        _open_locks = {}
268
242
                # LOCK_NB will cause IOError to be raised if we can't grab a
269
243
                # lock right away.
270
244
                fcntl.lockf(self.f, fcntl.LOCK_SH | fcntl.LOCK_NB)
271
 
            except IOError as e:
 
245
            except IOError, e:
272
246
                # we should be more precise about whats a locking
273
247
                # error and whats a random-other error
274
248
                raise errors.LockContention(self.filename, e)
293
267
            """
294
268
            if self.filename in _fcntl_WriteLock._open_locks:
295
269
                raise AssertionError('file already locked: %r'
296
 
                                     % (self.filename,))
 
270
                    % (self.filename,))
297
271
            try:
298
272
                wlock = _fcntl_TemporaryWriteLock(self)
299
273
            except errors.LockError:
301
275
                return False, self
302
276
            return True, wlock
303
277
 
 
278
 
304
279
    class _fcntl_TemporaryWriteLock(_OSLock):
305
280
        """A token used when grabbing a temporary_write_lock.
306
281
 
320
295
 
321
296
            if self.filename in _fcntl_WriteLock._open_locks:
322
297
                raise AssertionError('file already locked: %r'
323
 
                                     % (self.filename,))
 
298
                    % (self.filename,))
324
299
 
325
300
            # See if we can open the file for writing. Another process might
326
301
            # have a read lock. We don't use self._open() because we don't want
328
303
            # done by _fcntl_ReadLock
329
304
            try:
330
305
                new_f = open(self.filename, 'rb+')
331
 
            except IOError as e:
 
306
            except IOError, e:
332
307
                if e.errno in (errno.EACCES, errno.EPERM):
333
308
                    raise errors.LockFailed(self.filename, str(e))
334
309
                raise
336
311
                # LOCK_NB will cause IOError to be raised if we can't grab a
337
312
                # lock right away.
338
313
                fcntl.lockf(new_f, fcntl.LOCK_EX | fcntl.LOCK_NB)
339
 
            except IOError as e:
 
314
            except IOError, e:
340
315
                # TODO: Raise a more specific error based on the type of error
341
316
                raise errors.LockContention(self.filename, e)
342
317
            _fcntl_WriteLock._open_locks.add(self.filename)
345
320
 
346
321
        def restore_read_lock(self):
347
322
            """Restore the original ReadLock."""
348
 
            # For fcntl, since we never released the read lock, just release
349
 
            # the write lock, and return the original lock.
 
323
            # For fcntl, since we never released the read lock, just release the
 
324
            # write lock, and return the original lock.
350
325
            fcntl.lockf(self.f, fcntl.LOCK_UN)
351
326
            self._clear_f()
352
327
            _fcntl_WriteLock._open_locks.remove(self.filename)
355
330
            self._read_lock = None
356
331
            return read_lock
357
332
 
 
333
 
358
334
    _lock_classes.append(('fcntl', _fcntl_WriteLock, _fcntl_ReadLock))
359
335
 
360
336
 
361
337
if have_pywin32 and sys.platform == 'win32':
362
 
    win32file_CreateFile = win32file.CreateFileW
 
338
    if os.path.supports_unicode_filenames:
 
339
        # for Windows NT/2K/XP/etc
 
340
        win32file_CreateFile = win32file.CreateFileW
 
341
    else:
 
342
        # for Windows 98
 
343
        win32file_CreateFile = win32file.CreateFile
363
344
 
364
345
    class _w32c_FileLock(_OSLock):
365
346
 
366
347
        def _open(self, filename, access, share, cflags, pymode):
367
348
            self.filename = osutils.realpath(filename)
368
349
            try:
369
 
                self._handle = win32file_CreateFile(
370
 
                    filename, access, share, None, win32file.OPEN_ALWAYS,
 
350
                self._handle = win32file_CreateFile(filename, access, share,
 
351
                    None, win32file.OPEN_ALWAYS,
371
352
                    win32file.FILE_ATTRIBUTE_NORMAL, None)
372
 
            except pywintypes.error as e:
 
353
            except pywintypes.error, e:
373
354
                if e.args[0] == winerror.ERROR_ACCESS_DENIED:
374
355
                    raise errors.LockFailed(filename, e)
375
356
                if e.args[0] == winerror.ERROR_SHARING_VIOLATION:
383
364
            self._clear_f()
384
365
            self._handle = None
385
366
 
 
367
 
386
368
    class _w32c_ReadLock(_w32c_FileLock):
387
369
        def __init__(self, filename):
388
370
            super(_w32c_ReadLock, self).__init__()
389
371
            self._open(filename, win32file.GENERIC_READ,
390
 
                       win32file.FILE_SHARE_READ, os.O_RDONLY, "rb")
 
372
                win32file.FILE_SHARE_READ, os.O_RDONLY, "rb")
391
373
 
392
374
        def temporary_write_lock(self):
393
375
            """Try to grab a write lock on the file.
408
390
                return False, _w32c_ReadLock(self.filename)
409
391
            return True, wlock
410
392
 
 
393
 
411
394
    class _w32c_WriteLock(_w32c_FileLock):
412
395
        def __init__(self, filename):
413
396
            super(_w32c_WriteLock, self).__init__()
414
397
            self._open(filename,
415
 
                       win32file.GENERIC_READ | win32file.GENERIC_WRITE, 0,
416
 
                       os.O_RDWR, "rb+")
 
398
                win32file.GENERIC_READ | win32file.GENERIC_WRITE, 0,
 
399
                os.O_RDWR, "rb+")
417
400
 
418
401
        def restore_read_lock(self):
419
402
            """Restore the original ReadLock."""
422
405
            self.unlock()
423
406
            return _w32c_ReadLock(self.filename)
424
407
 
 
408
 
425
409
    _lock_classes.append(('pywin32', _w32c_WriteLock, _w32c_ReadLock))
426
410
 
427
411
 
428
412
if have_ctypes_win32:
429
 
    from ctypes.wintypes import DWORD, LPWSTR
430
 
    LPSECURITY_ATTRIBUTES = ctypes.c_void_p  # used as NULL no need to declare
431
 
    HANDLE = ctypes.c_int  # rather than unsigned as in ctypes.wintypes
432
 
    _function_name = "CreateFileW"
 
413
    from ctypes.wintypes import DWORD, LPCSTR, LPCWSTR
 
414
    LPSECURITY_ATTRIBUTES = ctypes.c_void_p # used as NULL no need to declare
 
415
    HANDLE = ctypes.c_int # rather than unsigned as in ctypes.wintypes
 
416
    if os.path.supports_unicode_filenames:
 
417
        _function_name = "CreateFileW"
 
418
        LPTSTR = LPCWSTR
 
419
    else:
 
420
        _function_name = "CreateFileA"
 
421
        class LPTSTR(LPCSTR):
 
422
            def __new__(cls, obj):
 
423
                return LPCSTR.__new__(cls, obj.encode("mbcs"))
433
424
 
434
425
    # CreateFile <http://msdn.microsoft.com/en-us/library/aa363858.aspx>
435
426
    _CreateFile = ctypes.WINFUNCTYPE(
436
 
        HANDLE,                # return value
437
 
        LPWSTR,                # lpFileName
438
 
        DWORD,                 # dwDesiredAccess
439
 
        DWORD,                 # dwShareMode
440
 
        LPSECURITY_ATTRIBUTES,  # lpSecurityAttributes
441
 
        DWORD,                 # dwCreationDisposition
442
 
        DWORD,                 # dwFlagsAndAttributes
443
 
        HANDLE                 # hTemplateFile
 
427
            HANDLE,                # return value
 
428
            LPTSTR,                # lpFileName
 
429
            DWORD,                 # dwDesiredAccess
 
430
            DWORD,                 # dwShareMode
 
431
            LPSECURITY_ATTRIBUTES, # lpSecurityAttributes
 
432
            DWORD,                 # dwCreationDisposition
 
433
            DWORD,                 # dwFlagsAndAttributes
 
434
            HANDLE                 # hTemplateFile
444
435
        )((_function_name, ctypes.windll.kernel32))
445
436
 
446
437
    INVALID_HANDLE_VALUE = -1
459
450
        def _open(self, filename, access, share, cflags, pymode):
460
451
            self.filename = osutils.realpath(filename)
461
452
            handle = _CreateFile(filename, access, share, None, OPEN_ALWAYS,
462
 
                                 FILE_ATTRIBUTE_NORMAL, 0)
 
453
                FILE_ATTRIBUTE_NORMAL, 0)
463
454
            if handle in (INVALID_HANDLE_VALUE, 0):
464
455
                e = ctypes.WinError()
465
456
                if e.args[0] == ERROR_ACCESS_DENIED:
474
465
        def unlock(self):
475
466
            self._clear_f()
476
467
 
 
468
 
477
469
    class _ctypes_ReadLock(_ctypes_FileLock):
478
470
        def __init__(self, filename):
479
471
            super(_ctypes_ReadLock, self).__init__()
480
472
            self._open(filename, GENERIC_READ, FILE_SHARE_READ, os.O_RDONLY,
481
 
                       "rb")
 
473
                "rb")
482
474
 
483
475
        def temporary_write_lock(self):
484
476
            """Try to grab a write lock on the file.
503
495
        def __init__(self, filename):
504
496
            super(_ctypes_WriteLock, self).__init__()
505
497
            self._open(filename, GENERIC_READ | GENERIC_WRITE, 0, os.O_RDWR,
506
 
                       "rb+")
 
498
                "rb+")
507
499
 
508
500
        def restore_read_lock(self):
509
501
            """Restore the original ReadLock."""
512
504
            self.unlock()
513
505
            return _ctypes_ReadLock(self.filename)
514
506
 
 
507
 
515
508
    _lock_classes.append(('ctypes', _ctypes_WriteLock, _ctypes_ReadLock))
516
509
 
517
510
 
534
527
    locked the same way), and -Drelock is set, then this will trace.note a
535
528
    message about it.
536
529
    """
537
 
 
 
530
    
538
531
    _prev_lock = None
539
532
 
540
533
    def _note_lock(self, lock_type):
543
536
                type_name = 'read'
544
537
            else:
545
538
                type_name = 'write'
546
 
            trace.note(gettext('{0!r} was {1} locked again'), self, type_name)
 
539
            trace.note('%r was %s locked again', self, type_name)
547
540
        self._prev_lock = lock_type
548
541
 
549
 
 
550
 
@contextlib.contextmanager
551
 
def write_locked(lockable):
552
 
    lockable.lock_write()
553
 
    try:
554
 
        yield lockable
555
 
    finally:
556
 
        lockable.unlock()