/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 breezy/lock.py

  • Committer: Jelmer Vernooij
  • Date: 2019-02-04 01:01:24 UTC
  • mto: This revision was merged to the branch mainline in revision 7268.
  • Revision ID: jelmer@jelmer.uk-20190204010124-ni0i4qc6f5tnbvux
Fix source tests.

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